diff options
Diffstat (limited to 'src/game/MovementHandler.cpp')
-rw-r--r-- | src/game/MovementHandler.cpp | 108 |
1 files changed, 107 insertions, 1 deletions
diff --git a/src/game/MovementHandler.cpp b/src/game/MovementHandler.cpp index 822818273ed..e3cc490adb4 100644 --- a/src/game/MovementHandler.cpp +++ b/src/game/MovementHandler.cpp @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Common.h" #include "WorldPacket.h" #include "WorldSession.h" @@ -32,31 +33,39 @@ #include "WaypointMovementGenerator.h" #include "InstanceSaveMgr.h" #include "ObjectMgr.h" + void WorldSession::HandleMoveWorldportAckOpcode( WorldPacket & /*recv_data*/ ) { sLog.outDebug( "WORLD: got MSG_MOVE_WORLDPORT_ACK." ); HandleMoveWorldportAckOpcode(); } + void WorldSession::HandleMoveWorldportAckOpcode() { // ignore unexpected far teleports if(!GetPlayer()->IsBeingTeleportedFar()) return; + // get the teleport destination WorldLocation &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 = objmgr.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; + GetPlayer()->SetSemaphoreTeleportFar(false); + Map * oldMap = GetPlayer()->GetMap(); assert(oldMap); if(GetPlayer()->IsInWorld()) @@ -64,6 +73,7 @@ void WorldSession::HandleMoveWorldportAckOpcode() sLog.outCrash("Player is still in world when teleported out of map %u! to new map %u", oldMap->GetId(), loc.GetMapId()); oldMap->Remove(GetPlayer(), false); } + // relocate the player to the teleport destination Map * newMap = MapManager::Instance().CreateMap(loc.GetMapId(), GetPlayer(), 0); // the CanEnter checks are done in TeleporTo but conditions may change @@ -76,8 +86,10 @@ void WorldSession::HandleMoveWorldportAckOpcode() } else GetPlayer()->Relocate(&loc); + GetPlayer()->ResetMap(); GetPlayer()->SetMap(newMap); + GetPlayer()->SendInitialPacketsBeforeAddToMap(); if(!GetPlayer()->GetMap()->Add(GetPlayer())) { @@ -87,6 +99,7 @@ void WorldSession::HandleMoveWorldportAckOpcode() 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()) @@ -106,7 +119,9 @@ void WorldSession::HandleMoveWorldportAckOpcode() bg->AddPlayer(_player); } } + GetPlayer()->SendInitialPacketsAfterAddToMap(); + // flight fast teleport case if(GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE) { @@ -117,10 +132,12 @@ void WorldSession::HandleMoveWorldportAckOpcode() flight->Initialize(*GetPlayer()); return; } + // battleground state prepare, stop flight GetPlayer()->GetMotionMaster()->MovementExpired(); GetPlayer()->m_taxi.ClearTaxiDestinations(); } + // resurrect character at enter into instance where his corpse exist after add to map Corpse *corpse = GetPlayer()->GetCorpse(); if (corpse && corpse->GetType() != CORPSE_BONES && corpse->GetMapId() == GetPlayer()->GetMapId()) @@ -132,48 +149,65 @@ void WorldSession::HandleMoveWorldportAckOpcode() GetPlayer()->SaveToDB(); } } + if((mEntry->IsRaid() || (mEntry->IsNonRaidDungeon() && mEntry->SupportsHeroicMode() && GetPlayer()->IsHeroic())) && mInstance) { uint32 timeleft = sInstanceSaveManager.GetResetTimeFor(GetPlayer()->GetMapId()) - time(NULL); GetPlayer()->SendInstanceResetWarning(GetPlayer()->GetMapId(), GetPlayer()->GetDifficulty(), timeleft); } + // mount allow check if(!mEntry->IsMountAllowed()) _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.inHostileArea) GetPlayer()->CastSpell(GetPlayer(), SPELL_ID_HONORLESS_TARGET, true); + // resummon pet GetPlayer()->ResummonPetTemporaryUnSummonedIfAny(); + //lets process all delayed operations on successful teleport GetPlayer()->ProcessDelayedOperations(); } + void WorldSession::HandleMoveTeleportAck(WorldPacket& recv_data) { sLog.outDebug("MSG_MOVE_TELEPORT_ACK"); uint64 guid; uint32 flags, time; + recv_data >> guid; recv_data >> flags >> time; DEBUG_LOG("Guid " UI64FMTD, guid); DEBUG_LOG("Flags %u, time %u", flags, time/IN_MILISECONDS); + Unit *mover = _player->m_mover; Player *plMover = mover->GetTypeId() == TYPEID_PLAYER ? (Player*)mover : NULL; + if(!plMover || !plMover->IsBeingTeleportedNear()) return; + if(guid != plMover->GetGUID()) return; + plMover->SetSemaphoreTeleportNear(false); + uint32 old_zone = plMover->GetZoneId(); + WorldLocation const& dest = plMover->GetTeleportDest(); + plMover->SetPosition(dest, true); + uint32 newzone, newarea; plMover->GetZoneAndAreaId(newzone, newarea); plMover->UpdateZone(newzone, newarea); + // new zone if(old_zone != newzone) { @@ -181,27 +215,34 @@ void WorldSession::HandleMoveTeleportAck(WorldPacket& recv_data) if(plMover->pvpInfo.inHostileArea) plMover->CastSpell(plMover, SPELL_ID_HONORLESS_TARGET, true); } + // resummon pet GetPlayer()->ResummonPetTemporaryUnSummonedIfAny(); + //lets process all delayed operations on successful teleport GetPlayer()->ProcessDelayedOperations(); } + void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) { uint32 opcode = recv_data.GetOpcode(); //sLog.outDebug("WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(opcode), opcode, opcode); + Unit *mover = _player->m_mover; Player *plMover = mover->GetTypeId()==TYPEID_PLAYER ? (Player*)mover : NULL; + // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck if(plMover && plMover->IsBeingTeleported()) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } + /* extract packet */ MovementInfo movementInfo; ReadMovementInfo(recv_data, &movementInfo); /*----------------*/ + if(recv_data.size() != recv_data.rpos()) { sLog.outError("MovementHandler: player %s (guid %d, account %u) sent a packet (opcode %u) that is " SIZEFMTD " bytes larger than it should be. Kicked as cheater.", _player->GetName(), _player->GetGUIDLow(), _player->GetSession()->GetAccountId(), recv_data.GetOpcode(), recv_data.size() - recv_data.rpos()); @@ -209,11 +250,13 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } + if (!MaNGOS::IsValidMapCoord(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o)) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } + /* handle special cases */ if (movementInfo.flags & MOVEMENTFLAG_ONTRANSPORT) { @@ -224,12 +267,14 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } + if( !MaNGOS::IsValidMapCoord(movementInfo.x+movementInfo.t_x, movementInfo.y + movementInfo.t_y, movementInfo.z + movementInfo.t_z, movementInfo.o + movementInfo.t_o) ) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } + // if we boarded a transport, add us to it if (plMover && !plMover->m_transport) { @@ -244,6 +289,7 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) } } } + if(!mover->GetTransport() && !mover->GetVehicle()) movementInfo.flags &= ~MOVEMENTFLAG_ONTRANSPORT; } @@ -258,31 +304,39 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) movementInfo.t_time = 0; movementInfo.t_seat = -1; } + // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == MSG_MOVE_FALL_LAND && plMover && !plMover->isInFlight()) plMover->HandleFall(movementInfo); + if (plMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plMover->IsInWater()) { // now client not include swimming flag in case jumping under water plMover->SetInWater( !plMover->IsInWater() || plMover->GetBaseMap()->IsUnderWater(movementInfo.x, movementInfo.y, movementInfo.z) ); } + /*----------------------*/ + /* process position-change */ recv_data.put<uint32>(6, getMSTime()); // fix time, offset flags(4) + unk(2) WorldPacket data(recv_data.GetOpcode(), (mover->GetPackGUID().size()+recv_data.size())); data.append(mover->GetPackGUID()); // use mover guid data.append(recv_data.contents(), recv_data.size()); GetPlayer()->SendMessageToSet(&data, false); + mover->m_movementInfo = movementInfo; + if(mover->GetVehicle()) { mover->SetOrientation(movementInfo.o); return; } + if(plMover) // nothing is charmed, or player charmed { plMover->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); plMover->UpdateFallInformationIfNeed(movementInfo, recv_data.GetOpcode()); + if(movementInfo.z < -500.0f) { if(plMover->InBattleGround() @@ -308,6 +362,7 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) plMover->BuildPlayerRepop(); } } + // cancel the death timer here if started plMover->RepopAtGraveyard(); } @@ -316,6 +371,7 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) else // creature charmed { mover->GetMap()->CreatureRelocation((Creature*)mover, movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); + /*if(mover->canFly()) { bool flying = mover->IsFlying(); @@ -323,34 +379,46 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) mover->SetFlying(flying); }*/ } + //sLog.outString("Receive Movement Packet %s:", opcodeTable[recv_data.GetOpcode()]); //mover->OutMovementInfo(); } + void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data) { //sLog.outDebug("WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(recv_data.GetOpcode()), recv_data.GetOpcode(), recv_data.GetOpcode()); + /* extract packet */ uint64 guid; uint32 unk1; float newspeed; + recv_data >> guid; + // now can skip not our packet if(_player->GetGUID() != guid) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } + // continue parse packet + recv_data >> unk1; // counter or moveEvent + MovementInfo movementInfo; ReadMovementInfo(recv_data, &movementInfo); + recv_data >> 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" }; + uint16 opcode = recv_data.GetOpcode(); switch(opcode) { @@ -367,6 +435,7 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data) sLog.outError("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[force_move_type] > 0) @@ -375,6 +444,7 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data) if(_player->m_forced_speed_changes[force_move_type] > 0) return; } + if (!_player->GetTransport() && fabs(_player->GetSpeed(move_type) - newspeed) > 0.01f) { if(_player->GetSpeed(move_type) > newspeed) // must be greater - just correct @@ -391,13 +461,17 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data) } } } + void WorldSession::HandleSetActiveMoverOpcode(WorldPacket &recv_data) { sLog.outDebug("WORLD: Recvd CMSG_SET_ACTIVE_MOVER"); + uint64 guid; recv_data >> guid; + if(guid == GetPlayer()->m_mover->GetGUID()) return; + if(Unit *mover = ObjectAccessor::GetUnit(*GetPlayer(), guid)) { GetPlayer()->SetMover(mover); @@ -412,41 +486,52 @@ void WorldSession::HandleSetActiveMoverOpcode(WorldPacket &recv_data) else { sLog.outError("HandleSetActiveMoverOpcode: incorrect mover guid: mover is " UI64FMTD " and should be " UI64FMTD, guid, _player->m_mover->GetGUID()); - GetPlayer()->SetMover(GetPlayer()); + GetPlayer()->SetMover(GetPlayer()); } } + void WorldSession::HandleMoveNotActiveMover(WorldPacket &recv_data) { sLog.outDebug("WORLD: Recvd CMSG_MOVE_NOT_ACTIVE_MOVER"); + uint64 old_mover_guid; recv_data >> old_mover_guid; + /*if(_player->m_mover->GetGUID() == old_mover_guid) { sLog.outError("HandleMoveNotActiveMover: incorrect mover guid: mover is " I64FMT " and should be " I64FMT " instead of " I64FMT, _player->m_mover->GetGUID(), _player->GetGUID(), old_mover_guid); recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; }*/ + ReadMovementInfo(recv_data, &_player->m_mover->m_movementInfo); } + void WorldSession::HandleDismissControlledVehicle(WorldPacket &recv_data) { sLog.outDebug("WORLD: Recvd CMSG_DISMISS_CONTROLLED_VEHICLE"); recv_data.hexlike(); + uint64 vehicleGUID = _player->GetCharmGUID(); + if(!vehicleGUID) // something wrong here... { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } + ReadMovementInfo(recv_data, &_player->m_mover->m_movementInfo); _player->ExitVehicle(); } + void WorldSession::HandleChangeSeatsOnControlledVehicle(WorldPacket &recv_data) { sLog.outDebug("WORLD: Recvd CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE"); recv_data.hexlike(); + if(!GetPlayer()->GetVehicle()) return; + if(recv_data.GetOpcode() == CMSG_REQUEST_VEHICLE_PREV_SEAT) { GetPlayer()->ChangeSeat(-1, false); @@ -459,11 +544,14 @@ void WorldSession::HandleChangeSeatsOnControlledVehicle(WorldPacket &recv_data) } else if(recv_data.GetOpcode() == CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE) ReadMovementInfo(recv_data, &GetPlayer()->GetVehicleBase()->m_movementInfo); + uint64 guid; if(!recv_data.readPackGUID(guid)) return; + int8 seatId; recv_data >> seatId; + if(!guid) GetPlayer()->ChangeSeat(-1, seatId > 0); // prev/next else if(Unit *vehUnit = ObjectAccessor::GetUnit(*GetPlayer(), guid)) @@ -471,53 +559,71 @@ void WorldSession::HandleChangeSeatsOnControlledVehicle(WorldPacket &recv_data) if(vehicle->HasEmptySeat(seatId)) GetPlayer()->EnterVehicle(vehicle, seatId); } + void WorldSession::HandleRequestVehicleExit(WorldPacket &recv_data) { sLog.outDebug("WORLD: Recvd CMSG_REQUEST_VEHICLE_EXIT"); recv_data.hexlike(); GetPlayer()->ExitVehicle(); } + void WorldSession::HandleMountSpecialAnimOpcode(WorldPacket& /*recvdata*/) { //sLog.outDebug("WORLD: Recvd CMSG_MOUNTSPECIAL_ANIM"); + WorldPacket data(SMSG_MOUNTSPECIAL_ANIM, 8); data << uint64(GetPlayer()->GetGUID()); + GetPlayer()->SendMessageToSet(&data, false); } + void WorldSession::HandleMoveKnockBackAck( WorldPacket & recv_data ) { sLog.outDebug("CMSG_MOVE_KNOCK_BACK_ACK"); + recv_data.read_skip<uint64>(); // guid recv_data.read_skip<uint32>(); // unk + MovementInfo movementInfo; ReadMovementInfo(recv_data, &movementInfo); } + void WorldSession::HandleMoveHoverAck( WorldPacket& recv_data ) { sLog.outDebug("CMSG_MOVE_HOVER_ACK"); + recv_data.read_skip<uint64>(); // guid recv_data.read_skip<uint32>(); // unk + MovementInfo movementInfo; ReadMovementInfo(recv_data, &movementInfo); + recv_data.read_skip<uint32>(); // unk2 } + void WorldSession::HandleMoveWaterWalkAck(WorldPacket& recv_data) { sLog.outDebug("CMSG_MOVE_WATER_WALK_ACK"); + recv_data.read_skip<uint64>(); // guid recv_data.read_skip<uint32>(); // unk + MovementInfo movementInfo; ReadMovementInfo(recv_data, &movementInfo); + recv_data.read_skip<uint32>(); // unk2 } + void WorldSession::HandleSummonResponseOpcode(WorldPacket& recv_data) { if(!_player->isAlive() || _player->isInCombat() ) return; + uint64 summoner_guid; bool agree; recv_data >> summoner_guid; recv_data >> agree; + _player->SummonIfPossible(agree); } |