diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 196d3393d4..1b6dc981b5 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -3002,7 +3002,7 @@ float Creature::GetNativeObjectScale() const void Creature::UpdateMovementFlags(bool initializeDBStates) { // 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 2de111c7b1..afdc577bbf 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -46,6 +46,7 @@ #include "DB2Stores.h" #include "DisableMgr.h" #include "Formulas.h" +#include "GameClient.h" #include "GameEventMgr.h" #include "GameObjectAI.h" #include "GameTime.h" @@ -279,9 +280,6 @@ Player::Player(WorldSession* session): Unit(true) m_itemUpdateQueueBlocked = false; - for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i) - m_forced_speed_changes[i] = 0; - m_stableSlots = 0; /////////////////// Instance System ///////////////////// @@ -317,8 +315,6 @@ Player::Player(WorldSession* session): Unit(true) // Player summoning m_summon_expire = 0; - m_unitMovedByMe = this; - m_playerMovingMe = this; m_seer = this; @@ -22928,7 +22924,7 @@ bool Player::IsNeverVisible() const bool Player::CanAlwaysSee(WorldObject const* obj) const { // Always can see self - if (m_unitMovedByMe == obj) + if (GetCharmedOrSelf() == obj) return true; if (ObjectGuid guid = GetGuidValue(PLAYER_FARSIGHT)) @@ -23212,14 +23208,16 @@ Player* Player::GetSelectedPlayer() const void Player::SendComboPoints() { + Unit* combotarget = ObjectAccessor::GetUnit(*this, m_comboTarget); if (combotarget) { WorldPacket data; - if (m_unitMovedByMe != this) + Player* movingMe = GetCharmerOrSelfPlayer(); + if (movingMe != this) { - data.Initialize(SMSG_PET_UPDATE_COMBO_POINTS, m_unitMovedByMe->GetPackGUID().size()+combotarget->GetPackGUID().size()+1); - data << m_unitMovedByMe->GetPackGUID(); + data.Initialize(SMSG_PET_UPDATE_COMBO_POINTS, movingMe->GetPackGUID().size()+combotarget->GetPackGUID().size()+1); + data << movingMe->GetPackGUID(); } else data.Initialize(SMSG_UPDATE_COMBO_POINTS, combotarget->GetPackGUID().size()+1); @@ -23415,10 +23413,7 @@ void Player::SendInitialPacketsBeforeAddToMap(bool firstLogin /*= false*/) // SMSG_UPDATE_WORLD_STATE // SMSG_POWER_UPDATE - // Not going with SetMover(this); because sniffs show no packet - m_unitMovedByMe->m_playerMovingMe = nullptr; - m_unitMovedByMe = this; - m_unitMovedByMe->m_playerMovingMe = this; + GetSession()->GetGameClient()->AddAllowedMover(this); } void Player::SendInitialPacketsAfterAddToMap() @@ -24758,54 +24753,38 @@ void Player::ResurrectUsingRequestData() void Player::SetClientControl(Unit* target, bool allowMove) { - // still affected by some aura that shouldn't allow control, only allow on last such aura to be removed - if (allowMove && target->HasUnitState(UNIT_STATE_CANT_CLIENT_CONTROL)) + // a player can never client control nothing + ASSERT(target); + + // don't allow possession to be overridden + if (target->HasUnitState(UNIT_STATE_CHARMED) && (GetGUID() != target->GetCharmerGUID())) { - // this should never happen, otherwise m_unitBeingMoved might be left dangling! - ASSERT(GetUnitBeingMoved() == target); + TC_LOG_ERROR("entities.player", "Player '%s' attempt to client control '%s', which is charmed by GUID %s", + GetName().c_str(), target->GetName().c_str(), target->GetCharmerGUID().ToString().c_str()); return; } + // still affected by some aura that shouldn't allow control, only allow on last such aura to be removed + if (target->HasUnitState(UNIT_STATE_FLEEING | UNIT_STATE_CONFUSED)) + allowMove = false; + WorldPacket data(SMSG_CLIENT_CONTROL_UPDATE, target->GetPackGUID().size()+1); data << target->GetPackGUID(); data << uint8(allowMove ? 1 : 0); SendDirectMessage(&data); - if (this != target) - SetViewpoint(target, allowMove); + WorldObject* viewpoint = GetViewpoint(); + if (!viewpoint) + viewpoint = this; + if (target != viewpoint) + { + if (viewpoint != this) + SetViewpoint(viewpoint, false); + if (target != this) + SetViewpoint(target, true); + } - if (allowMove) - SetMover(target); -} - -void Player::SetMover(Unit* target) -{ - m_unitMovedByMe->m_playerMovingMe = nullptr; - m_unitMovedByMe = target; - m_unitMovedByMe->m_playerMovingMe = this; - - ObjectGuid guid = target->GetGUID(); - - WorldPacket data(SMSG_MOVE_SET_ACTIVE_MOVER, 9); - data.WriteBit(guid[5]); - data.WriteBit(guid[7]); - data.WriteBit(guid[3]); - data.WriteBit(guid[6]); - data.WriteBit(guid[0]); - data.WriteBit(guid[4]); - data.WriteBit(guid[1]); - data.WriteBit(guid[2]); - - data.WriteByteSeq(guid[6]); - data.WriteByteSeq(guid[2]); - data.WriteByteSeq(guid[3]); - data.WriteByteSeq(guid[0]); - data.WriteByteSeq(guid[5]); - data.WriteByteSeq(guid[7]); - data.WriteByteSeq(guid[1]); - data.WriteByteSeq(guid[4]); - - SendDirectMessage(&data); + GetGameClient()->SetMovedUnit(target, allowMove); } void Player::UpdateZoneDependentAuras(uint32 newZone) @@ -27834,7 +27813,7 @@ void Player::ValidateMovementInfo(MovementInfo* mi) mi->RemoveMovementFlag((maskToRemove)); #endif - if (!m_unitMovedByMe->GetVehicleBase() || !(m_unitMovedByMe->GetVehicle()->GetVehicleInfo()->Flags & VEHICLE_FLAG_FIXED_POSITION)) + if (!GetCharmedOrSelf()->GetVehicleBase() || !(GetCharmedOrSelf()->GetVehicle()->GetVehicleInfo()->Flags & VEHICLE_FLAG_FIXED_POSITION)) REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT), MOVEMENTFLAG_ROOT); /*! This must be a packet spoofing attempt. MOVEMENTFLAG_ROOT sent from the client is not valid @@ -27845,7 +27824,7 @@ void Player::ValidateMovementInfo(MovementInfo* mi) MOVEMENTFLAG_MASK_MOVING); //! Cannot hover without SPELL_AURA_HOVER - REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_HOVER) && !m_unitMovedByMe->HasAuraType(SPELL_AURA_HOVER), + REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_HOVER) && !GetCharmedOrSelf()->HasAuraType(SPELL_AURA_HOVER), MOVEMENTFLAG_HOVER); //! Cannot ascend and descend at the same time @@ -27870,12 +27849,12 @@ void Player::ValidateMovementInfo(MovementInfo* mi) //! Cannot walk on water without SPELL_AURA_WATER_WALK except for ghosts REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_WATERWALKING) && - !m_unitMovedByMe->HasAuraType(SPELL_AURA_WATER_WALK) && - !m_unitMovedByMe->HasAuraType(SPELL_AURA_GHOST), + !GetCharmedOrSelf()->HasAuraType(SPELL_AURA_WATER_WALK) && + !GetCharmedOrSelf()->HasAuraType(SPELL_AURA_GHOST), MOVEMENTFLAG_WATERWALKING); //! Cannot feather fall without SPELL_AURA_FEATHER_FALL - REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FALLING_SLOW) && !m_unitMovedByMe->HasAuraType(SPELL_AURA_FEATHER_FALL), + REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FALLING_SLOW) && !GetCharmedOrSelf()->HasAuraType(SPELL_AURA_FEATHER_FALL), MOVEMENTFLAG_FALLING_SLOW); /*! Cannot fly if no fly auras present. Exception is being a GM. @@ -27885,8 +27864,8 @@ void Player::ValidateMovementInfo(MovementInfo* mi) */ REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY) && GetSession()->GetSecurity() == SEC_PLAYER && - !m_unitMovedByMe->HasAuraType(SPELL_AURA_FLY) && - !m_unitMovedByMe->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED), + !GetCharmedOrSelf()->HasAuraType(SPELL_AURA_FLY) && + !GetCharmedOrSelf()->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED), MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY); REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY | MOVEMENTFLAG_CAN_FLY) && mi->HasMovementFlag(MOVEMENTFLAG_FALLING), @@ -28284,3 +28263,8 @@ void Player::SendTamePetFailure(PetTameFailureReason reason) data << uint8(reason); SendDirectMessage(&data); } + +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 b225233bc6..357db26329 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -65,6 +65,7 @@ class Channel; class CharacterCreateInfo; class Creature; class DynamicObject; +class GameClient; class Group; class Guild; class Item; @@ -1837,6 +1838,7 @@ class TC_GAME_API Player : public Unit, public GridObject 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; @@ -2138,16 +2140,13 @@ class TC_GAME_API Player : public Unit, public GridObject /*** VARIOUS SYSTEMS ***/ /*********************************************************/ void UpdateFallInformationIfNeed(MovementInfo const& minfo, uint16 opcode); - // only changed for direct client control (possess, vehicle etc.), not stuff you control using pet commands - Unit* m_unitMovedByMe; + WorldObject* m_seer; void SetFallInformation(uint32 time, float z); void HandleFall(MovementInfo const& movementInfo); void SetClientControl(Unit* target, bool allowMove); - void SetMover(Unit* target); - void SetSeer(WorldObject* target) { m_seer = target; } void SetViewpoint(WorldObject* target, bool apply); WorldObject* GetViewpoint() const; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 79ad167575..8a49a378b8 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -34,6 +34,7 @@ #include "CreatureAIImpl.h" #include "CreatureGroups.h" #include "Formulas.h" +#include "GameClient.h" #include "GameTime.h" #include "GridNotifiersImpl.h" #include "Group.h" @@ -325,7 +326,7 @@ bool DispelableAura::RollDispel() const Unit::Unit(bool isWorldObject) : - WorldObject(isWorldObject), m_playerMovingMe(nullptr), m_lastSanctuaryTime(0), + WorldObject(isWorldObject), m_lastSanctuaryTime(0), LastCharmerGUID(), m_ControlledByPlayer(false), movespline(new Movement::MoveSpline()), m_AutoRepeatFirstCast(false), m_procDeep(0), m_removedAurasCount(0), m_interruptMask(SpellAuraInterruptFlags::None), m_interruptMask2(SpellAuraInterruptFlags2::None), @@ -398,7 +399,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) @@ -454,6 +459,7 @@ Unit::~Unit() ASSERT(m_removedAuras.empty()); ASSERT(m_gameObj.empty()); ASSERT(m_dynObj.empty()); + ASSERT(!_gameClientMovingMe || _gameClientMovingMe->GetBasePlayer() == this); } void Unit::Update(uint32 p_time) @@ -5611,7 +5617,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(); @@ -6430,6 +6436,14 @@ bool Unit::isPossessing() const return false; } +Unit* Unit::GetCharmerOrSelf() const +{ + if (IsCharmed()) + return GetCharmer(); + else + return const_cast(this); +} + Unit* Unit::GetNextRandomRaidMemberOrPet(float radius) { Player* player = nullptr; @@ -9052,18 +9066,14 @@ 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]; - - if (!IsInCombat()) - if (Pet* pet = ToPlayer()->GetPet()) - pet->SetSpeedRate(mtype, m_speed_rate[mtype]); + ++m_forced_speed_changes[mtype]; } static MovementStatusElements const speedVal = MSEExtraFloat; Movement::ExtraMovementStatusElement extra(&speedVal); extra.Data.floatData = GetSpeed(mtype); - Movement::PacketSender(this, moveTypeToOpcode[mtype][0], moveTypeToOpcode[mtype][1], NULL_OPCODE, &extra).Send(); + Movement::PacketSender(IsMovedByClient() ? GetGameClientMovingMe()->GetBasePlayer() : this, moveTypeToOpcode[mtype][0], moveTypeToOpcode[mtype][1], NULL_OPCODE, &extra).Send(); } void Unit::FollowTarget(Unit* target) @@ -10602,20 +10612,6 @@ void CharmInfo::SetSpellAutocast(SpellInfo const* spellInfo, bool state) } } -Unit* Unit::GetUnitBeingMoved() const -{ - if (Player const* player = ToPlayer()) - return player->m_unitMovedByMe; - return nullptr; -} - -Player* Unit::GetPlayerBeingMoved() const -{ - if (Unit* mover = GetUnitBeingMoved()) - return mover->ToPlayer(); - return nullptr; -} - bool Unit::isFrozen() const { return HasAuraState(AURA_STATE_FROZEN); @@ -11999,11 +11995,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) @@ -12025,11 +12018,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) @@ -12679,27 +12669,16 @@ void Unit::SendMoveKnockBack(Player* player, float speedXY, float speedZ, float 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->m_unitMovedByMe != this) - player = nullptr; - } - } - - if (!player) + if (IsMovedByServer()) GetMotionMaster()->MoveKnockbackFrom(x, y, speedXY, speedZ); else { float vcos, vsin; GetSinCos(x, y, vsin, vcos); - SendMoveKnockBack(player, speedXY, -speedZ, vcos, vsin); + SendMoveKnockBack(GetGameClientMovingMe()->GetBasePlayer(), speedXY, -speedZ, vcos, vsin); - if (player->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) || player->HasAuraType(SPELL_AURA_FLY)) - player->SetCanFly(true, false); + if (HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) || HasAuraType(SPELL_AURA_FLY)) + SetCanFly(true, false); } } @@ -13733,8 +13712,9 @@ void Unit::SendTeleportPacket(Position const& pos) { // SMSG_MOVE_UPDATE_TELEPORT is sent to nearby players to signal the teleport // MSG_MOVE_TELEPORT is sent to self in order to trigger MSG_MOVE_TELEPORT_ACK and update the position server side - if (Player* playerMover = GetPlayerBeingMoved()) + if (IsMovedByClient()) { + Player* playerMover = GetGameClientMovingMe()->GetBasePlayer(); float x, y, z, o; pos.GetPosition(x, y, z, o); if (TransportBase* transportBase = GetDirectTransport()) @@ -14102,10 +14082,14 @@ bool Unit::SetDisableGravity(bool disable, bool packetOnly /*= false*/, bool /*u } } + WorldPacket data; if (disable) - Movement::PacketSender(this, SMSG_SPLINE_MOVE_GRAVITY_DISABLE, SMSG_MOVE_GRAVITY_DISABLE).Send(); + data.Initialize(IsMovedByClient() ? SMSG_MOVE_GRAVITY_DISABLE : SMSG_SPLINE_MOVE_GRAVITY_DISABLE); else - Movement::PacketSender(this, SMSG_SPLINE_MOVE_GRAVITY_ENABLE, SMSG_MOVE_GRAVITY_ENABLE).Send(); + data.Initialize(IsMovedByClient() ? SMSG_MOVE_GRAVITY_ENABLE : SMSG_SPLINE_MOVE_GRAVITY_ENABLE); + + WriteMovementInfo(data); + return true; } @@ -14976,3 +14960,11 @@ void Unit::ProcessItemCast(PendingSpellCastRequest const& castRequest, SpellCast player->CastItemUseSpell(item, targets, castRequest.CastRequest.CastID, castRequest.CastRequest.Misc); } } + +void Unit::SetGameClientMovingMe(GameClient* gameClientMovingMe) +{ + _gameClientMovingMe = gameClientMovingMe; + + Player* mover = _gameClientMovingMe->GetBasePlayer(); + mover->SendDirectMessage(WorldPackets::Movement::MoveSetActiveMover(mover->GetGUID()).Write()); +} diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 5650741bd1..34cc8b560a 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -75,6 +75,7 @@ class AuraApplication; class AuraEffect; class Creature; class DynamicObject; +class GameClient; class GameObject; class Guardian; class Item; @@ -1258,6 +1259,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; @@ -1266,14 +1268,19 @@ class TC_GAME_API Unit : public WorldObject CharmInfo* GetCharmInfo() { return m_charmInfo; } CharmInfo* InitCharmInfo(); void DeleteCharmInfo(); - // returns the unit that this player IS CONTROLLING - Unit* GetUnitBeingMoved() const; - // returns the player that this player IS CONTROLLING - Player* GetPlayerBeingMoved() const; - // returns the player that this unit is BEING CONTROLLED BY - Player* GetPlayerMovingMe() const { return m_playerMovingMe; } - // only set for direct client control (possess effects, vehicles and similar) - Player* 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 GetCharmerOrSelf()->ToPlayer();} + Unit* GetCharmedOrSelf() const { return IsCharming() ? GetCharmed() : const_cast(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); + SharedVisionList const& GetSharedVisionList() { return m_sharedVision; } void AddPlayerToVision(Player* player); void RemovePlayerFromVision(Player* player); @@ -1748,6 +1755,8 @@ class TC_GAME_API Unit : public WorldObject // Movement info Movement::MoveSpline * movespline; + uint8 m_forced_speed_changes[MAX_MOVE_TYPE]; + // Part of Evade mechanics time_t GetLastDamagedTime() const { return _lastDamagedTime; } void UpdateLastDamagedTime(SpellInfo const* spellProto); @@ -1838,6 +1847,7 @@ class TC_GAME_API Unit : public WorldObject 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 7fda030e36..9637bb0949 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -912,64 +912,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(); // unk - - MovementInfo movementInfo; - movementInfo.guid = guid; - ReadMovementInfo(recvData, &movementInfo); - recvData.read_skip(); // 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(); // unk - - MovementInfo movementInfo; - ReadMovementInfo(recvData, &movementInfo); -*/ -} - void WorldSession::HandleSetActionBarToggles(WorldPacket& recvData) { uint8 actionBar; @@ -1390,17 +1332,6 @@ void WorldSession::HandleCancelMountAuraOpcode(WorldPacket& /*recvData*/) _player->RemoveAurasByType(SPELL_AURA_MOUNTED); // Calls Dismount() } -void WorldSession::HandleMoveSetCanFlyAckOpcode(WorldPacket& recvData) -{ - // fly mode on/off - TC_LOG_DEBUG("network", "WORLD: CMSG_MOVE_SET_CAN_FLY_ACK"); - - MovementInfo movementInfo; - _player->ReadMovementInfo(recvData, &movementInfo); - - _player->m_unitMovedByMe->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 9d269954ce..4b05935092 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -33,6 +33,7 @@ #include "ObjectMgr.h" #include "Vehicle.h" #include "GameTime.h" +#include "GameClient.h" #include "SpellAuraEffects.h" #include "SpellMgr.h" #include @@ -235,15 +236,16 @@ void WorldSession::HandleSuspendTokenResponse(WorldPackets::Movement::SuspendTok void WorldSession::HandleMoveTeleportAck(WorldPackets::Movement::MoveTeleportAck& packet) { - TC_LOG_DEBUG("network", "CMSG_MOVE_TELEPORT_ACK: Guid: %s, Sequence: %u, Time: %u", packet.MoverGUID.ToString().c_str(), packet.AckIndex, packet.MoveTime); + if (!IsRightUnitBeingMoved(packet.MoverGUID)) + return; - Player* plMover = _player->m_unitMovedByMe->ToPlayer(); + GameClient* client = GetGameClient(); + Unit* mover = client->GetActivelyMovedUnit(); + Player* plMover = mover->ToPlayer(); if (!plMover || !plMover->IsBeingTeleportedNear()) return; - if (packet.MoverGUID != plMover->GetGUID()) - return; plMover->SetSemaphoreTeleportNear(false); @@ -294,7 +296,11 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket) void WorldSession::HandleMovementOpcode(uint16 opcode, MovementInfo& movementInfo) { - Unit* mover = _player->m_unitMovedByMe; + if (!IsRightUnitBeingMoved(movementInfo.guid)) + return; + + GameClient* client = GetGameClient(); + Unit* mover = client->GetActivelyMovedUnit(); // there must always be a mover if (!mover) @@ -459,12 +465,11 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recvData) Movement::ExtraMovementStatusElement extras(&speedElement); GetPlayer()->ReadMovementInfo(recvData, &movementInfo, &extras); - // now can skip not our packet - if (_player->GetGUID() != movementInfo.guid) - { - recvData.rfinish(); // prevent warnings spam + if (!IsRightUnitBeingMoved(movementInfo.guid)) return; - } + + GameClient* client = GetGameClient(); + Unit* mover = client->GetActivelyMovedUnit(); float newspeed = extras.Data.floatData; /*----------------*/ @@ -533,24 +538,25 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recvData) static MovementStatusElements const speedVal = MSEExtraFloat; Movement::ExtraMovementStatusElement extra(&speedVal); extra.Data.floatData = newspeed; - Movement::PacketSender(_player, NULL_OPCODE, NULL_OPCODE, moveUpdateOpcode, &extra).Send(); + + Movement::PacketSender(mover, NULL_OPCODE, NULL_OPCODE, moveUpdateOpcode, &extra).Send(); // 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[move_type] > 0) + if (mover->m_forced_speed_changes[move_type] > 0) { - --_player->m_forced_speed_changes[move_type]; - if (_player->m_forced_speed_changes[move_type] > 0) + --mover->m_forced_speed_changes[move_type]; + if (mover->m_forced_speed_changes[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], mover->GetName().c_str(), mover->GetSpeed(move_type), newspeed); + mover->SetSpeedRate(move_type, mover->GetSpeedRate(move_type)); } else // must be lesser - cheating { @@ -563,18 +569,41 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recvData) void WorldSession::HandleSetActiveMoverOpcode(WorldPackets::Movement::SetActiveMover& packet) { - if (GetPlayer()->IsInWorld()) - if (_player->m_unitMovedByMe->GetGUID() != packet.ActiveMover) - TC_LOG_DEBUG("network", "HandleSetActiveMoverOpcode: incorrect mover guid: mover is %s and should be %s" , packet.ActiveMover.ToString().c_str(), _player->m_unitMovedByMe->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(packet.ActiveMover)) + { + // @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(), packet.ActiveMover.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(), packet.ActiveMover.ToString().c_str()); + Unit* newActivelyMovedUnit = ObjectAccessor::GetUnit(*_player, packet.ActiveMover); + client->SetActivelyMovedUnit(newActivelyMovedUnit); } void WorldSession::HandleMoveNotActiveMover(WorldPacket &recvData) { TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_MOVE_NOT_ACTIVE_MOVER"); - MovementInfo mi; - GetPlayer()->ReadMovementInfo(recvData, &mi); - _player->m_movementInfo = mi; + MovementInfo movementInfo; + GetPlayer()->ReadMovementInfo(recvData, &movementInfo); + + GameClient* client = GetGameClient(); + + if (client->GetActivelyMovedUnit() == nullptr || client->GetActivelyMovedUnit()->GetGUID() != movementInfo.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(), movementInfo.guid.ToString().c_str()); + return; + } + + TC_LOG_DEBUG("entities.unit", "unset active mover OK for client of player %s. GUID %s.", _player->GetName().c_str(), movementInfo.guid.ToString().c_str()); + client->SetActivelyMovedUnit(nullptr); } void WorldSession::HandleMountSpecialAnimOpcode(WorldPacket& /*recvData*/) @@ -585,6 +614,19 @@ void WorldSession::HandleMountSpecialAnimOpcode(WorldPacket& /*recvData*/) GetPlayer()->SendMessageToSet(&data, false); } +void WorldSession::HandleSummonResponseOpcode(WorldPacket& recvData) +{ + if (!_player->IsAlive() || _player->IsInCombat()) + return; + + ObjectGuid summoner_guid; + bool agree; + recvData >> summoner_guid; + recvData >> agree; + + _player->SummonIfPossible(agree); +} + void WorldSession::HandleMoveKnockBackAck(WorldPacket& recvData) { TC_LOG_DEBUG("network", "CMSG_MOVE_KNOCK_BACK_ACK"); @@ -592,23 +634,18 @@ void WorldSession::HandleMoveKnockBackAck(WorldPacket& recvData) MovementInfo movementInfo; GetPlayer()->ReadMovementInfo(recvData, &movementInfo); - if (_player->m_unitMovedByMe->GetGUID() != movementInfo.guid) + if (!IsRightUnitBeingMoved(movementInfo.guid)) return; - _player->m_movementInfo = movementInfo; + GameClient* client = GetGameClient(); + Unit* mover = client->GetActivelyMovedUnit(); + + mover->m_movementInfo = movementInfo; + WorldPacket data(SMSG_MOVE_UPDATE_KNOCK_BACK, 66); - _player->WriteMovementInfo(data); - _player->SendMessageToSet(&data, false); -} - -void WorldSession::HandleGravityAckMessage(WorldPacket& recvData) -{ - MovementInfo movementInfo; - GetPlayer()->ReadMovementInfo(recvData, &movementInfo); - if (movementInfo.guid != _player->m_unitMovedByMe->GetGUID()) - TC_LOG_ERROR("network", "HandleGravityAckMessage: incorrect mover guid: mover is %s and should be %s.", movementInfo.guid.ToString().c_str(), _player->m_unitMovedByMe->GetGUID().ToString().c_str()); - + mover->WriteMovementInfo(data); + client->GetBasePlayer()->SendMessageToSet(&data, false); } void WorldSession::HandleMoveHoverAck(WorldPacket& recvData) @@ -625,17 +662,46 @@ void WorldSession::HandleMoveWaterWalkAck(WorldPacket& recvData) GetPlayer()->ReadMovementInfo(recvData, &movementInfo); } -void WorldSession::HandleSummonResponseOpcode(WorldPacket& recvData) +void WorldSession::HandleMoveRootAck(WorldPacket& recvData) { - if (!_player->IsAlive() || _player->IsInCombat()) - return; + TC_LOG_DEBUG("network", "CMSG_FORCE_MOVE_ROOT_ACK"); + MovementInfo movementInfo; + GetPlayer()->ReadMovementInfo(recvData, &movementInfo); +} - ObjectGuid summoner_guid; - bool agree; - recvData >> summoner_guid; - recvData >> agree; +void WorldSession::HandleFeatherFallAck(WorldPacket& recvData) +{ + TC_LOG_DEBUG("network", "CMSG_MOVE_FEATHER_FALL_ACK"); + MovementInfo movementInfo; + GetPlayer()->ReadMovementInfo(recvData, &movementInfo); +} - _player->SummonIfPossible(agree); +void WorldSession::HandleMoveUnRootAck(WorldPacket& recvData) +{ + TC_LOG_DEBUG("network", "CMSG_FORCE_MOVE_UNROOT_ACK"); + MovementInfo movementInfo; + GetPlayer()->ReadMovementInfo(recvData, &movementInfo); +} + +void WorldSession::HandleMoveSetCanTransitionBetweenSwinAndFlyAck(WorldPacket& recvData) +{ + TC_LOG_DEBUG("network", "CMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY_ACK"); + MovementInfo movementInfo; + GetPlayer()->ReadMovementInfo(recvData, &movementInfo); +} + +void WorldSession::HandleMoveGravityDisableAck(WorldPacket& recvData) +{ + TC_LOG_DEBUG("network", "CMSG_MOVE_GRAVITY_DISABLE_ACK"); + MovementInfo movementInfo; + GetPlayer()->ReadMovementInfo(recvData, &movementInfo); +} + +void WorldSession::HandleMoveGravityEnableAck(WorldPacket& recvData) +{ + TC_LOG_DEBUG("network", "CMSG_MOVE_GRAVITY_ENABLE_ACK"); + MovementInfo movementInfo; + GetPlayer()->ReadMovementInfo(recvData, &movementInfo); } void WorldSession::HandleSetCollisionHeightAck(WorldPacket& recvData) @@ -644,10 +710,14 @@ void WorldSession::HandleSetCollisionHeightAck(WorldPacket& recvData) static MovementStatusElements const heightElement = MSEExtraFloat; Movement::ExtraMovementStatusElement extras(&heightElement); GetPlayer()->ReadMovementInfo(recvData, &movementInfo, &extras); - - Movement::PacketSender(_player, NULL_OPCODE, NULL_OPCODE, SMSG_MOVE_UPDATE_COLLISION_HEIGHT, &extras).Send(); } +void WorldSession::HandleMoveSetCanFlyAckOpcode(WorldPacket& recvData) +{ + TC_LOG_DEBUG("network", "CMSG_MOVE_SET_CAN_FLY_ACK"); + MovementInfo movementInfo; + GetPlayer()->ReadMovementInfo(recvData, &movementInfo); +} void WorldSession::HandleMoveTimeSkippedOpcode(WorldPacket& recvData) { TC_LOG_DEBUG("network", "WORLD: Received CMSG_MOVE_TIME_SKIPPED"); @@ -674,7 +744,11 @@ void WorldSession::HandleMoveTimeSkippedOpcode(WorldPacket& recvData) recvData.ReadByteSeq(guid[0]); recvData.ReadByteSeq(guid[5]); - Unit* mover = GetPlayer()->m_unitMovedByMe; + if (!IsRightUnitBeingMoved(guid)) + return; + + GameClient* client = GetGameClient(); + Unit* mover = client->GetActivelyMovedUnit(); if (!mover) { diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index d6763b742a..dc1b0e0ebd 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -93,7 +93,7 @@ void WorldSession::HandleUseItemOpcode(WorldPackets::Spells::UseItem& packet) Player* user = _player; // ignore for remote control state - if (user->m_unitMovedByMe != user) + if (user->IsCharming()) return; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(packet.Cast.SpellID); @@ -111,7 +111,7 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) Player* player = GetPlayer(); // ignore for remote control state - if (player->m_unitMovedByMe != player) + if (player->IsCharming()) return; // additional check, client outputs message on its own @@ -232,8 +232,8 @@ void WorldSession::HandleGameObjectUseOpcode(WorldPacket& recvData) if (GameObject* obj = GetPlayer()->GetGameObjectIfCanInteractWith(guid)) { // ignore for remote control state - if (GetPlayer()->m_unitMovedByMe != GetPlayer()) - if (!(GetPlayer()->IsOnVehicle(GetPlayer()->m_unitMovedByMe) || GetPlayer()->IsMounted()) && !obj->GetGOInfo()->IsUsableMounted()) + if (GetPlayer()->IsCharming()) + if (!(GetPlayer()->IsOnVehicle(GetPlayer()->GetCharmed()) || GetPlayer()->IsMounted()) && !obj->GetGOInfo()->IsUsableMounted()) return; obj->Use(GetPlayer()); @@ -248,7 +248,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->m_unitMovedByMe != _player) + if (_player->IsCharming()) return; if (GameObject* go = GetPlayer()->GetGameObjectIfCanInteractWith(guid)) @@ -263,7 +263,7 @@ void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket) void WorldSession::HandleCastSpellOpcode(WorldPackets::Spells::CastSpell& cast) { // ignore for remote control state (for player case) - Unit* mover = _player->m_unitMovedByMe; + Unit* mover = _player->GetCharmedOrSelf(); if (mover != _player && mover->IsPlayer()) return; @@ -402,8 +402,8 @@ void WorldSession::HandleCancelChanneling(WorldPacket& recvData) return; // ignore for remote control state (for player case) - Unit* mover = _player->m_unitMovedByMe; - if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER) + Unit* mover = _player->GetCharmedOrSelf(); + if (_player->GetCharmed() && _player->GetCharmed()->GetTypeId() == TYPEID_PLAYER) return; mover->InterruptSpell(CURRENT_CHANNELED_SPELL); @@ -412,7 +412,7 @@ void WorldSession::HandleCancelChanneling(WorldPacket& recvData) void WorldSession::HandleTotemDestroyed(WorldPacket& recvPacket) { // ignore for remote control state - if (_player->m_unitMovedByMe != _player) + if (_player->IsCharming()) return; uint8 slotId; @@ -601,7 +601,7 @@ void WorldSession::HandleRequestCategoryCooldowns(WorldPacket& /*recvPacket*/) void WorldSession::HandleCancelQueuedSpellOpcode(WorldPackets::Spells::CancelQueuedSpell& /*cancelQueuedSpell*/) { - Unit* mover = _player->m_unitMovedByMe; + Unit* mover = _player->GetCharmedOrSelf(); if (mover != _player && mover->IsPlayer()) return; diff --git a/src/server/game/Handlers/TaxiHandler.cpp b/src/server/game/Handlers/TaxiHandler.cpp index 9dbccdd157..e8250f1475 100644 --- a/src/server/game/Handlers/TaxiHandler.cpp +++ b/src/server/game/Handlers/TaxiHandler.cpp @@ -210,6 +210,9 @@ void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& recvData) MovementInfo movementInfo; // used only for proper packet read _player->ReadMovementInfo(recvData, &movementInfo); + if (!IsRightUnitBeingMoved(movementInfo.guid)) + return; + // in taxi flight packet received in 2 case: // 1) end taxi path in far (multi-node) flight // 2) switch from one map to other in case multim-map taxi path diff --git a/src/server/game/Movement/MovementStructures.cpp b/src/server/game/Movement/MovementStructures.cpp index efb38aedd4..e14bd6e03b 100644 --- a/src/server/game/Movement/MovementStructures.cpp +++ b/src/server/game/Movement/MovementStructures.cpp @@ -5535,7 +5535,7 @@ bool Movement::PrintInvalidSequenceElement(MovementStatusElements const element, Movement::PacketSender::PacketSender(Unit* unit, uint16 serverControl, uint16 playerControl, uint16 broadcast /*= SMSG_PLAYER_MOVE*/, ExtraMovementStatusElement* extras /*= nullptr*/) : _extraElements(extras), _unit(unit) { - if (unit->GetTypeId() == TYPEID_PLAYER && unit->ToPlayer()->m_unitMovedByMe->GetTypeId() == TYPEID_PLAYER) + if (unit->IsPlayer() && unit->IsMovedByClient()) { _selfOpcode = playerControl; _broadcast = broadcast; @@ -5552,7 +5552,7 @@ void Movement::PacketSender::Send() const bool isPlayerMovement = false; if (Player* player = _unit->ToPlayer()) { - isPlayerMovement = player->m_unitMovedByMe->GetTypeId() == TYPEID_PLAYER; + isPlayerMovement = player->IsMovedByClient(); if (isPlayerMovement && _selfOpcode != NULL_OPCODE) { WorldPacket data(_selfOpcode); diff --git a/src/server/game/Server/GameClient.cpp b/src/server/game/Server/GameClient.cpp new file mode 100644 index 0000000000..9dde28b3bf --- /dev/null +++ b/src/server/game/Server/GameClient.cpp @@ -0,0 +1,69 @@ +/* + * 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 . + */ + +#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) +{ + if (GameClient* previousController = unit->GetGameClientMovingMe()) + previousController->RemoveAllowedMover(unit); + + _allowedMovers.insert(unit->GetGUID()); + unit->SetGameClientMovingMe(this); +} + +void GameClient::RemoveAllowedMover(Unit* unit) +{ + _allowedMovers.erase(unit->GetGUID()); + if (unit->GetGameClientMovingMe() == this) + { + unit->SetGameClientMovingMe(nullptr); + SetActivelyMovedUnit(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 0000000000..4baf5015dd --- /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 . + */ + +#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/Packets/MovementPackets.cpp b/src/server/game/Server/Packets/MovementPackets.cpp index 6be82e97b0..672e8370e4 100644 --- a/src/server/game/Server/Packets/MovementPackets.cpp +++ b/src/server/game/Server/Packets/MovementPackets.cpp @@ -539,3 +539,26 @@ ByteBuffer& WorldPackets::operator<<(ByteBuffer& data, Movement::MovementMonster return data; } + +WorldPacket const* WorldPackets::Movement::MoveSetActiveMover::Write() +{ + _worldPacket.WriteBit(MoverGUID[5]); + _worldPacket.WriteBit(MoverGUID[7]); + _worldPacket.WriteBit(MoverGUID[3]); + _worldPacket.WriteBit(MoverGUID[6]); + _worldPacket.WriteBit(MoverGUID[0]); + _worldPacket.WriteBit(MoverGUID[4]); + _worldPacket.WriteBit(MoverGUID[1]); + _worldPacket.WriteBit(MoverGUID[2]); + + _worldPacket.WriteByteSeq(MoverGUID[6]); + _worldPacket.WriteByteSeq(MoverGUID[2]); + _worldPacket.WriteByteSeq(MoverGUID[3]); + _worldPacket.WriteByteSeq(MoverGUID[0]); + _worldPacket.WriteByteSeq(MoverGUID[5]); + _worldPacket.WriteByteSeq(MoverGUID[7]); + _worldPacket.WriteByteSeq(MoverGUID[1]); + _worldPacket.WriteByteSeq(MoverGUID[4]); + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/MovementPackets.h b/src/server/game/Server/Packets/MovementPackets.h index f3931a654b..6df11d2ed7 100644 --- a/src/server/game/Server/Packets/MovementPackets.h +++ b/src/server/game/Server/Packets/MovementPackets.h @@ -248,6 +248,16 @@ namespace WorldPackets uint8 TransfertAbort = 0; uint8 Arg = 0; }; + + class MoveSetActiveMover final : public ServerPacket + { + public: + MoveSetActiveMover(ObjectGuid moverGuid) : ServerPacket(SMSG_FLIGHT_SPLINE_SYNC, 4 + 8), MoverGUID(moverGuid) { } + + WorldPacket const* Write() override; + + ObjectGuid MoverGUID; + }; } ByteBuffer& operator<<(ByteBuffer& data, Movement::MovementSpline const& movementSpline); diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index b185cc7c31..408b04846b 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -462,14 +462,14 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_MOVE_FORCE_SWIM_SPEED_CHANGE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleForceSpeedChangeAck ); DEFINE_HANDLER(CMSG_MOVE_FORCE_TURN_RATE_CHANGE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleForceSpeedChangeAck ); DEFINE_HANDLER(CMSG_MOVE_FORCE_WALK_SPEED_CHANGE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleForceSpeedChangeAck ); - DEFINE_HANDLER(CMSG_MOVE_GRAVITY_DISABLE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleGravityAckMessage ); - DEFINE_HANDLER(CMSG_MOVE_GRAVITY_ENABLE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleGravityAckMessage ); + DEFINE_HANDLER(CMSG_MOVE_GRAVITY_DISABLE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveGravityDisableAck ); + DEFINE_HANDLER(CMSG_MOVE_GRAVITY_ENABLE_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveGravityEnableAck ); DEFINE_HANDLER(CMSG_MOVE_HOVER_ACK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMoveHoverAck ); DEFINE_HANDLER(CMSG_MOVE_KNOCK_BACK_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveKnockBackAck ); DEFINE_HANDLER(CMSG_MOVE_NOT_ACTIVE_MOVER, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveNotActiveMover ); DEFINE_HANDLER(CMSG_MOVE_SET_CAN_FLY, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); DEFINE_HANDLER(CMSG_MOVE_SET_CAN_FLY_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveSetCanFlyAckOpcode ); - DEFINE_HANDLER(CMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes ); + DEFINE_HANDLER(CMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveSetCanTransitionBetweenSwinAndFlyAck); DEFINE_HANDLER(CMSG_MOVE_SET_COLLISION_HEIGHT_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleSetCollisionHeightAck ); DEFINE_HANDLER(CMSG_MOVE_SPLINE_DONE, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveSplineDoneOpcode ); DEFINE_HANDLER(CMSG_MOVE_TIME_SKIPPED, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleMoveTimeSkippedOpcode ); diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 17b93bd836..c35a9326b7 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -28,6 +28,7 @@ #include "Common.h" #include "Config.h" #include "DatabaseEnv.h" +#include "GameClient.h" #include "GameTime.h" #include "Group.h" #include "Guild.h" @@ -136,7 +137,8 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 battlenetAccoun m_currentBankerGUID(), _timeSyncClockDeltaQueue(6), _timeSyncClockDelta(0), - _pendingTimeSyncRequests() + _pendingTimeSyncRequests(), + _gameClient(new GameClient(this)) { memset(m_Tutorials, 0, sizeof(m_Tutorials)); @@ -174,6 +176,8 @@ WorldSession::~WorldSession() delete _warden; delete _RBACData; + delete _gameClient; + ///- empty incoming packet queue WorldPacket* packet = nullptr; while (_recvQueue.next(packet)) @@ -1589,3 +1593,27 @@ void WorldSession::SendTimeSync() _timeSyncTimer = 5000; _timeSyncNextCounter++; } + +bool WorldSession::IsRightUnitBeingMoved(ObjectGuid guid) +{ + GameClient* client = GetGameClient(); + + // the client is attempting to tamper movement data + // edit: this wouldn't happen in retail but it does in TC, even with a legitimate client. + if (!client->GetActivelyMovedUnit() || client->GetActivelyMovedUnit()->GetGUID() != guid) + { + TC_LOG_DEBUG("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 67df804fee..7da49eac8d 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -35,6 +35,7 @@ class BigNumber; class Creature; +class GameClient; class GameObject; class InstanceSave; class Item; @@ -471,6 +472,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(); @@ -670,11 +673,6 @@ class TC_GAME_API WorldSession // played time void HandlePlayedTime(WorldPacket& recvPacket); - // new - void HandleMoveUnRootAck(WorldPacket& recvPacket); - void HandleMoveRootAck(WorldPacket& recvPacket); - void HandleLookingForGroup(WorldPacket& recvPacket); - // cemetery/graveyard related void HandleReturnToGraveyard(WorldPacket& recvPacket); void HandleRequestCemeteryList(WorldPacket& recvPacket); @@ -686,11 +684,19 @@ class TC_GAME_API WorldSession void HandleRequestHonorStatsOpcode(WorldPackets::Inspect::RequestHonorStats& packet); void HandleRequestInspectRatedBgStatsOpcode(WorldPackets::Inspect::RequestInspectRatedBgStats& packet); - + void HandleForceSpeedChangeAck(WorldPacket& recvData); + void HandleMoveKnockBackAck(WorldPacket& recvPacket); + void HandleMoveTeleportAck(WorldPackets::Movement::MoveTeleportAck& packet); 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 HandleSetCollisionHeightAck(WorldPacket& recvData); void HandleMountSpecialAnimOpcode(WorldPacket& recvdata); @@ -701,13 +707,6 @@ class TC_GAME_API WorldSession // repair void HandleRepairItemOpcode(WorldPacket& recvPacket); - // Knockback - void HandleMoveKnockBackAck(WorldPacket& recvPacket); - void HandleGravityAckMessage(WorldPacket & recvData); - void HandleMoveTeleportAck(WorldPackets::Movement::MoveTeleportAck& packet); - void HandleForceSpeedChangeAck(WorldPacket& recvData); - void HandleSetCollisionHeightAck(WorldPacket& recvData); - void HandlePingOpcode(WorldPacket& recvPacket); void HandleRepopRequestOpcode(WorldPacket& recvPacket); void HandleAutostoreLootItemOpcode(WorldPacket& recvPacket); @@ -1088,7 +1087,6 @@ class TC_GAME_API WorldSession void HandleSetDungeonDifficultyOpcode(WorldPacket& recvData); void HandleSetRaidDifficultyOpcode(WorldPacket& recvData); void HandleChangePlayerDifficulty(WorldPacket& recvData); - void HandleMoveSetCanFlyAckOpcode(WorldPacket& recvData); void HandleSetTitleOpcode(WorldPacket& recvData); void HandleRealmSplitOpcode(WorldPacket& recvData); void HandleTimeSyncResp(WorldPacket& recvData); @@ -1322,6 +1320,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; @@ -1395,6 +1396,8 @@ class TC_GAME_API WorldSession ConnectToKey _instanceConnectKey; + 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 2bcef69af0..0fa895e826 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -27,6 +27,7 @@ #include "DisableMgr.h" #include "DynamicObject.h" #include "G3DPosition.hpp" +#include "GameClient.h" #include "GameObjectAI.h" #include "GameTime.h" #include "GridNotifiersImpl.h" @@ -3288,7 +3289,7 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered m_powerCost = m_CastItem ? 0 : m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask, this); // Set combo point requirement - if ((_triggeredCastFlags & TRIGGERED_IGNORE_COMBO_POINTS) || m_CastItem || !m_caster->m_playerMovingMe) + if ((_triggeredCastFlags & TRIGGERED_IGNORE_COMBO_POINTS) || m_CastItem || !m_caster->IsMovedByClient()) m_needComboPoints = false; MountResult mountResult = MountResult::Ok; @@ -3898,18 +3899,19 @@ void Spell::_handle_immediate_phase() void Spell::_handle_finish_phase() { - if (m_caster->m_playerMovingMe) + if (m_caster->IsMovedByClient()) { + Player* mover = m_caster->GetGameClientMovingMe()->GetBasePlayer(); // Take for real after all targets are processed if (m_needComboPoints) - m_caster->m_playerMovingMe->ClearComboPoints(); + mover->ClearComboPoints(); // Real add combo points from effects if (m_comboPointGain) - m_caster->m_playerMovingMe->GainSpellComboPoints(m_comboPointGain); + mover->GainSpellComboPoints(m_comboPointGain); - if (m_spellInfo->PowerType == POWER_HOLY_POWER && m_caster->m_playerMovingMe->getClass() == CLASS_PALADIN) - HandleHolyPower(m_caster->m_playerMovingMe); + if (m_spellInfo->PowerType == POWER_HOLY_POWER && mover->getClass() == CLASS_PALADIN) + HandleHolyPower(mover); } if (m_caster->m_extraAttacks && m_spellInfo->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS)) diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index d356514ea7..ace1df863e 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -28,6 +28,7 @@ #include "Formulas.h" #include "GameObject.h" #include "GameObjectAI.h" +#include "GameClient.h" #include "GossipDef.h" #include "GridNotifiers.h" #include "Group.h" @@ -3488,13 +3489,13 @@ void Spell::EffectAddComboPoints(SpellEffIndex /*effIndex*/) if (!unitTarget) return; - if (!m_caster->m_playerMovingMe) + if (!m_caster->IsMovedByClient()) return; if (damage <= 0) return; - m_caster->m_playerMovingMe->AddComboPoints(unitTarget, damage, this); + m_caster->GetGameClientMovingMe()->GetBasePlayer()->AddComboPoints(unitTarget, damage, this); } void Spell::EffectDuel(SpellEffIndex effIndex) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index bf41b91125..8539727dd2 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -20,6 +20,7 @@ #include "Corpse.h" #include "Creature.h" #include "DBCStores.h" +#include "GameClient.h" #include "InstanceScript.h" #include "Item.h" #include "ItemTemplate.h" @@ -528,8 +529,8 @@ int32 SpellEffectInfo::CalcValue(Unit const* caster, int32 const* bp, Unit const if (caster) { // bonus amount from combo points - if (_spellInfo->HasAttribute(SPELL_ATTR1_FINISHING_MOVE_DAMAGE) && caster->m_playerMovingMe && comboDamage) - if (uint8 comboPoints = caster->m_playerMovingMe->GetComboPoints()) + if (_spellInfo->HasAttribute(SPELL_ATTR1_FINISHING_MOVE_DAMAGE) && caster->IsMovedByClient() && comboDamage) + if (uint8 comboPoints = caster->GetGameClientMovingMe()->GetBasePlayer()->GetComboPoints()) value += comboDamage * comboPoints; value = caster->ApplyEffectModifiers(_spellInfo, _effIndex, value); @@ -3370,7 +3371,7 @@ int32 SpellInfo::CalcDuration(Unit* caster, Spell* spell) const // Increase duration based on combo points if (HasAttribute(SPELL_ATTR1_FINISHING_MOVE_DURATION)) { - if (uint8 comboPoints = (caster && caster->m_playerMovingMe) ? caster->m_playerMovingMe->GetComboPoints() : 0) + if (uint8 comboPoints = (caster && caster->IsMovedByClient()) ? caster->GetGameClientMovingMe()->GetBasePlayer()->GetComboPoints() : 0) { if (GetDuration() != GetMaxDuration() && GetDuration() != -1) duration += int32((GetMaxDuration() - GetDuration()) * comboPoints / 5); diff --git a/src/server/scripts/EasternKingdoms/GrimBatol/grim_batol.cpp b/src/server/scripts/EasternKingdoms/GrimBatol/grim_batol.cpp index 9561fe911d..dfa1544b0a 100644 --- a/src/server/scripts/EasternKingdoms/GrimBatol/grim_batol.cpp +++ b/src/server/scripts/EasternKingdoms/GrimBatol/grim_batol.cpp @@ -221,17 +221,13 @@ struct npc_grim_batol_battered_red_drake: public VehicleAI me->PlayOneShotAnimKitId(ANIM_KIT_ID_LIFTOFF); me->SetAnimationTier(AnimationTier::Fly); - player->SetMover(me); _playerGuid = player->GetGUID(); _events.ScheduleEvent(EVENT_SET_HOVERING, 2s); _events.ScheduleEvent(EVENT_PREPARE_BOMBARDMENT, 1s); } else - { - player->SetMover(player); me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); - } } void UpdateAI(uint32 diff) override