diff options
author | Chaouki Dhib <chaodhib@gmail.com> | 2021-05-16 13:16:08 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-16 13:16:08 +0200 |
commit | 2d114ea560cd8716af2b0d12990fc3480ecaf2b7 (patch) | |
tree | aaa711aa4d63e9b33d8e804d481425368ddcee18 /src/server | |
parent | edcaac6c959e944dc6d6dc224666b832521412b4 (diff) |
Core/Movement: Improve client control logic (#26348)
Diffstat (limited to 'src/server')
-rw-r--r-- | src/server/game/AI/CreatureAISelector.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Creature/Creature.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 18 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 4 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.cpp | 71 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.h | 24 | ||||
-rw-r--r-- | src/server/game/Handlers/MiscHandler.cpp | 77 | ||||
-rw-r--r-- | src/server/game/Handlers/MovementHandler.cpp | 311 | ||||
-rw-r--r-- | src/server/game/Handlers/SpellHandler.cpp | 19 | ||||
-rw-r--r-- | src/server/game/Handlers/TaxiHandler.cpp | 10 | ||||
-rw-r--r-- | src/server/game/Server/GameClient.cpp | 63 | ||||
-rw-r--r-- | src/server/game/Server/GameClient.h | 54 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Opcodes.cpp | 10 | ||||
-rw-r--r-- | src/server/game/Server/WorldSession.cpp | 33 | ||||
-rw-r--r-- | src/server/game/Server/WorldSession.h | 29 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.cpp | 2 |
16 files changed, 502 insertions, 227 deletions
diff --git a/src/server/game/AI/CreatureAISelector.cpp b/src/server/game/AI/CreatureAISelector.cpp index 06738fdc8ea..be45c7d5420 100644 --- a/src/server/game/AI/CreatureAISelector.cpp +++ b/src/server/game/AI/CreatureAISelector.cpp @@ -103,7 +103,7 @@ namespace FactorySelector { MovementGeneratorType type = unit->GetDefaultMovementType(); if (Creature* creature = unit->ToCreature()) - if (!creature->GetPlayerMovingMe()) + if (!creature->GetCharmerOrSelfPlayer()) type = creature->GetDefaultMovementType(); MovementGeneratorCreator const* mv_factory = sMovementGeneratorRegistry->GetRegistryItem(type); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index d318a941784..4da1b249b64 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2652,7 +2652,7 @@ void Creature::InitializeMovementFlags() void Creature::UpdateMovementFlags() { // Do not update movement flags if creature is controlled by a player (charm/vehicle) - if (m_playerMovingMe) + if (IsMovedByClient()) return; // Creatures with CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE should control MovementFlags in your own scripts diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 21410f84f12..bbf68c2a1f9 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -41,6 +41,7 @@ #include "DatabaseEnv.h" #include "DisableMgr.h" #include "Formulas.h" +#include "GameClient.h" #include "GameEventMgr.h" #include "GameObjectAI.h" #include "GameTime.h" @@ -307,9 +308,6 @@ Player::Player(WorldSession* session): Unit(true) m_resetTalentsTime = 0; m_itemUpdateQueueBlocked = false; - for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i) - m_forced_speed_changes[i] = 0; - /////////////////// Instance System ///////////////////// m_HomebindTimer = 0; @@ -354,8 +352,6 @@ Player::Player(WorldSession* session): Unit(true) // Player summoning m_summon_expire = 0; - m_unitMovedByMe = this; - m_playerMovingMe = this; m_seer = this; m_homebindMapId = 0; @@ -1351,7 +1347,6 @@ void Player::Update(uint32 p_time) //because we don't want player's ghost teleported from graveyard if (IsHasDelayedTeleport() && IsAlive()) TeleportTo(m_teleport_dest, m_teleport_options); - } void Player::setDeathState(DeathState s) @@ -22352,7 +22347,7 @@ bool Player::IsNeverVisible() const bool Player::CanAlwaysSee(WorldObject const* obj) const { // Always can see self - if (GetUnitBeingMoved() == obj) + if (GetCharmedOrSelf() == obj) return true; if (ObjectGuid guid = GetGuidValue(PLAYER_FARSIGHT)) @@ -22727,7 +22722,7 @@ void Player::SendInitialPacketsBeforeAddToMap() /// SMSG_RESYNC_RUNES ResyncRunes(); - SetMovedUnit(this); + GetSession()->GetGameClient()->AddAllowedMover(this); } void Player::SendInitialPacketsAfterAddToMap() @@ -24045,7 +24040,7 @@ void Player::SetClientControl(Unit* target, bool allowMove) SetViewpoint(target, true); } - SetMovedUnit(target); + GetGameClient()->SetMovedUnit(target, allowMove); } void Player::UpdateZoneDependentAuras(uint32 newZone) @@ -26969,3 +26964,8 @@ std::string Player::GetDebugInfo() const sstr << Unit::GetDebugInfo(); return sstr.str(); } + +GameClient* Player::GetGameClient() const +{ + return GetSession()->GetGameClient(); +} diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index d8ae36a54d2..7a0aef3c5c5 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -61,6 +61,7 @@ class Channel; class CharacterCreateInfo; class Creature; class DynamicObject; +class GameClient; class Group; class Guild; class Item; @@ -1653,6 +1654,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void RemovedInsignia(Player* looterPlr); WorldSession* GetSession() const { return m_session; } + GameClient* GetGameClient() const; void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const override; void DestroyForPlayer(Player* target, bool onDeath = false) const override; @@ -2017,8 +2019,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> template<class T> void UpdateVisibilityOf(T* target, UpdateData& data, std::set<Unit*>& visibleNow); - uint8 m_forced_speed_changes[MAX_MOVE_TYPE]; - bool HasAtLoginFlag(AtLoginFlags f) const { return (m_atLoginFlags & f) != 0; } void SetAtLoginFlag(AtLoginFlags f) { m_atLoginFlags |= f; } void RemoveAtLoginFlag(AtLoginFlags flags, bool persist = false); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 0bb20566b21..888457c7cbc 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -33,6 +33,7 @@ #include "CreatureAIImpl.h" #include "CreatureGroups.h" #include "Formulas.h" +#include "GameClient.h" #include "GameObjectAI.h" #include "GameTime.h" #include "GridNotifiersImpl.h" @@ -295,7 +296,7 @@ bool DispelableAura::RollDispel() const Unit::Unit(bool isWorldObject) : WorldObject(isWorldObject), m_lastSanctuaryTime(0), LastCharmerGUID(), movespline(new Movement::MoveSpline()), m_ControlledByPlayer(false), m_AutoRepeatFirstCast(false), m_procDeep(0), m_transformSpell(0), - m_removedAurasCount(0), m_unitMovedByMe(nullptr), m_playerMovingMe(nullptr), m_charmer(nullptr), m_charmed(nullptr), + m_removedAurasCount(0), m_charmer(nullptr), m_charmed(nullptr), i_motionMaster(new MotionMaster(this)), m_regenTimer(0), m_vehicle(nullptr), m_vehicleKit(nullptr), m_unitTypeMask(UNIT_MASK_NONE), m_Diminishing(), m_combatManager(this), m_threatManager(this), m_aiLocked(false), m_comboTarget(nullptr), m_comboPoints(0), m_spellHistory(new SpellHistory(this)) @@ -367,7 +368,11 @@ Unit::Unit(bool isWorldObject) : for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i) m_speed_rate[i] = 1.0f; + for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i) + m_forced_speed_changes[i] = 0; + m_charmInfo = nullptr; + _gameClientMovingMe = nullptr; // remove aurastates allowing special moves for (uint8 i = 0; i < MAX_REACTIVE; ++i) @@ -417,8 +422,6 @@ Unit::~Unit() ASSERT(m_removedAuras.empty()); ASSERT(m_gameObj.empty()); ASSERT(m_dynObj.empty()); - ASSERT(!m_unitMovedByMe || (m_unitMovedByMe == this)); - ASSERT(!m_playerMovingMe || (m_playerMovingMe == this)); } void Unit::Update(uint32 p_time) @@ -5496,7 +5499,7 @@ Unit* Unit::getAttackerForHelper() const // If someone wants to return nullptr; if (Unit* victim = GetVictim()) - if ((!IsPet() && !GetPlayerMovingMe()) || IsInCombatWith(victim)) + if ((!IsPet() && !IsCharmerOrSelfPlayer()) || IsInCombatWith(victim)) return victim; CombatManager const& mgr = GetCombatManager(); @@ -6270,6 +6273,14 @@ bool Unit::isPossessing() const return false; } +Unit* Unit::GetCharmerOrSelf() const +{ + if (IsCharmed()) + return GetCharmer(); + else + return const_cast<Unit*>(this); +} + Unit* Unit::GetNextRandomRaidMemberOrPet(float radius) { Player* player = nullptr; @@ -8627,15 +8638,17 @@ void Unit::SetSpeedRate(UnitMoveType mtype, float rate) { // register forced speed changes for WorldSession::HandleForceSpeedChangeAck // and do it only for real sent packets and use run for run/mounted as client expected - ++ToPlayer()->m_forced_speed_changes[mtype]; + ++m_forced_speed_changes[mtype]; if (!IsInCombat()) if (Pet* pet = ToPlayer()->GetPet()) pet->SetSpeedRate(mtype, m_speed_rate[mtype]); } - if (Player* playerMover = Unit::ToPlayer(GetUnitBeingMoved())) // unit controlled by a player. + if (IsMovedByClient()) // unit controlled by a player. { + Player* playerMover = GetGameClientMovingMe()->GetBasePlayer(); + // Send notification to self. this packet is only sent to one client (the client of the player concerned by the change). WorldPacket self; self.Initialize(moveTypeToOpcode[mtype][1], mtype != MOVE_RUN ? 8 + 4 + 4 : 8 + 4 + 1 + 4); @@ -9933,13 +9946,6 @@ void CharmInfo::SetSpellAutocast(SpellInfo const* spellInfo, bool state) } } -void Unit::SetMovedUnit(Unit* target) -{ - m_unitMovedByMe->m_playerMovingMe = nullptr; - m_unitMovedByMe = ASSERT_NOTNULL(target); - m_unitMovedByMe->m_playerMovingMe = ASSERT_NOTNULL(ToPlayer()); -} - uint32 createProcHitMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition) { uint32 hitMask = PROC_HIT_NONE; @@ -10446,7 +10452,7 @@ void Unit::SendComboPoints() data << uint8(m_comboPoints); playerMe->SendDirectMessage(&data); } - Player* movingMe = GetPlayerMovingMe(); + Player* movingMe = GetCharmerOrSelfPlayer(); ObjectGuid ownerGuid = GetCharmerOrOwnerGUID(); Player* owner = nullptr; if (ownerGuid.IsPlayer()) @@ -11435,11 +11441,8 @@ void Unit::SetFeared(bool apply) } // block / allow control to real player in control (eg charmer) - if (GetTypeId() == TYPEID_PLAYER) - { - if (m_playerMovingMe) - m_playerMovingMe->SetClientControl(this, !apply); - } + if (GetCharmerOrSelfPlayer()) + GetCharmerOrSelfPlayer()->SetClientControl(this, !apply); } void Unit::SetConfused(bool apply) @@ -11460,11 +11463,8 @@ void Unit::SetConfused(bool apply) } // block / allow control to real player in control (eg charmer) - if (GetTypeId() == TYPEID_PLAYER) - { - if (m_playerMovingMe) - m_playerMovingMe->SetClientControl(this, !apply); - } + if (GetCharmerOrSelfPlayer()) + GetCharmerOrSelfPlayer()->SetClientControl(this, !apply); } bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* aurApp) @@ -12163,18 +12163,7 @@ void Unit::UpdateObjectVisibility(bool forced) void Unit::KnockbackFrom(float x, float y, float speedXY, float speedZ) { - Player* player = ToPlayer(); - if (!player) - { - if (Unit* charmer = GetCharmer()) - { - player = charmer->ToPlayer(); - if (player && player->GetUnitBeingMoved() != this) - player = nullptr; - } - } - - if (!player) + if (IsMovedByServer()) { GetMotionMaster()->MoveKnockbackFrom(x, y, speedXY, speedZ); } @@ -12190,10 +12179,10 @@ void Unit::KnockbackFrom(float x, float y, float speedXY, float speedZ) data << float(speedXY); // Horizontal speed data << float(-speedZ); // Z Movement speed (vertical) - player->SendDirectMessage(&data); + GetGameClientMovingMe()->SendDirectMessage(&data); - if (player->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) || player->HasAuraType(SPELL_AURA_FLY)) - player->SetCanFly(true, true); + if (HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) || HasAuraType(SPELL_AURA_FLY)) + SetCanFly(true, true); } } @@ -12853,9 +12842,9 @@ void Unit::SendTeleportPacket(Position const& pos, bool teleportingTransport /*= moveUpdateTeleport << GetPackGUID(); Unit* broadcastSource = this; - // should this really be the unit _being_ moved? not the unit doing the moving? - if (Player* playerMover = Unit::ToPlayer(GetUnitBeingMoved())) + if (IsMovedByClient()) { + Player* playerMover = GetGameClientMovingMe()->GetBasePlayer(); WorldPacket moveTeleport(MSG_MOVE_TELEPORT_ACK, 41); moveTeleport << GetPackGUID(); moveTeleport << uint32(0); // this value increments every time diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index a78c049d9f6..87c2e6d1e87 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -71,6 +71,7 @@ class AuraApplication; class AuraEffect; class Creature; class DynamicObject; +class GameClient; class GameObject; class Guardian; class Item; @@ -1184,6 +1185,7 @@ class TC_GAME_API Unit : public WorldObject void RemoveAllControlled(); bool IsCharmed() const { return !GetCharmerGUID().IsEmpty(); } + bool IsCharming() const { return !GetCharmedGUID().IsEmpty(); } bool isPossessed() const { return HasUnitState(UNIT_STATE_POSSESSED); } bool isPossessedByPlayer() const; bool isPossessing() const; @@ -1193,12 +1195,17 @@ class TC_GAME_API Unit : public WorldObject CharmInfo* InitCharmInfo(); void DeleteCharmInfo(); - // all of these are for DIRECT CLIENT CONTROL only - void SetMovedUnit(Unit* target); - // returns the unit that this player IS CONTROLLING - Unit* GetUnitBeingMoved() const { return m_unitMovedByMe; } - // returns the player that this unit is BEING CONTROLLED BY - Player* GetPlayerMovingMe() const { return m_playerMovingMe; } + // base client control of this unit (possess effects, vehicles and similar). Not affected by temporary CC. + bool IsCharmerOrSelfPlayer() const { return GetCharmerOrSelf()->IsPlayer(); } + Unit* GetCharmerOrSelf() const; + Player* GetCharmerOrSelfPlayer() const { return ToPlayer(GetCharmerOrSelf());} + Unit* GetCharmedOrSelf() const { return IsCharming() ? GetCharmed() : const_cast<Unit*>(this); } + + // real time client control status of this unit (possess effects, vehicles and similar). For example, if this unit is a player temporarly under fear, it will return false. + bool IsMovedByClient() const { return _gameClientMovingMe != nullptr; } + bool IsMovedByServer() const { return !IsMovedByClient(); } + GameClient* GetGameClientMovingMe() const { return _gameClientMovingMe; } + void SetGameClientMovingMe(GameClient* gameClientMovingMe) { _gameClientMovingMe = gameClientMovingMe; } SharedVisionList const& GetSharedVisionList() { return m_sharedVision; } void AddPlayerToVision(Player* player); @@ -1673,6 +1680,8 @@ class TC_GAME_API Unit : public WorldObject // Movement info Movement::MoveSpline * movespline; + uint8 m_forced_speed_changes[MAX_MOVE_TYPE]; + int32 GetHighestExclusiveSameEffectSpellGroupValue(AuraEffect const* aurEff, AuraType auraType, bool checkMiscValue = false, int32 miscValue = 0) const; bool IsHighestExclusiveAura(Aura const* aura, bool removeOtherAuraApplications = false); bool IsHighestExclusiveAuraEffect(SpellInfo const* spellInfo, AuraType auraType, int32 effectAmount, uint8 auraEffectMask, bool removeOtherAuraApplications = false); @@ -1751,12 +1760,11 @@ class TC_GAME_API Unit : public WorldObject float m_speed_rate[MAX_MOVE_TYPE]; - Unit* m_unitMovedByMe; // only ever set for players, and only for direct client control - Player* m_playerMovingMe; // only set for direct client control (possess effects, vehicles and similar) Unit* m_charmer; // Unit that is charming ME Unit* m_charmed; // Unit that is being charmed BY ME CharmInfo* m_charmInfo; SharedVisionList m_sharedVision; + GameClient* _gameClientMovingMe; MotionMaster* i_motionMaster; diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index c9a4a483801..f549f75b49f 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -854,64 +854,6 @@ void WorldSession::HandleNextCinematicCamera(WorldPackets::Misc::NextCinematicCa GetPlayer()->GetCinematicMgr()->BeginCinematic(); } -void WorldSession::HandleFeatherFallAck(WorldPacket& recvData) -{ - TC_LOG_DEBUG("network", "WORLD: CMSG_MOVE_FEATHER_FALL_ACK"); - - // no used - recvData.rfinish(); // prevent warnings spam -} - -void WorldSession::HandleMoveUnRootAck(WorldPacket& recvData) -{ - // no used - recvData.rfinish(); // prevent warnings spam -/* - uint64 guid; - recvData >> guid; - - // now can skip not our packet - if (_player->GetGUID() != guid) - { - recvData.rfinish(); // prevent warnings spam - return; - } - - TC_LOG_DEBUG("network", "WORLD: CMSG_FORCE_MOVE_UNROOT_ACK"); - - recvData.read_skip<uint32>(); // unk - - MovementInfo movementInfo; - movementInfo.guid = guid; - ReadMovementInfo(recvData, &movementInfo); - recvData.read_skip<float>(); // unk2 -*/ -} - -void WorldSession::HandleMoveRootAck(WorldPacket& recvData) -{ - // no used - recvData.rfinish(); // prevent warnings spam -/* - uint64 guid; - recvData >> guid; - - // now can skip not our packet - if (_player->GetGUID() != guid) - { - recvData.rfinish(); // prevent warnings spam - return; - } - - TC_LOG_DEBUG("network", "WORLD: CMSG_FORCE_MOVE_ROOT_ACK"); - - recvData.read_skip<uint32>(); // unk - - MovementInfo movementInfo; - ReadMovementInfo(recvData, &movementInfo); -*/ -} - void WorldSession::HandleCompleteMovie(WorldPackets::Misc::CompleteMovie& /*packet*/) { uint32 movie = _player->GetMovie(); @@ -1327,25 +1269,6 @@ void WorldSession::HandleSetRaidDifficultyOpcode(WorldPacket& recvData) } } -void WorldSession::HandleMoveSetCanFlyAckOpcode(WorldPacket& recvData) -{ - // fly mode on/off - TC_LOG_DEBUG("network", "WORLD: CMSG_MOVE_SET_CAN_FLY_ACK"); - - ObjectGuid guid; // guid - unused - recvData >> guid.ReadAsPacked(); - - recvData.read_skip<uint32>(); // unk - - MovementInfo movementInfo; - movementInfo.guid = guid; - ReadMovementInfo(recvData, &movementInfo); - - recvData.read_skip<float>(); // unk2 - - _player->GetUnitBeingMoved()->m_movementInfo.flags = movementInfo.GetMovementFlags(); -} - void WorldSession::HandleSetTaxiBenchmarkOpcode(WorldPacket& recvData) { TC_LOG_DEBUG("network", "WORLD: CMSG_SET_TAXI_BENCHMARK_MODE"); diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 7b7ccf8771c..a292db59548 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -15,23 +15,25 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "Battleground.h" #include "Common.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "Opcodes.h" -#include "Log.h" #include "Corpse.h" -#include "Player.h" +#include "GameTime.h" +#include "GameClient.h" +#include "InstanceSaveMgr.h" +#include "Log.h" #include "MapManager.h" #include "MotionMaster.h" #include "MovementGenerator.h" #include "MoveSpline.h" -#include "Transport.h" -#include "Battleground.h" -#include "InstanceSaveMgr.h" +#include "ObjectAccessor.h" #include "ObjectMgr.h" +#include "Opcodes.h" +#include "Player.h" +#include "Transport.h" #include "Vehicle.h" -#include "GameTime.h" +#include "WorldPacket.h" +#include "WorldSession.h" #include <boost/accumulators/statistics/variance.hpp> #include <boost/accumulators/accumulators.hpp> #include <boost/accumulators/statistics.hpp> @@ -212,17 +214,22 @@ void WorldSession::HandleMoveTeleportAck(WorldPacket& recvData) recvData >> guid.ReadAsPacked(); + if (!IsRightUnitBeingMoved(guid)) + { + recvData.rfinish(); // prevent warnings spam + return; + } + uint32 sequenceIndex, time; recvData >> sequenceIndex >> time; - Player* plMover = _player->GetUnitBeingMoved()->ToPlayer(); + GameClient* client = GetGameClient(); + Unit* mover = client->GetActivelyMovedUnit(); + Player* plMover = mover->ToPlayer(); if (!plMover || !plMover->IsBeingTeleportedNear()) return; - if (guid != plMover->GetGUID()) - return; - plMover->SetSemaphoreTeleportNear(false); uint32 old_zone = plMover->GetZoneId(); @@ -259,10 +266,17 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvData) { uint16 opcode = recvData.GetOpcode(); - Unit* mover = _player->GetUnitBeingMoved(); + ObjectGuid guid; + recvData >> guid.ReadAsPacked(); - ASSERT(mover != nullptr); // there must always be a mover + if (!IsRightUnitBeingMoved(guid)) + { + recvData.rfinish(); // prevent warnings spam + return; + } + GameClient* client = GetGameClient(); + Unit* mover = client->GetActivelyMovedUnit(); Player* plrMover = mover->ToPlayer(); // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck @@ -273,9 +287,6 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvData) } /* extract packet */ - ObjectGuid guid; - - recvData >> guid.ReadAsPacked(); MovementInfo movementInfo; movementInfo.guid = guid; @@ -283,10 +294,6 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvData) recvData.rfinish(); // prevent warnings spam - // prevent tampered movement data - if (guid != mover->GetGUID()) - return; - if (!movementInfo.pos.IsPositionValid()) { recvData.rfinish(); // prevent warnings spam @@ -416,7 +423,7 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvData) if (movementInfo.pos.GetPositionZ() < plrMover->GetMap()->GetMinHeight(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY())) { - if (!(plrMover->GetBattleground() && plrMover->GetBattleground()->HandlePlayerUnderMap(_player))) + if (!(plrMover->GetBattleground() && plrMover->GetBattleground()->HandlePlayerUnderMap(plrMover))) { // NOTE: this is actually called many times while falling // even after the player has been teleported away @@ -448,13 +455,15 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recvData) recvData >> guid.ReadAsPacked(); - // now can skip not our packet - if (_player->GetGUID() != guid) + if (!IsRightUnitBeingMoved(guid)) { - recvData.rfinish(); // prevent warnings spam + recvData.rfinish(); // prevent warnings spam return; } + GameClient* client = GetGameClient(); + Unit* mover = client->GetActivelyMovedUnit(); + // continue parse packet recvData >> unk1; // counter or moveEvent @@ -491,25 +500,25 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recvData) // skip all forced speed changes except last and unexpected // in run/mounted case used one ACK and it must be skipped.m_forced_speed_changes[MOVE_RUN} store both. - if (_player->m_forced_speed_changes[force_move_type] > 0) + if (mover->m_forced_speed_changes[force_move_type] > 0) { - --_player->m_forced_speed_changes[force_move_type]; - if (_player->m_forced_speed_changes[force_move_type] > 0) + --mover->m_forced_speed_changes[force_move_type]; + if (mover->m_forced_speed_changes[force_move_type] > 0) return; } - if (!_player->GetTransport() && std::fabs(_player->GetSpeed(move_type) - newspeed) > 0.01f) + if (!mover->GetTransport() && std::fabs(mover->GetSpeed(move_type) - newspeed) > 0.01f) { - if (_player->GetSpeed(move_type) > newspeed) // must be greater - just correct + if (mover->GetSpeed(move_type) > newspeed) // must be greater - just correct { TC_LOG_ERROR("network", "%sSpeedChange player %s is NOT correct (must be %f instead %f), force set to correct value", - move_type_name[move_type], _player->GetName().c_str(), _player->GetSpeed(move_type), newspeed); - _player->SetSpeedRate(move_type, _player->GetSpeedRate(move_type)); + move_type_name[move_type], _player->GetName().c_str(), mover->GetSpeed(move_type), newspeed); + mover->SetSpeedRate(move_type, mover->GetSpeedRate(move_type)); } else // must be lesser - cheating { TC_LOG_DEBUG("misc", "Player %s from account id %u kicked for incorrect speed (must be %f instead %f)", - _player->GetName().c_str(), _player->GetSession()->GetAccountId(), _player->GetSpeed(move_type), newspeed); + _player->GetName().c_str(), _player->GetSession()->GetAccountId(), mover->GetSpeed(move_type), newspeed); _player->GetSession()->KickPlayer("WorldSession::HandleForceSpeedChangeAck Incorrect speed"); } } @@ -522,9 +531,21 @@ void WorldSession::HandleSetActiveMoverOpcode(WorldPacket &recvData) ObjectGuid guid; recvData >> guid; - if (GetPlayer()->IsInWorld()) - if (_player->GetUnitBeingMoved()->GetGUID() != guid) - TC_LOG_DEBUG("network", "HandleSetActiveMoverOpcode: incorrect mover guid: mover is %s and should be %s" , guid.ToString().c_str(), _player->GetUnitBeingMoved()->GetGUID().ToString().c_str()); + GameClient* client = GetGameClient(); + + // step 1: look at the list of units that this client is allowed to move. check if the client is allowed to even move the + // unit that is mentioned in the packet. if not, either silently ignore, log this event or kick the client. + if (!client->IsAllowedToMove(guid)) + { + // @todo log or kick or do nothing depending on configuration + TC_LOG_DEBUG("entities.unit", "set active mover FAILED for client of player %s. GUID %s.", _player->GetName().c_str(), guid.ToString().c_str()); + return; + } + + // step 2: + TC_LOG_DEBUG("entities.unit", "set active mover OK for client of player %s. GUID %s.", _player->GetName().c_str(), guid.ToString().c_str()); + Unit* newActivelyMovedUnit = ObjectAccessor::GetUnit(*_player, guid); + client->SetActivelyMovedUnit(newActivelyMovedUnit); } void WorldSession::HandleMoveNotActiveMover(WorldPacket &recvData) @@ -533,13 +554,20 @@ void WorldSession::HandleMoveNotActiveMover(WorldPacket &recvData) ObjectGuid old_mover_guid; recvData >> old_mover_guid.ReadAsPacked(); + recvData.rfinish(); // prevent warnings spam. + // the movement info in this kind of packet is ignored for now. It's unclear if it should be used. - MovementInfo mi; - ReadMovementInfo(recvData, &mi); + GameClient* client = GetGameClient(); - mi.guid = old_mover_guid; + if (client->GetActivelyMovedUnit() == nullptr || client->GetActivelyMovedUnit()->GetGUID() != old_mover_guid) + { + // this shouldn't never happen in theory + TC_LOG_WARN("entities.unit", "unset active mover FAILED for client of player %s. GUID %s.", _player->GetName().c_str(), old_mover_guid.ToString().c_str()); + return; + } - _player->m_movementInfo = mi; + TC_LOG_DEBUG("entities.unit", "unset active mover OK for client of player %s. GUID %s.", _player->GetName().c_str(), old_mover_guid.ToString().c_str()); + client->SetActivelyMovedUnit(nullptr); } void WorldSession::HandleMountSpecialAnimOpcode(WorldPacket& /*recvData*/) @@ -557,19 +585,25 @@ void WorldSession::HandleMoveKnockBackAck(WorldPacket& recvData) ObjectGuid guid; recvData >> guid.ReadAsPacked(); - if (_player->GetUnitBeingMoved()->GetGUID() != guid) + if (!IsRightUnitBeingMoved(guid)) + { + recvData.rfinish(); // prevent warnings spam return; + } recvData.read_skip<uint32>(); // unk MovementInfo movementInfo; ReadMovementInfo(recvData, &movementInfo); - _player->m_movementInfo = movementInfo; + GameClient* client = GetGameClient(); + Unit* mover = client->GetActivelyMovedUnit(); + + mover->m_movementInfo = movementInfo; WorldPacket data(MSG_MOVE_KNOCK_BACK, 66); data << guid.WriteAsPacked(); - _player->BuildMovementPacket(&data); + mover->BuildMovementPacket(&data); // knockback specific info data << movementInfo.jump.sinAngle; @@ -577,7 +611,7 @@ void WorldSession::HandleMoveKnockBackAck(WorldPacket& recvData) data << movementInfo.jump.xyspeed; data << movementInfo.jump.zspeed; - _player->SendMessageToSet(&data, false); + client->GetBasePlayer()->SendMessageToSet(&data, false); } void WorldSession::HandleMoveHoverAck(WorldPacket& recvData) @@ -587,6 +621,12 @@ void WorldSession::HandleMoveHoverAck(WorldPacket& recvData) ObjectGuid guid; // guid - unused recvData >> guid.ReadAsPacked(); + if (!IsRightUnitBeingMoved(guid)) + { + recvData.rfinish(); // prevent warnings spam + return; + } + recvData.read_skip<uint32>(); // unk MovementInfo movementInfo; @@ -602,6 +642,113 @@ void WorldSession::HandleMoveWaterWalkAck(WorldPacket& recvData) ObjectGuid guid; // guid - unused recvData >> guid.ReadAsPacked(); + if (!IsRightUnitBeingMoved(guid)) + { + recvData.rfinish(); // prevent warnings spam + return; + } + + recvData.read_skip<uint32>(); // unk + + MovementInfo movementInfo; + ReadMovementInfo(recvData, &movementInfo); + + recvData.read_skip<uint32>(); // unk2 +} + +void WorldSession::HandleMoveRootAck(WorldPacket& recvData) +{ + TC_LOG_DEBUG("network", "CMSG_FORCE_MOVE_ROOT_ACK"); + + ObjectGuid guid; // guid - unused + recvData >> guid.ReadAsPacked(); + + if (!IsRightUnitBeingMoved(guid)) + { + recvData.rfinish(); // prevent warnings spam + return; + } + + recvData.read_skip<uint32>(); // unk + + MovementInfo movementInfo; + ReadMovementInfo(recvData, &movementInfo); +} + +void WorldSession::HandleFeatherFallAck(WorldPacket& recvData) +{ + TC_LOG_DEBUG("network", "WORLD: CMSG_MOVE_FEATHER_FALL_ACK"); + + ObjectGuid guid; // guid - unused + recvData >> guid.ReadAsPacked(); + + if (!IsRightUnitBeingMoved(guid)) + { + recvData.rfinish(); // prevent warnings spam + return; + } + + recvData.read_skip<uint32>(); // unk + + MovementInfo movementInfo; + ReadMovementInfo(recvData, &movementInfo); + + recvData.read_skip<uint32>(); // unk2 +} + +void WorldSession::HandleMoveUnRootAck(WorldPacket& recvData) +{ + TC_LOG_DEBUG("network", "WORLD: CMSG_FORCE_MOVE_UNROOT_ACK"); + + ObjectGuid guid; // guid - unused + recvData >> guid.ReadAsPacked(); + + if (!IsRightUnitBeingMoved(guid)) + { + recvData.rfinish(); // prevent warnings spam + return; + } + + recvData.read_skip<uint32>(); // unk + + MovementInfo movementInfo; + ReadMovementInfo(recvData, &movementInfo); +} + +void WorldSession::HandleMoveSetCanFlyAckOpcode(WorldPacket& recvData) +{ + TC_LOG_DEBUG("network", "WORLD: CMSG_MOVE_SET_CAN_FLY_ACK"); + + ObjectGuid guid; // guid - unused + recvData >> guid.ReadAsPacked(); + + if (!IsRightUnitBeingMoved(guid)) + { + recvData.rfinish(); // prevent warnings spam + return; + } + + recvData.read_skip<uint32>(); // unk + + MovementInfo movementInfo; + ReadMovementInfo(recvData, &movementInfo); + + recvData.read_skip<uint32>(); // unk2 +} + +void WorldSession::HandleMoveSetCanTransitionBetweenSwinAndFlyAck(WorldPacket& recvData) +{ + TC_LOG_DEBUG("network", "WORLD: CMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY_ACK"); + + ObjectGuid guid; + recvData >> guid.ReadAsPacked(); + + if (!IsRightUnitBeingMoved(guid)) + { + recvData.rfinish(); // prevent warnings spam + return; + } + recvData.read_skip<uint32>(); // unk MovementInfo movementInfo; @@ -610,6 +757,65 @@ void WorldSession::HandleMoveWaterWalkAck(WorldPacket& recvData) recvData.read_skip<uint32>(); // unk2 } +void WorldSession::HandleMoveGravityDisableAck(WorldPacket& recvData) +{ + TC_LOG_DEBUG("network", "WORLD: CMSG_MOVE_GRAVITY_DISABLE_ACK"); + + ObjectGuid guid; + recvData >> guid.ReadAsPacked(); + + if (!IsRightUnitBeingMoved(guid)) + { + recvData.rfinish(); // prevent warnings spam + return; + } + + recvData.read_skip<uint32>(); // unk + + MovementInfo movementInfo; + ReadMovementInfo(recvData, &movementInfo); +} + +void WorldSession::HandleMoveGravityEnableAck(WorldPacket& recvData) +{ + TC_LOG_DEBUG("network", "WORLD: CMSG_MOVE_GRAVITY_ENABLE_ACK"); + + ObjectGuid guid; + recvData >> guid.ReadAsPacked(); + + if (!IsRightUnitBeingMoved(guid)) + { + recvData.rfinish(); // prevent warnings spam + return; + } + + recvData.read_skip<uint32>(); // unk + + MovementInfo movementInfo; + ReadMovementInfo(recvData, &movementInfo); +} + +void WorldSession::HandleMoveSetCollisionHgtAck(WorldPacket& recvData) +{ + TC_LOG_DEBUG("network", "WORLD: CMSG_MOVE_SET_COLLISION_HGT_ACK"); + + ObjectGuid guid; + float newValue; + recvData >> guid.ReadAsPacked(); + + if (!IsRightUnitBeingMoved(guid)) + { + recvData.rfinish(); // prevent warnings spam + return; + } + + recvData.read_skip<uint32>(); // movement counter + recvData >> newValue; + + MovementInfo movementInfo; + ReadMovementInfo(recvData, &movementInfo); +} + void WorldSession::HandleSummonResponseOpcode(WorldPacket& recvData) { if (!_player->IsAlive() || _player->IsInCombat()) @@ -632,21 +838,14 @@ void WorldSession::HandleMoveTimeSkippedOpcode(WorldPacket& recvData) recvData >> guid.ReadAsPacked(); recvData >> timeSkipped; - Unit* mover = GetPlayer()->m_unitMovedByMe; - - if (!mover) - { - TC_LOG_WARN("entities.player", "WorldSession::HandleMoveTimeSkippedOpcode wrong mover state from the unit moved by the player %s", GetPlayer()->GetGUID().ToString().c_str()); - return; - } - - // prevent tampered movement data - if (guid != mover->GetGUID()) + if (!IsRightUnitBeingMoved(guid)) { - TC_LOG_WARN("entities.player", "WorldSession::HandleMoveTimeSkippedOpcode wrong guid from the unit moved by the player %s", GetPlayer()->GetGUID().ToString().c_str()); + recvData.rfinish(); // prevent warnings spam return; } + GameClient* client = GetGameClient(); + Unit* mover = client->GetActivelyMovedUnit(); mover->m_movementInfo.time += timeSkipped; WorldPacket data(MSG_MOVE_TIME_SKIPPED, recvData.size()); diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index c3ac4582ad4..775a57c448e 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -67,7 +67,7 @@ void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) Player* pUser = _player; // ignore for remote control state - if (pUser->GetUnitBeingMoved() != pUser) + if (pUser->IsCharming()) return; uint8 bagIndex, slot, castFlags; @@ -178,7 +178,7 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) Player* player = GetPlayer(); // ignore for remote control state - if (player->GetUnitBeingMoved() != player) + if (player->IsCharming()) return; // additional check, client outputs message on its own @@ -298,8 +298,8 @@ void WorldSession::HandleGameObjectUseOpcode(WorldPacket& recvData) if (GameObject* obj = GetPlayer()->GetGameObjectIfCanInteractWith(guid)) { // ignore for remote control state - if (GetPlayer()->GetUnitBeingMoved() != GetPlayer()) - if (!(GetPlayer()->IsOnVehicle(GetPlayer()->GetUnitBeingMoved()) || GetPlayer()->IsMounted()) && !obj->GetGOInfo()->IsUsableMounted()) + if (GetPlayer()->IsCharming()) + if (!(GetPlayer()->IsOnVehicle(GetPlayer()->GetCharmed()) || GetPlayer()->IsMounted()) && !obj->GetGOInfo()->IsUsableMounted()) return; obj->Use(GetPlayer()); @@ -314,7 +314,7 @@ void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket) TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_GAMEOBJ_REPORT_USE Message [%s]", guid.ToString().c_str()); // ignore for remote control state - if (_player->GetUnitBeingMoved() != _player) + if (_player->IsCharming()) return; if (GameObject* go = GetPlayer()->GetGameObjectIfCanInteractWith(guid)) @@ -336,7 +336,7 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) TC_LOG_DEBUG("network", "WORLD: got cast spell packet, castCount: %u, spellId: %u, castFlags: %u, data length = %u", castCount, spellId, castFlags, (uint32)recvPacket.size()); // ignore for remote control state (for player case) - Unit* mover = _player->GetUnitBeingMoved(); + Unit* mover = _player->GetCharmedOrSelf(); if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER) { recvPacket.rfinish(); // prevent spam at ignore packet @@ -546,9 +546,10 @@ void WorldSession::HandleCancelAutoRepeatSpellOpcode(WorldPackets::Spells::Cance void WorldSession::HandleCancelChanneling(WorldPackets::Spells::CancelChannelling& cancelChanneling) { + Unit* mover = _player->GetCharmedOrSelf(); + // ignore for remote control state (for player case) - Unit* mover = _player->GetUnitBeingMoved(); - if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER) + if (_player->GetCharmed() && _player->GetCharmed()->GetTypeId() == TYPEID_PLAYER) return; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(cancelChanneling.ChannelSpell); @@ -569,7 +570,7 @@ void WorldSession::HandleCancelChanneling(WorldPackets::Spells::CancelChannellin void WorldSession::HandleTotemDestroyed(WorldPackets::Totem::TotemDestroyed& totemDestroyed) { // ignore for remote control state - if (_player->GetUnitBeingMoved() != _player) + if (_player->IsCharming()) return; uint8 slotId = totemDestroyed.Slot; diff --git a/src/server/game/Handlers/TaxiHandler.cpp b/src/server/game/Handlers/TaxiHandler.cpp index 2d077ed1527..0b8754dd978 100644 --- a/src/server/game/Handlers/TaxiHandler.cpp +++ b/src/server/game/Handlers/TaxiHandler.cpp @@ -202,8 +202,14 @@ void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& recvData) { TC_LOG_DEBUG("network", "WORLD: Received CMSG_MOVE_SPLINE_DONE"); - uint64 guid; // used only for proper packet read - recvData.readPackGUID(guid); + ObjectGuid guid; + recvData >> guid.ReadAsPacked(); + + if (!IsRightUnitBeingMoved(guid)) + { + recvData.rfinish(); // prevent warnings spam + return; + } MovementInfo movementInfo; // used only for proper packet read ReadMovementInfo(recvData, &movementInfo); diff --git a/src/server/game/Server/GameClient.cpp b/src/server/game/Server/GameClient.cpp new file mode 100644 index 00000000000..f2a662d63dd --- /dev/null +++ b/src/server/game/Server/GameClient.cpp @@ -0,0 +1,63 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "GameClient.h" +#include "WorldSession.h" +#include "Unit.h" +#include "Player.h" + +GameClient::GameClient(WorldSession* sessionToServer) +{ + _sessionToServer = sessionToServer; + _activelyMovedUnit = nullptr; +} + +void GameClient::AddAllowedMover(Unit* unit) +{ + _allowedMovers.insert(unit->GetGUID()); + unit->SetGameClientMovingMe(this); +} + +void GameClient::RemoveAllowedMover(Unit* unit) +{ + _allowedMovers.erase(unit->GetGUID()); + if (unit->GetGameClientMovingMe() == this) + unit->SetGameClientMovingMe(nullptr); +} + +bool GameClient::IsAllowedToMove(Unit* unit) const +{ + return _allowedMovers.count(unit->GetGUID()); +} + +bool GameClient::IsAllowedToMove(ObjectGuid guid) const +{ + return _allowedMovers.count(guid); +} + +void GameClient::SetMovedUnit(Unit* target, bool allowMove) +{ + if (allowMove) + AddAllowedMover(target); + else + RemoveAllowedMover(target); +} + +void GameClient::SendDirectMessage(WorldPacket const* data) const +{ + GetBasePlayer()->SendDirectMessage(data); +} diff --git a/src/server/game/Server/GameClient.h b/src/server/game/Server/GameClient.h new file mode 100644 index 00000000000..4baf5015ddb --- /dev/null +++ b/src/server/game/Server/GameClient.h @@ -0,0 +1,54 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GAMECLIENT_H +#define __GAMECLIENT_H + +#include "WorldSession.h" + +class Player; +class Unit; + +class TC_GAME_API GameClient +{ + public: + GameClient(WorldSession* sessionToServer); + + void AddAllowedMover(Unit* unit); + void RemoveAllowedMover(Unit* unit); + bool IsAllowedToMove(Unit* unit) const; + bool IsAllowedToMove(ObjectGuid guid) const; + void SetMovedUnit(Unit* target, bool allowMove); + + Unit* GetActivelyMovedUnit() const { return _activelyMovedUnit; } + void SetActivelyMovedUnit(Unit* activelyMovedUnit) { _activelyMovedUnit = activelyMovedUnit; } + + Player* GetBasePlayer() const { return _sessionToServer->GetPlayer(); } + + void SendDirectMessage(WorldPacket const* data) const; + private: + // describe all units that this client has direct control over. Example, a player on a vehicle has client control over himself and the vehicle at the same time. + GuidUnorderedSet _allowedMovers; + + // set/unset upon receiving CMSG_SET_ACTIVE_MOVER and CMSG_MOVE_NOT_ACTIVE_MOVER by the client + // in other words, this field is set by the client (as long as the change is allowed by the server) + Unit* _activelyMovedUnit; + + WorldSession* _sessionToServer; +}; + +#endif // __GAMECLIENT_H diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index a8d84a8d0ea..0dff9c1ea93 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -960,7 +960,7 @@ void OpcodeTable::Initialize() /*0x33D*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOTD, STATUS_NEVER); /*0x33E*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY, STATUS_NEVER); /*0x33F*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_UNSET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY, STATUS_NEVER); - /*0x340*/ DEFINE_HANDLER(CMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY_ACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); + /*0x340*/ DEFINE_HANDLER(CMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveSetCanTransitionBetweenSwinAndFlyAck); /*0x341*/ DEFINE_HANDLER(MSG_MOVE_START_SWIM_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); /*0x342*/ DEFINE_HANDLER(MSG_MOVE_STOP_SWIM_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); /*0x343*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_SET_CAN_FLY, STATUS_NEVER); @@ -1245,7 +1245,7 @@ void OpcodeTable::Initialize() /*0x45A*/ DEFINE_HANDLER(MSG_MOVE_SET_PITCH_RATE_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); /*0x45B*/ DEFINE_HANDLER(MSG_MOVE_SET_PITCH_RATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); /*0x45C*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_FORCE_PITCH_RATE_CHANGE, STATUS_NEVER); - /*0x45D*/ DEFINE_HANDLER(CMSG_FORCE_PITCH_RATE_CHANGE_ACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); + /*0x45D*/ DEFINE_HANDLER(CMSG_FORCE_PITCH_RATE_CHANGE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleForceSpeedChangeAck); /*0x45E*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPLINE_SET_PITCH_RATE, STATUS_NEVER); /*0x45F*/ DEFINE_HANDLER(CMSG_CALENDAR_EVENT_INVITE_NOTES, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); /*0x460*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_CALENDAR_EVENT_INVITE_NOTES, STATUS_NEVER); @@ -1359,9 +1359,9 @@ void OpcodeTable::Initialize() /*0x4CC*/ DEFINE_HANDLER(CMSG_END_BATTLEFIELD_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); /*0x4CD*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MULTIPLE_PACKETS, STATUS_NEVER); /*0x4CE*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_GRAVITY_DISABLE, STATUS_NEVER); - /*0x4CF*/ DEFINE_HANDLER(CMSG_MOVE_GRAVITY_DISABLE_ACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); + /*0x4CF*/ DEFINE_HANDLER(CMSG_MOVE_GRAVITY_DISABLE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveGravityDisableAck ); /*0x4D0*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_GRAVITY_ENABLE, STATUS_NEVER); - /*0x4D1*/ DEFINE_HANDLER(CMSG_MOVE_GRAVITY_ENABLE_ACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); + /*0x4D1*/ DEFINE_HANDLER(CMSG_MOVE_GRAVITY_ENABLE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveGravityEnableAck); /*0x4D2*/ DEFINE_SERVER_OPCODE_HANDLER(MSG_MOVE_GRAVITY_CHNG, STATUS_NEVER); /*0x4D3*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPLINE_MOVE_GRAVITY_DISABLE, STATUS_NEVER); /*0x4D4*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPLINE_MOVE_GRAVITY_ENABLE, STATUS_NEVER); @@ -1431,7 +1431,7 @@ void OpcodeTable::Initialize() /*0x514*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_SEND_ALL_COMBAT_LOG, STATUS_NEVER); /*0x515*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_OPEN_LFG_DUNGEON_FINDER, STATUS_NEVER); /*0x516*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_SET_COLLISION_HGT, STATUS_NEVER); - /*0x517*/ DEFINE_HANDLER(CMSG_MOVE_SET_COLLISION_HGT_ACK, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); + /*0x517*/ DEFINE_HANDLER(CMSG_MOVE_SET_COLLISION_HGT_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveSetCollisionHgtAck ); /*0x518*/ DEFINE_HANDLER(MSG_MOVE_SET_COLLISION_HGT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); /*0x519*/ DEFINE_HANDLER(CMSG_CLEAR_RANDOM_BG_WIN_TIME, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); /*0x51A*/ DEFINE_HANDLER(CMSG_CLEAR_HOLIDAY_BG_WIN_TIME, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 18b46ffd73e..2405631cd75 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -28,6 +28,7 @@ #include "Common.h" #include "DatabaseEnv.h" #include "DBCStructure.h" +#include "GameClient.h" #include "GameTime.h" #include "Group.h" #include "Guild.h" @@ -136,7 +137,8 @@ WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr<WorldS _pendingTimeSyncRequests(), _timeSyncNextCounter(0), _timeSyncTimer(0), - _calendarEventCreationCooldown(0) + _calendarEventCreationCooldown(0), + _gameClient(new GameClient(this)) { memset(m_Tutorials, 0, sizeof(m_Tutorials)); @@ -165,6 +167,8 @@ WorldSession::~WorldSession() delete _RBACData; + delete _gameClient; + ///- empty incoming packet queue WorldPacket* packet = nullptr; while (_recvQueue.next(packet)) @@ -973,8 +977,8 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo* mi) */ REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY) && GetSecurity() == SEC_PLAYER && - !GetPlayer()->GetUnitBeingMoved()->HasAuraType(SPELL_AURA_FLY) && - !GetPlayer()->GetUnitBeingMoved()->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED), + !GetPlayer()->GetCharmedOrSelf()->HasAuraType(SPELL_AURA_FLY) && + !GetPlayer()->GetCharmedOrSelf()->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED), MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY); //! Cannot fly and fall at the same time @@ -1655,3 +1659,26 @@ void WorldSession::SendTimeSync() _timeSyncTimer = _timeSyncNextCounter == 0 ? 5000 : 10000; _timeSyncNextCounter++; } + +bool WorldSession::IsRightUnitBeingMoved(ObjectGuid guid) +{ + GameClient* client = GetGameClient(); + + // the client is attempting to tamper movement data + if (!client->GetActivelyMovedUnit() || client->GetActivelyMovedUnit()->GetGUID() != guid) + { + TC_LOG_INFO("entities.unit", "Attempt at tampering movement data by Player %s", _player->GetName().c_str()); + return false; + } + + // This can happen if a legitimate client has lost control of a unit but hasn't received SMSG_CONTROL_UPDATE before + // sending this packet yet. The server should silently ignore all MOVE messages coming from the client as soon + // as control over that unit is revoked (through a 'SMSG_CONTROL_UPDATE allowMove=false' message). + if (!client->IsAllowedToMove(guid)) + { + TC_LOG_DEBUG("entities.unit", "Bad or outdated movement data by Player %s", _player->GetName().c_str()); + return false; + } + + return true; +} diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index d2d31892004..582b083f2ff 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -37,6 +37,7 @@ #include <boost/circular_buffer.hpp> class Creature; +class GameClient; class GameObject; class InstanceSave; class Item; @@ -427,6 +428,8 @@ class TC_GAME_API WorldSession void InitializeSession(); void InitializeSessionCallback(CharacterDatabaseQueryHolder const& realmHolder); + GameClient* GetGameClient() const { return _gameClient; }; + rbac::RBACData* GetRBACData(); bool HasPermission(uint32 permissionId); void LoadPermissions(); @@ -622,20 +625,25 @@ class TC_GAME_API WorldSession // played time void HandlePlayedTime(WorldPackets::Character::PlayedTimeClient& packet); - // new - void HandleMoveUnRootAck(WorldPacket& recvPacket); - void HandleMoveRootAck(WorldPacket& recvPacket); - // new inspect void HandleInspectOpcode(WorldPacket& recvPacket); // new party stats void HandleInspectHonorStatsOpcode(WorldPacket& recvPacket); + void HandleForceSpeedChangeAck(WorldPacket& recvData); + void HandleMoveKnockBackAck(WorldPacket& recvPacket); + void HandleMoveTeleportAck(WorldPacket& recvPacket); void HandleMoveWaterWalkAck(WorldPacket& recvPacket); void HandleFeatherFallAck(WorldPacket& recvData); - void HandleMoveHoverAck(WorldPacket& recvData); + void HandleMoveUnRootAck(WorldPacket& recvPacket); + void HandleMoveRootAck(WorldPacket& recvPacket); + void HandleMoveSetCanFlyAckOpcode(WorldPacket& recvData); + void HandleMoveSetCanTransitionBetweenSwinAndFlyAck(WorldPacket& recvData); + void HandleMoveGravityDisableAck(WorldPacket& recvData); + void HandleMoveGravityEnableAck(WorldPacket& recvData); + void HandleMoveSetCollisionHgtAck(WorldPacket& recvData); void HandleMountSpecialAnimOpcode(WorldPacket& recvdata); @@ -646,12 +654,6 @@ class TC_GAME_API WorldSession // repair void HandleRepairItemOpcode(WorldPacket& recvPacket); - // Knockback - void HandleMoveKnockBackAck(WorldPacket& recvPacket); - - void HandleMoveTeleportAck(WorldPacket& recvPacket); - void HandleForceSpeedChangeAck(WorldPacket& recvData); - void HandleRepopRequest(WorldPackets::Misc::RepopRequest& packet); void HandleAutostoreLootItemOpcode(WorldPacket& recvPacket); void HandleLootMoneyOpcode(WorldPacket& recvPacket); @@ -987,7 +989,6 @@ class TC_GAME_API WorldSession void HandleFarSightOpcode(WorldPacket& recvData); void HandleSetDungeonDifficultyOpcode(WorldPacket& recvData); void HandleSetRaidDifficultyOpcode(WorldPacket& recvData); - void HandleMoveSetCanFlyAckOpcode(WorldPacket& recvData); void HandleSetTitleOpcode(WorldPacket& recvData); void HandleRealmSplitOpcode(WorldPacket& recvData); void HandleTimeSyncResponse(WorldPacket& recvData); @@ -1170,6 +1171,9 @@ class TC_GAME_API WorldSession return _legitCharacters.find(lowGUID) != _legitCharacters.end(); } + // Movement helpers + bool IsRightUnitBeingMoved(ObjectGuid guid); + // this stores the GUIDs of the characters who can login // characters who failed on Player::BuildEnumData shouldn't login GuidSet _legitCharacters; @@ -1239,6 +1243,7 @@ class TC_GAME_API WorldSession // Packets cooldown time_t _calendarEventCreationCooldown; + GameClient* _gameClient; WorldSession(WorldSession const& right) = delete; WorldSession& operator=(WorldSession const& right) = delete; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index cd78f49cd84..b95ce469b08 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -6186,7 +6186,7 @@ SpellCastResult Spell::CheckCasterAuras(uint32* param1) const if (unitCaster->GetCharmerGUID()) { if (Unit* charmer = unitCaster->GetCharmer()) - if (charmer->GetUnitBeingMoved() != unitCaster && !CheckSpellCancelsCharm(param1)) + if (charmer->GetCharmed() != unitCaster && !CheckSpellCancelsCharm(param1)) result = SPELL_FAILED_CHARMED; } */ |