aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Handlers/MovementHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/game/Handlers/MovementHandler.cpp')
-rw-r--r--src/server/game/Handlers/MovementHandler.cpp157
1 files changed, 101 insertions, 56 deletions
diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp
index 3705afd2f4a..ddfd2468a4d 100644
--- a/src/server/game/Handlers/MovementHandler.cpp
+++ b/src/server/game/Handlers/MovementHandler.cpp
@@ -25,6 +25,7 @@
#include "MapManager.h"
#include "MotionMaster.h"
#include "MovementGenerator.h"
+#include "MovementPacketSender.h"
#include "MoveSpline.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
@@ -450,78 +451,105 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recvData)
{
/* extract packet */
ObjectGuid guid;
- uint32 unk1;
- float newspeed;
-
recvData >> guid.ReadAsPacked();
- if (!IsRightUnitBeingMoved(guid))
+ GameClient* client = GetGameClient();
+
+ // ACK handlers should call GameClient::IsAllowedToMove instead of WorldSession::IsRightUnitBeingMoved
+ // because the ACK could be coming from a unit that is under the control of that client but is not the 'Active Mover' unit.
+ // Example: Get a speed buff on yourself, then mount a vehicle before the end of the buff. When the buff expires,
+ // a force message will be sent to the client regarding the player and the client is required to respond with an ACK.
+ // But the vehicle will be the active mover unit at that time.
+ if (!client->IsAllowedToMove(guid))
{
recvData.rfinish(); // prevent warnings spam
+ TC_LOG_DEBUG("entities.unit", "Ignoring ACK. Bad or outdated movement data by Player %s", _player->GetName().c_str());
return;
}
- GameClient* client = GetGameClient();
- Unit* mover = client->GetActivelyMovedUnit();
-
- // continue parse packet
+ Unit* mover = ObjectAccessor::GetUnit(*_player, guid);
- recvData >> unk1; // counter or moveEvent
+ UnitMoveType move_type;
+ switch (recvData.GetOpcode())
+ {
+ case CMSG_FORCE_WALK_SPEED_CHANGE_ACK: move_type = MOVE_WALK; break;
+ case CMSG_FORCE_RUN_SPEED_CHANGE_ACK: move_type = MOVE_RUN; break;
+ case CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK: move_type = MOVE_RUN_BACK; break;
+ case CMSG_FORCE_SWIM_SPEED_CHANGE_ACK: move_type = MOVE_SWIM; break;
+ case CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK: move_type = MOVE_SWIM_BACK; break;
+ case CMSG_FORCE_TURN_RATE_CHANGE_ACK: move_type = MOVE_TURN_RATE; break;
+ case CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT; break;
+ case CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT_BACK; break;
+ case CMSG_FORCE_PITCH_RATE_CHANGE_ACK: move_type = MOVE_PITCH_RATE; break;
+ default:
+ TC_LOG_ERROR("network", "WorldSession::HandleForceSpeedChangeAck: Unknown move type opcode: %s", GetOpcodeNameForLogging(static_cast<OpcodeClient>(recvData.GetOpcode())).c_str());
+ return;
+ }
+ uint32 movementCounter;
+ float speedReceived;
MovementInfo movementInfo;
movementInfo.guid = guid;
- ReadMovementInfo(recvData, &movementInfo);
- recvData >> newspeed;
- /*----------------*/
-
- // client ACK send one packet for mounted/run case and need skip all except last from its
- // in other cases anti-cheat check can be fail in false case
- UnitMoveType move_type;
- UnitMoveType force_move_type;
-
- static char const* move_type_name[MAX_MOVE_TYPE] = { "Walk", "Run", "RunBack", "Swim", "SwimBack", "TurnRate", "Flight", "FlightBack", "PitchRate" };
+ recvData >> movementCounter;
+ ReadMovementInfo(recvData, &movementInfo);
+ recvData >> speedReceived;
- switch (recvData.GetOpcode())
+ // verify that indeed the client is replying with the changes that were send to him
+ if (!mover->HasPendingMovementChange() || mover->PeakFirstPendingMovementChange().movementCounter > movementCounter)
{
- case CMSG_FORCE_WALK_SPEED_CHANGE_ACK: move_type = MOVE_WALK; force_move_type = MOVE_WALK; break;
- case CMSG_FORCE_RUN_SPEED_CHANGE_ACK: move_type = MOVE_RUN; force_move_type = MOVE_RUN; break;
- case CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK: move_type = MOVE_RUN_BACK; force_move_type = MOVE_RUN_BACK; break;
- case CMSG_FORCE_SWIM_SPEED_CHANGE_ACK: move_type = MOVE_SWIM; force_move_type = MOVE_SWIM; break;
- case CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK: move_type = MOVE_SWIM_BACK; force_move_type = MOVE_SWIM_BACK; break;
- case CMSG_FORCE_TURN_RATE_CHANGE_ACK: move_type = MOVE_TURN_RATE; force_move_type = MOVE_TURN_RATE; break;
- case CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT; force_move_type = MOVE_FLIGHT; break;
- case CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT_BACK; force_move_type = MOVE_FLIGHT_BACK; break;
- case CMSG_FORCE_PITCH_RATE_CHANGE_ACK: move_type = MOVE_PITCH_RATE; force_move_type = MOVE_PITCH_RATE; break;
+ TC_LOG_DEBUG("entities.unit", "Ignoring ACK. Bad or outdated movement data by Player %s", _player->GetName().c_str());
+ return;
+ }
+
+ PlayerMovementPendingChange pendingChange = mover->PopPendingMovementChange();
+ float speedSent = pendingChange.newValue;
+ MovementChangeType changeType = pendingChange.movementChangeType;
+ UnitMoveType moveTypeSent;
+ switch (changeType)
+ {
+ case MovementChangeType::SPEED_CHANGE_WALK: moveTypeSent = MOVE_WALK; break;
+ case MovementChangeType::SPEED_CHANGE_RUN: moveTypeSent = MOVE_RUN; break;
+ case MovementChangeType::SPEED_CHANGE_RUN_BACK: moveTypeSent = MOVE_RUN_BACK; break;
+ case MovementChangeType::SPEED_CHANGE_SWIM: moveTypeSent = MOVE_SWIM; break;
+ case MovementChangeType::SPEED_CHANGE_SWIM_BACK: moveTypeSent = MOVE_SWIM_BACK; break;
+ case MovementChangeType::RATE_CHANGE_TURN: moveTypeSent = MOVE_TURN_RATE; break;
+ case MovementChangeType::SPEED_CHANGE_FLIGHT_SPEED: moveTypeSent = MOVE_FLIGHT; break;
+ case MovementChangeType::SPEED_CHANGE_FLIGHT_BACK_SPEED: moveTypeSent = MOVE_FLIGHT_BACK; break;
+ case MovementChangeType::RATE_CHANGE_PITCH: moveTypeSent = MOVE_PITCH_RATE; break;
default:
- TC_LOG_ERROR("network", "WorldSession::HandleForceSpeedChangeAck: Unknown move type opcode: %u", recvData.GetOpcode());
+ TC_LOG_INFO("cheat", "WorldSession::HandleForceSpeedChangeAck: Player %s from account id %u kicked for incorrect data returned in an ack",
+ _player->GetName().c_str(), _player->GetSession()->GetAccountId());
+ _player->GetSession()->KickPlayer("incorrect data returned in an ack");
return;
}
- // 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 (mover->m_forced_speed_changes[force_move_type] > 0)
+ if (pendingChange.movementCounter != movementCounter || std::fabs(speedSent - speedReceived) > 0.01f || moveTypeSent!= move_type)
{
- --mover->m_forced_speed_changes[force_move_type];
- if (mover->m_forced_speed_changes[force_move_type] > 0)
- return;
+ TC_LOG_INFO("cheat", "WorldSession::HandleForceSpeedChangeAck: Player %s from account id %u kicked for incorrect data returned in an ack",
+ _player->GetName().c_str(), _player->GetSession()->GetAccountId());
+ _player->GetSession()->KickPlayer("incorrect data returned in an ack");
+ return;
}
- if (!mover->GetTransport() && std::fabs(mover->GetSpeed(move_type) - newspeed) > 0.01f)
+ /* the client data has been verified. let's do the actual change now */
+ int64 movementTime = (int64)movementInfo.time + _timeSyncClockDelta;
+ if (_timeSyncClockDelta == 0 || movementTime < 0 || movementTime > 0xFFFFFFFF)
{
- 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(), 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(), mover->GetSpeed(move_type), newspeed);
- _player->GetSession()->KickPlayer("WorldSession::HandleForceSpeedChangeAck Incorrect speed");
- }
+ TC_LOG_WARN("misc", "The computed movement time using clockDelta is erronous. Using fallback instead");
+ movementInfo.time = GameTime::GetGameTimeMS();
+ }
+ else
+ {
+ movementInfo.time = (uint32)movementTime;
}
+
+ mover->m_movementInfo = movementInfo;
+ mover->UpdatePosition(movementInfo.pos);
+
+ float newSpeedRate = speedSent / (mover->IsControlledByPlayer() ? playerBaseMoveSpeed[move_type] : baseMoveSpeed[move_type]);
+ mover->SetSpeedRateReal(move_type, newSpeedRate);
+ MovementPacketSender::SendSpeedChangeToObservers(mover, move_type, speedSent);
}
void WorldSession::HandleSetActiveMoverOpcode(WorldPacket &recvData)
@@ -561,8 +589,7 @@ void WorldSession::HandleMoveNotActiveMover(WorldPacket &recvData)
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());
+ TC_LOG_DEBUG("entities.unit", "unset active mover FAILED for client of player %s. GUID %s.", _player->GetName().c_str(), old_mover_guid.ToString().c_str());
return;
}
@@ -585,25 +612,43 @@ void WorldSession::HandleMoveKnockBackAck(WorldPacket& recvData)
ObjectGuid guid;
recvData >> guid.ReadAsPacked();
- if (!IsRightUnitBeingMoved(guid))
+ GameClient* client = GetGameClient();
+
+ // ACK handlers should call GameClient::IsAllowedToMove instead of WorldSession::IsRightUnitBeingMoved
+ // because the ACK could be coming from a unit that is under the control of that client but is not the 'Active Mover' unit.
+ // Example: Get a speed buff on yourself, then mount a vehicle before the end of the buff. When the buff expires,
+ // a force message will be sent to the client regarding the player and the client is required to respond with an ACK.
+ // But the vehicle will be the active mover unit at that time.
+ if (!client->IsAllowedToMove(guid))
{
recvData.rfinish(); // prevent warnings spam
return;
}
+ Unit* mover = ObjectAccessor::GetUnit(*_player, guid);
+
recvData.read_skip<uint32>(); // unk
MovementInfo movementInfo;
+ movementInfo.guid = guid;
ReadMovementInfo(recvData, &movementInfo);
- GameClient* client = GetGameClient();
- Unit* mover = client->GetActivelyMovedUnit();
+ int64 movementTime = (int64)movementInfo.time + _timeSyncClockDelta;
+ if (_timeSyncClockDelta == 0 || movementTime < 0 || movementTime > 0xFFFFFFFF)
+ {
+ TC_LOG_WARN("misc", "The computed movement time using clockDelta is erronous. Using fallback instead");
+ movementInfo.time = GameTime::GetGameTimeMS();
+ }
+ else
+ {
+ movementInfo.time = (uint32)movementTime;
+ }
mover->m_movementInfo = movementInfo;
+ mover->UpdatePosition(movementInfo.pos);
WorldPacket data(MSG_MOVE_KNOCK_BACK, 66);
- data << guid.WriteAsPacked();
- mover->BuildMovementPacket(&data);
+ WriteMovementInfo(&data, &movementInfo);
// knockback specific info
data << movementInfo.jump.sinAngle;