/* * 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 "WorldSession.h" #include "Battleground.h" #include "Common.h" #include "Corpse.h" #include "GameTime.h" #include "Garrison.h" #include "InstancePackets.h" #include "InstanceSaveMgr.h" #include "Log.h" #include "MapManager.h" #include "MovementPackets.h" #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" #include "SpellInfo.h" #include "MotionMaster.h" #include "MovementGenerator.h" #include "Transport.h" #include "Vehicle.h" #include "WaypointMovementGenerator.h" #include "SpellMgr.h" #define MOVEMENT_PACKET_TIME_DELAY 0 void WorldSession::HandleMoveWorldportAckOpcode(WorldPackets::Movement::WorldPortResponse& /*packet*/) { HandleMoveWorldportAck(); } void WorldSession::HandleMoveWorldportAck() { // ignore unexpected far teleports if (!GetPlayer()->IsBeingTeleportedFar()) return; bool seamlessTeleport = GetPlayer()->IsBeingTeleportedSeamlessly(); GetPlayer()->SetSemaphoreTeleportFar(false); // get the teleport destination WorldLocation const& loc = GetPlayer()->GetTeleportDest(); // possible errors in the coordinate validity check if (!MapManager::IsValidMapCoord(loc)) { LogoutPlayer(false); return; } // get the destination map entry, not the current one, this will fix homebind and reset greeting MapEntry const* mEntry = sMapStore.LookupEntry(loc.GetMapId()); InstanceTemplate const* mInstance = sObjectMgr->GetInstanceTemplate(loc.GetMapId()); // reset instance validity, except if going to an instance inside an instance if (GetPlayer()->m_InstanceValid == false && !mInstance) GetPlayer()->m_InstanceValid = true; Map* oldMap = GetPlayer()->GetMap(); Map* newMap = sMapMgr->CreateMap(loc.GetMapId(), GetPlayer()); if (GetPlayer()->IsInWorld()) { TC_LOG_ERROR("network", "%s %s is still in world when teleported from map %s (%u) to new map %s (%u)", GetPlayer()->GetGUID().ToString().c_str(), GetPlayer()->GetName().c_str(), oldMap->GetMapName(), oldMap->GetId(), newMap ? newMap->GetMapName() : "Unknown", loc.GetMapId()); oldMap->RemovePlayerFromMap(GetPlayer(), false); } // relocate the player to the teleport destination // the CannotEnter checks are done in TeleporTo but conditions may change // while the player is in transit, for example the map may get full if (!newMap || newMap->CannotEnter(GetPlayer())) { TC_LOG_ERROR("network", "Map %d (%s) could not be created for %s (%s), porting player to homebind", loc.GetMapId(), newMap ? newMap->GetMapName() : "Unknown", GetPlayer()->GetGUID().ToString().c_str(), GetPlayer()->GetName().c_str()); GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation()); return; } float z = loc.GetPositionZ(); if (GetPlayer()->HasUnitMovementFlag(MOVEMENTFLAG_HOVER)) z += GetPlayer()->m_unitData->HoverHeight; GetPlayer()->Relocate(loc.GetPositionX(), loc.GetPositionY(), z, loc.GetOrientation()); GetPlayer()->SetFallInformation(0, GetPlayer()->GetPositionZ()); GetPlayer()->ResetMap(); GetPlayer()->SetMap(newMap); WorldPackets::Movement::ResumeToken resumeToken; resumeToken.SequenceIndex = _player->m_movementCounter; resumeToken.Reason = seamlessTeleport ? 2 : 1; SendPacket(resumeToken.Write()); if (!seamlessTeleport) GetPlayer()->SendInitialPacketsBeforeAddToMap(); if (!GetPlayer()->GetMap()->AddPlayerToMap(GetPlayer(), !seamlessTeleport)) { TC_LOG_ERROR("network", "WORLD: failed to teleport player %s (%s) to map %d (%s) because of unknown reason!", GetPlayer()->GetName().c_str(), GetPlayer()->GetGUID().ToString().c_str(), loc.GetMapId(), newMap ? newMap->GetMapName() : "Unknown"); GetPlayer()->ResetMap(); GetPlayer()->SetMap(oldMap); GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation()); return; } // battleground state prepare (in case join to BG), at relogin/tele player not invited // only add to bg group and object, if the player was invited (else he entered through command) if (_player->InBattleground()) { // cleanup setting if outdated if (!mEntry->IsBattlegroundOrArena()) { // We're not in BG _player->SetBattlegroundId(0, BATTLEGROUND_TYPE_NONE); // reset destination bg team _player->SetBGTeam(0); } // join to bg case else if (Battleground* bg = _player->GetBattleground()) { if (_player->IsInvitedForBattlegroundInstance(_player->GetBattlegroundId())) bg->AddPlayer(_player); } } if (!seamlessTeleport) GetPlayer()->SendInitialPacketsAfterAddToMap(); else { GetPlayer()->UpdateVisibilityForPlayer(); if (Garrison* garrison = GetPlayer()->GetGarrison()) garrison->SendRemoteInfo(); } // flight fast teleport case if (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE) { if (!_player->InBattleground()) { if (!seamlessTeleport) { // short preparations to continue flight MovementGenerator* movementGenerator = GetPlayer()->GetMotionMaster()->top(); movementGenerator->Initialize(GetPlayer()); } return; } // battleground state prepare, stop flight GetPlayer()->GetMotionMaster()->MovementExpired(); GetPlayer()->CleanupAfterTaxiFlight(); } // resurrect character at enter into instance where his corpse exist after add to map if (mEntry->IsDungeon() && !GetPlayer()->IsAlive()) if (GetPlayer()->GetCorpseLocation().GetMapId() == mEntry->ID) { GetPlayer()->ResurrectPlayer(0.5f, false); GetPlayer()->SpawnCorpseBones(); } bool allowMount = !mEntry->IsDungeon() || mEntry->IsBattlegroundOrArena(); if (mInstance) { // check if this instance has a reset time and send it to player if so Difficulty diff = newMap->GetDifficultyID(); if (MapDifficultyEntry const* mapDiff = sDB2Manager.GetMapDifficultyData(mEntry->ID, diff)) { if (mapDiff->GetRaidDuration()) { if (time_t timeReset = sInstanceSaveMgr->GetResetTimeFor(mEntry->ID, diff)) { uint32 timeleft = uint32(timeReset - time(nullptr)); GetPlayer()->SendInstanceResetWarning(mEntry->ID, diff, timeleft, true); } } } // check if instance is valid if (!GetPlayer()->CheckInstanceValidity(false)) GetPlayer()->m_InstanceValid = false; // instance mounting is handled in InstanceTemplate allowMount = mInstance->AllowMount; } // mount allow check if (!allowMount) _player->RemoveAurasByType(SPELL_AURA_MOUNTED); // update zone immediately, otherwise leave channel will cause crash in mtmap uint32 newzone, newarea; GetPlayer()->GetZoneAndAreaId(newzone, newarea); GetPlayer()->UpdateZone(newzone, newarea); // honorless target if (GetPlayer()->pvpInfo.IsHostile) GetPlayer()->CastSpell(GetPlayer(), 2479, true); // in friendly area else if (GetPlayer()->IsPvP() && !GetPlayer()->HasPlayerFlag(PLAYER_FLAGS_IN_PVP)) GetPlayer()->UpdatePvP(false, false); // resummon pet GetPlayer()->ResummonPetTemporaryUnSummonedIfAny(); //lets process all delayed operations on successful teleport GetPlayer()->ProcessDelayedOperations(); } void WorldSession::HandleSuspendTokenResponse(WorldPackets::Movement::SuspendTokenResponse& /*suspendTokenResponse*/) { if (!_player->IsBeingTeleportedFar()) return; WorldLocation const& loc = GetPlayer()->GetTeleportDest(); if (sMapStore.AssertEntry(loc.GetMapId())->IsDungeon()) { WorldPackets::Instance::UpdateLastInstance updateLastInstance; updateLastInstance.MapID = loc.GetMapId(); SendPacket(updateLastInstance.Write()); } WorldPackets::Movement::NewWorld packet; packet.MapID = loc.GetMapId(); packet.Loc.Pos = loc; packet.Reason = !_player->IsBeingTeleportedSeamlessly() ? NEW_WORLD_NORMAL : NEW_WORLD_SEAMLESS; SendPacket(packet.Write()); if (_player->IsBeingTeleportedSeamlessly()) HandleMoveWorldportAck(); } 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); Player* plMover = _player->m_unitMovedByMe->ToPlayer(); if (!plMover || !plMover->IsBeingTeleportedNear()) return; if (packet.MoverGUID != plMover->GetGUID()) return; plMover->SetSemaphoreTeleportNear(false); uint32 old_zone = plMover->GetZoneId(); WorldLocation const& dest = plMover->GetTeleportDest(); plMover->UpdatePosition(dest, true); plMover->SetFallInformation(0, GetPlayer()->GetPositionZ()); uint32 newzone, newarea; plMover->GetZoneAndAreaId(newzone, newarea); plMover->UpdateZone(newzone, newarea); // new zone if (old_zone != newzone) { // honorless target if (plMover->pvpInfo.IsHostile) plMover->CastSpell(plMover, 2479, true); // in friendly area else if (plMover->IsPvP() && !plMover->HasPlayerFlag(PLAYER_FLAGS_IN_PVP)) plMover->UpdatePvP(false, false); } // resummon pet GetPlayer()->ResummonPetTemporaryUnSummonedIfAny(); //lets process all delayed operations on successful teleport GetPlayer()->ProcessDelayedOperations(); } void WorldSession::HandleMovementOpcodes(WorldPackets::Movement::ClientPlayerMovement& packet) { HandleMovementOpcode(packet.GetOpcode(), packet.Status); } void WorldSession::HandleMovementOpcode(OpcodeClient opcode, MovementInfo& movementInfo) { Unit* mover = _player->m_unitMovedByMe; ASSERT(mover != nullptr); // there must always be a mover Player* plrMover = mover->ToPlayer(); // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck if (plrMover && plrMover->IsBeingTeleported()) return; GetPlayer()->ValidateMovementInfo(&movementInfo); // prevent tampered movement data if (movementInfo.guid != mover->GetGUID()) { TC_LOG_ERROR("network", "HandleMovementOpcodes: guid error"); return; } if (!movementInfo.pos.IsPositionValid()) { TC_LOG_ERROR("network", "HandleMovementOpcodes: Invalid Position"); return; } // stop some emotes at player move if (plrMover && (plrMover->GetEmoteState() != 0)) plrMover->SetEmoteState(EMOTE_ONESHOT_NONE); /* handle special cases */ if (!movementInfo.transport.guid.IsEmpty()) { // We were teleported, skip packets that were broadcast before teleport if (movementInfo.pos.GetExactDist2d(mover) > SIZE_OF_GRIDS) return; // transports size limited // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) if (fabs(movementInfo.transport.pos.GetPositionX()) > 75.0f || fabs(movementInfo.transport.pos.GetPositionY()) > 75.0f || fabs(movementInfo.transport.pos.GetPositionZ()) > 75.0f) { return; } if (!Trinity::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.transport.pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.transport.pos.GetPositionY(), movementInfo.pos.GetPositionZ() + movementInfo.transport.pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.transport.pos.GetOrientation())) { return; } // if we boarded a transport, add us to it if (plrMover) { if (!plrMover->GetTransport()) { if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) transport->AddPassenger(plrMover); } else if (plrMover->GetTransport()->GetGUID() != movementInfo.transport.guid) { plrMover->GetTransport()->RemovePassenger(plrMover); if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) transport->AddPassenger(plrMover); else movementInfo.ResetTransport(); } } if (!mover->GetTransport() && !mover->GetVehicle()) { GameObject* go = mover->GetMap()->GetGameObject(movementInfo.transport.guid); if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT) movementInfo.transport.Reset(); } } else if (plrMover && plrMover->GetTransport()) // if we were on a transport, leave plrMover->m_transport->RemovePassenger(plrMover); // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == CMSG_MOVE_FALL_LAND && plrMover && !plrMover->IsInFlight()) plrMover->HandleFall(movementInfo); // interrupt parachutes upon falling or landing in water if (opcode == CMSG_MOVE_FALL_LAND || opcode == CMSG_MOVE_START_SWIM) mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_LANDING); // Parachutes if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater()) { // now client not include swimming flag in case jumping under water plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetMap()->IsUnderWater(plrMover->GetPhaseShift(), movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ())); } uint32 mstime = GameTime::GetGameTimeMS(); /*----------------------*/ if (m_clientTimeDelay == 0) m_clientTimeDelay = mstime - movementInfo.time; /* process position-change */ movementInfo.time = movementInfo.time + m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY; movementInfo.guid = mover->GetGUID(); mover->m_movementInfo = movementInfo; // Some vehicles allow the passenger to turn by himself if (Vehicle* vehicle = mover->GetVehicle()) { if (VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(mover)) { if (seat->Flags & VEHICLE_SEAT_FLAG_ALLOW_TURNING) { if (movementInfo.pos.GetOrientation() != mover->GetOrientation()) { mover->SetOrientation(movementInfo.pos.GetOrientation()); mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING); } } } return; } mover->UpdatePosition(movementInfo.pos); WorldPackets::Movement::MoveUpdate moveUpdate; moveUpdate.Status = &mover->m_movementInfo; mover->SendMessageToSet(moveUpdate.Write(), _player); if (plrMover) // nothing is charmed, or player charmed { if (plrMover->IsSitState() && (movementInfo.flags & (MOVEMENTFLAG_MASK_MOVING | MOVEMENTFLAG_MASK_TURNING))) plrMover->SetStandState(UNIT_STAND_STATE_STAND); plrMover->UpdateFallInformationIfNeed(movementInfo, opcode); if (movementInfo.pos.GetPositionZ() < plrMover->GetMap()->GetMinHeight(plrMover->GetPhaseShift(), movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY())) { if (!(plrMover->GetBattleground() && plrMover->GetBattleground()->HandlePlayerUnderMap(_player))) { // NOTE: this is actually called many times while falling // even after the player has been teleported away /// @todo discard movement packets after the player is rooted if (plrMover->IsAlive()) { plrMover->AddPlayerFlag(PLAYER_FLAGS_IS_OUT_OF_BOUNDS); plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // player can be alive if GM/etc // change the death state to CORPSE to prevent the death timer from // starting in the next player update if (plrMover->IsAlive()) plrMover->KillPlayer(); } } } else plrMover->RemovePlayerFlag(PLAYER_FLAGS_IS_OUT_OF_BOUNDS); if (opcode == CMSG_MOVE_JUMP) { plrMover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_JUMP, 605); // Mind Control plrMover->ProcSkillsAndAuras(nullptr, PROC_FLAG_JUMP, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); } } } void WorldSession::HandleForceSpeedChangeAck(WorldPackets::Movement::MovementSpeedAck& packet) { GetPlayer()->ValidateMovementInfo(&packet.Ack.Status); // now can skip not our packet if (_player->GetGUID() != packet.Ack.Status.guid) return; /*----------------*/ // 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; static char const* const move_type_name[MAX_MOVE_TYPE] = { "Walk", "Run", "RunBack", "Swim", "SwimBack", "TurnRate", "Flight", "FlightBack", "PitchRate" }; OpcodeClient opcode = packet.GetOpcode(); switch (opcode) { case CMSG_MOVE_FORCE_WALK_SPEED_CHANGE_ACK: move_type = MOVE_WALK; break; case CMSG_MOVE_FORCE_RUN_SPEED_CHANGE_ACK: move_type = MOVE_RUN; break; case CMSG_MOVE_FORCE_RUN_BACK_SPEED_CHANGE_ACK: move_type = MOVE_RUN_BACK; break; case CMSG_MOVE_FORCE_SWIM_SPEED_CHANGE_ACK: move_type = MOVE_SWIM; break; case CMSG_MOVE_FORCE_SWIM_BACK_SPEED_CHANGE_ACK: move_type = MOVE_SWIM_BACK; break; case CMSG_MOVE_FORCE_TURN_RATE_CHANGE_ACK: move_type = MOVE_TURN_RATE; break; case CMSG_MOVE_FORCE_FLIGHT_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT; break; case CMSG_MOVE_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT_BACK; break; case CMSG_MOVE_FORCE_PITCH_RATE_CHANGE_ACK: move_type = MOVE_PITCH_RATE; break; default: TC_LOG_ERROR("network", "WorldSession::HandleForceSpeedChangeAck: Unknown move type opcode: %u", opcode); 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 (_player->m_forced_speed_changes[move_type] > 0) { --_player->m_forced_speed_changes[move_type]; if (_player->m_forced_speed_changes[move_type] > 0) return; } if (!_player->GetTransport() && std::fabs(_player->GetSpeed(move_type) - packet.Speed) > 0.01f) { if (_player->GetSpeed(move_type) > packet.Speed) // 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), packet.Speed); _player->SetSpeedRate(move_type, _player->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), packet.Speed); _player->GetSession()->KickPlayer(); } } } 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()); } void WorldSession::HandleMoveKnockBackAck(WorldPackets::Movement::MoveKnockBackAck& movementAck) { GetPlayer()->ValidateMovementInfo(&movementAck.Ack.Status); if (_player->m_unitMovedByMe->GetGUID() != movementAck.Ack.Status.guid) return; _player->m_movementInfo = movementAck.Ack.Status; WorldPackets::Movement::MoveUpdateKnockBack updateKnockBack; updateKnockBack.Status = &_player->m_movementInfo; _player->SendMessageToSet(updateKnockBack.Write(), false); } void WorldSession::HandleMovementAckMessage(WorldPackets::Movement::MovementAckMessage& movementAck) { GetPlayer()->ValidateMovementInfo(&movementAck.Ack.Status); } void WorldSession::HandleSummonResponseOpcode(WorldPackets::Movement::SummonResponse& packet) { if (!_player->IsAlive() || _player->IsInCombat()) return; _player->SummonIfPossible(packet.Accept); } void WorldSession::HandleSetCollisionHeightAck(WorldPackets::Movement::MoveSetCollisionHeightAck& setCollisionHeightAck) { GetPlayer()->ValidateMovementInfo(&setCollisionHeightAck.Data.Status); } void WorldSession::HandleMoveApplyMovementForceAck(WorldPackets::Movement::MoveApplyMovementForceAck& moveApplyMovementForceAck) { Unit* mover = _player->m_unitMovedByMe; ASSERT(mover != nullptr); _player->ValidateMovementInfo(&moveApplyMovementForceAck.Ack.Status); // prevent tampered movement data if (moveApplyMovementForceAck.Ack.Status.guid != mover->GetGUID()) { TC_LOG_ERROR("network", "HandleMoveApplyMovementForceAck: guid error, expected %s, got %s", mover->GetGUID().ToString().c_str(), moveApplyMovementForceAck.Ack.Status.guid.ToString().c_str()); return; } moveApplyMovementForceAck.Ack.Status.time += m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY; WorldPackets::Movement::MoveUpdateApplyMovementForce updateApplyMovementForce; updateApplyMovementForce.Status = &moveApplyMovementForceAck.Ack.Status; updateApplyMovementForce.Force = &moveApplyMovementForceAck.Force; mover->SendMessageToSet(updateApplyMovementForce.Write(), false); } void WorldSession::HandleMoveRemoveMovementForceAck(WorldPackets::Movement::MoveRemoveMovementForceAck& moveRemoveMovementForceAck) { Unit* mover = _player->m_unitMovedByMe; ASSERT(mover != nullptr); _player->ValidateMovementInfo(&moveRemoveMovementForceAck.Ack.Status); // prevent tampered movement data if (moveRemoveMovementForceAck.Ack.Status.guid != mover->GetGUID()) { TC_LOG_ERROR("network", "HandleMoveRemoveMovementForceAck: guid error, expected %s, got %s", mover->GetGUID().ToString().c_str(), moveRemoveMovementForceAck.Ack.Status.guid.ToString().c_str()); return; } moveRemoveMovementForceAck.Ack.Status.time += m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY; WorldPackets::Movement::MoveUpdateRemoveMovementForce updateRemoveMovementForce; updateRemoveMovementForce.Status = &moveRemoveMovementForceAck.Ack.Status; updateRemoveMovementForce.TriggerGUID = moveRemoveMovementForceAck.ID; mover->SendMessageToSet(updateRemoveMovementForce.Write(), false); } void WorldSession::HandleMoveSetModMovementForceMagnitudeAck(WorldPackets::Movement::MovementSpeedAck& setModMovementForceMagnitudeAck) { Unit* mover = _player->m_unitMovedByMe; ASSERT(mover != nullptr); // there must always be a mover _player->ValidateMovementInfo(&setModMovementForceMagnitudeAck.Ack.Status); // prevent tampered movement data if (setModMovementForceMagnitudeAck.Ack.Status.guid != mover->GetGUID()) { TC_LOG_ERROR("network", "HandleSetModMovementForceMagnitudeAck: guid error, expected %s, got %s", mover->GetGUID().ToString().c_str(), setModMovementForceMagnitudeAck.Ack.Status.guid.ToString().c_str()); return; } // skip all except last if (_player->m_movementForceModMagnitudeChanges > 0) { --_player->m_movementForceModMagnitudeChanges; if (!_player->m_movementForceModMagnitudeChanges) { float expectedModMagnitude = 1.0f; if (MovementForces const* movementForces = mover->GetMovementForces()) expectedModMagnitude = movementForces->GetModMagnitude(); if (std::fabs(expectedModMagnitude - setModMovementForceMagnitudeAck.Speed) > 0.01f) { TC_LOG_DEBUG("misc", "Player %s from account id %u kicked for incorrect movement force magnitude (must be %f instead %f)", _player->GetName().c_str(), _player->GetSession()->GetAccountId(), expectedModMagnitude, setModMovementForceMagnitudeAck.Speed); _player->GetSession()->KickPlayer(); return; } } } setModMovementForceMagnitudeAck.Ack.Status.time += m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY; WorldPackets::Movement::MoveUpdateSpeed updateModMovementForceMagnitude(SMSG_MOVE_UPDATE_MOD_MOVEMENT_FORCE_MAGNITUDE); updateModMovementForceMagnitude.Status = &setModMovementForceMagnitudeAck.Ack.Status; updateModMovementForceMagnitude.Speed = setModMovementForceMagnitudeAck.Speed; mover->SendMessageToSet(updateModMovementForceMagnitude.Write(), false); } void WorldSession::HandleMoveTimeSkippedOpcode(WorldPackets::Movement::MoveTimeSkipped& /*moveTimeSkipped*/) { } void WorldSession::HandleMoveSplineDoneOpcode(WorldPackets::Movement::MoveSplineDone& moveSplineDone) { MovementInfo movementInfo = moveSplineDone.Status; _player->ValidateMovementInfo(&movementInfo); // 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 // we need process only (1) uint32 curDest = GetPlayer()->m_taxi.GetTaxiDestination(); if (curDest) { TaxiNodesEntry const* curDestNode = sTaxiNodesStore.LookupEntry(curDest); // far teleport case if (curDestNode && curDestNode->ContinentID != GetPlayer()->GetMapId()) { if (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE) { // short preparations to continue flight FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top()); flight->SetCurrentNodeAfterTeleport(); TaxiPathNodeEntry const* node = flight->GetPath()[flight->GetCurrentNode()]; flight->SkipCurrentNode(); GetPlayer()->TeleportTo(curDestNode->ContinentID, node->Loc.X, node->Loc.Y, node->Loc.Z, GetPlayer()->GetOrientation()); } } return; } // at this point only 1 node is expected (final destination) if (GetPlayer()->m_taxi.GetPath().size() != 1) return; GetPlayer()->CleanupAfterTaxiFlight(); GetPlayer()->SetFallInformation(0, GetPlayer()->GetPositionZ()); if (GetPlayer()->pvpInfo.IsHostile) GetPlayer()->CastSpell(GetPlayer(), 2479, true); }