aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorChaouki Dhib <chaodhib@gmail.com>2021-05-16 13:16:08 +0200
committerGitHub <noreply@github.com>2021-05-16 13:16:08 +0200
commit2d114ea560cd8716af2b0d12990fc3480ecaf2b7 (patch)
treeaaa711aa4d63e9b33d8e804d481425368ddcee18 /src/server
parentedcaac6c959e944dc6d6dc224666b832521412b4 (diff)
Core/Movement: Improve client control logic (#26348)
Diffstat (limited to 'src/server')
-rw-r--r--src/server/game/AI/CreatureAISelector.cpp2
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp2
-rw-r--r--src/server/game/Entities/Player/Player.cpp18
-rw-r--r--src/server/game/Entities/Player/Player.h4
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp71
-rw-r--r--src/server/game/Entities/Unit/Unit.h24
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp77
-rw-r--r--src/server/game/Handlers/MovementHandler.cpp311
-rw-r--r--src/server/game/Handlers/SpellHandler.cpp19
-rw-r--r--src/server/game/Handlers/TaxiHandler.cpp10
-rw-r--r--src/server/game/Server/GameClient.cpp63
-rw-r--r--src/server/game/Server/GameClient.h54
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp10
-rw-r--r--src/server/game/Server/WorldSession.cpp33
-rw-r--r--src/server/game/Server/WorldSession.h29
-rw-r--r--src/server/game/Spells/Spell.cpp2
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;
}
*/