Files
CypherCore/Source/Game/Networking/Packets/MovementPackets.cs
T

1546 lines
52 KiB
C#

/*
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
*
* 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 3 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/>.
*/
using Framework.Constants;
using Framework.Dynamic;
using Game.Entities;
using Game.Movement;
using System;
using System.Collections.Generic;
using System.Numerics;
namespace Game.Networking.Packets
{
public static class MovementExtensions
{
public static void ReadTransportInfo(WorldPacket data, ref MovementInfo.TransportInfo transportInfo)
{
transportInfo.guid = data.ReadPackedGuid(); // Transport Guid
transportInfo.pos.posX = data.ReadFloat();
transportInfo.pos.posY = data.ReadFloat();
transportInfo.pos.posZ = data.ReadFloat();
transportInfo.pos.Orientation = data.ReadFloat();
transportInfo.seat = data.ReadInt8(); // VehicleSeatIndex
transportInfo.time = data.ReadUInt32(); // MoveTime
bool hasPrevTime = data.HasBit();
bool hasVehicleId = data.HasBit();
if (hasPrevTime)
transportInfo.prevTime = data.ReadUInt32(); // PrevMoveTime
if (hasVehicleId)
transportInfo.vehicleId = data.ReadUInt32(); // VehicleRecID
}
public static void WriteTransportInfo(WorldPacket data, MovementInfo.TransportInfo transportInfo)
{
bool hasPrevTime = transportInfo.prevTime != 0;
bool hasVehicleId = transportInfo.vehicleId != 0;
data.WritePackedGuid(transportInfo.guid); // Transport Guid
data.WriteFloat(transportInfo.pos.GetPositionX());
data.WriteFloat(transportInfo.pos.GetPositionY());
data.WriteFloat(transportInfo.pos.GetPositionZ());
data.WriteFloat(transportInfo.pos.GetOrientation());
data.WriteInt8(transportInfo.seat); // VehicleSeatIndex
data.WriteUInt32(transportInfo.time); // MoveTime
data.WriteBit(hasPrevTime);
data.WriteBit(hasVehicleId);
data.FlushBits();
if (hasPrevTime)
data.WriteUInt32(transportInfo.prevTime); // PrevMoveTime
if (hasVehicleId)
data.WriteUInt32(transportInfo.vehicleId); // VehicleRecID
}
public static MovementInfo ReadMovementInfo(WorldPacket data)
{
var movementInfo = new MovementInfo();
movementInfo.Guid = data.ReadPackedGuid();
movementInfo.SetMovementFlags((MovementFlag)data.ReadUInt32());
movementInfo.SetMovementFlags2((MovementFlag2)data.ReadUInt32());
movementInfo.SetExtraMovementFlags2((MovementFlags3)data.ReadUInt32());
movementInfo.Time = data.ReadUInt32();
float x = data.ReadFloat();
float y = data.ReadFloat();
float z = data.ReadFloat();
float o = data.ReadFloat();
movementInfo.Pos.Relocate(x, y, z, o);
movementInfo.Pitch = data.ReadFloat();
movementInfo.stepUpStartElevation = data.ReadFloat();
uint removeMovementForcesCount = data.ReadUInt32();
uint moveIndex = data.ReadUInt32();
for (uint i = 0; i < removeMovementForcesCount; ++i)
{
data.ReadPackedGuid();
}
// ResetBitReader
bool hasTransport = data.HasBit();
bool hasFall = data.HasBit();
bool hasSpline = data.HasBit(); // todo 6.x read this infos
data.ReadBit(); // HeightChangeFailed
data.ReadBit(); // RemoteTimeValid
bool hasInertia = data.HasBit();
bool hasAdvFlying = data.HasBit();
if (hasTransport)
ReadTransportInfo(data, ref movementInfo.transport);
if (hasInertia)
{
MovementInfo.Inertia inertia = new();
inertia.id = data.ReadInt32();
inertia.force = data.ReadPosition();
inertia.lifetime = data.ReadUInt32();
movementInfo.inertia = inertia;
}
if (hasAdvFlying)
{
MovementInfo.AdvFlying advFlying = new();
advFlying.forwardVelocity = data.ReadFloat();
advFlying.upVelocity = data.ReadFloat();
movementInfo.advFlying = advFlying;
}
if (hasFall)
{
movementInfo.jump.fallTime = data.ReadUInt32();
movementInfo.jump.zspeed = data.ReadFloat();
// ResetBitReader
bool hasFallDirection = data.HasBit();
if (hasFallDirection)
{
movementInfo.jump.sinAngle = data.ReadFloat();
movementInfo.jump.cosAngle = data.ReadFloat();
movementInfo.jump.xyspeed = data.ReadFloat();
}
}
return movementInfo;
}
public static void WriteMovementInfo(WorldPacket data, MovementInfo movementInfo)
{
bool hasTransportData = !movementInfo.transport.guid.IsEmpty();
bool hasFallDirection = movementInfo.HasMovementFlag(MovementFlag.Falling | MovementFlag.FallingFar);
bool hasFallData = hasFallDirection || movementInfo.jump.fallTime != 0;
bool hasSpline = false; // todo 6.x send this infos
bool hasInertia = movementInfo.inertia.HasValue;
bool hasAdvFlying = movementInfo.advFlying.HasValue;
data.WritePackedGuid(movementInfo.Guid);
data.WriteUInt32((uint)movementInfo.GetMovementFlags());
data.WriteUInt32((uint)movementInfo.GetMovementFlags2());
data.WriteUInt32((uint)movementInfo.GetExtraMovementFlags2());
data.WriteUInt32(movementInfo.Time);
data.WriteFloat(movementInfo.Pos.GetPositionX());
data.WriteFloat(movementInfo.Pos.GetPositionY());
data.WriteFloat(movementInfo.Pos.GetPositionZ());
data.WriteFloat(movementInfo.Pos.GetOrientation());
data.WriteFloat(movementInfo.Pitch);
data.WriteFloat(movementInfo.stepUpStartElevation);
uint removeMovementForcesCount = 0;
data.WriteUInt32(removeMovementForcesCount);
uint moveIndex = 0;
data.WriteUInt32(moveIndex);
/*for (public uint i = 0; i < removeMovementForcesCount; ++i)
{
_worldPacket << ObjectGuid;
}*/
data.WriteBit(hasTransportData);
data.WriteBit(hasFallData);
data.WriteBit(hasSpline);
data.WriteBit(false); // HeightChangeFailed
data.WriteBit(false); // RemoteTimeValid
data.WriteBit(hasInertia);
data.WriteBit(hasAdvFlying);
data.FlushBits();
if (hasTransportData)
WriteTransportInfo(data, movementInfo.transport);
if (hasInertia)
{
data.WriteInt32(movementInfo.inertia.Value.id);
data.WriteXYZ(movementInfo.inertia.Value.force);
data.WriteUInt32(movementInfo.inertia.Value.lifetime);
}
if (hasAdvFlying)
{
data.WriteFloat(movementInfo.advFlying.Value.forwardVelocity);
data.WriteFloat(movementInfo.advFlying.Value.upVelocity);
}
if (hasFallData)
{
data.WriteUInt32(movementInfo.jump.fallTime);
data.WriteFloat(movementInfo.jump.zspeed);
data.WriteBit(hasFallDirection);
data.FlushBits();
if (hasFallDirection)
{
data.WriteFloat(movementInfo.jump.sinAngle);
data.WriteFloat(movementInfo.jump.cosAngle);
data.WriteFloat(movementInfo.jump.xyspeed);
}
}
}
public static void WriteCreateObjectSplineDataBlock(MoveSpline moveSpline, WorldPacket data)
{
data.WriteUInt32(moveSpline.GetId()); // ID
if (!moveSpline.IsCyclic()) // Destination
data.WriteVector3(moveSpline.FinalDestination());
else
data.WriteVector3(Vector3.Zero);
bool hasSplineMove = data.WriteBit(!moveSpline.Finalized() && !moveSpline.splineIsFacingOnly);
data.FlushBits();
if (hasSplineMove)
{
data.WriteUInt32((uint)moveSpline.splineflags.Flags); // SplineFlags
data.WriteInt32(moveSpline.TimePassed()); // Elapsed
data.WriteInt32(moveSpline.Duration()); // Duration
data.WriteFloat(1.0f); // DurationModifier
data.WriteFloat(1.0f); // NextDurationModifier
data.WriteBits((byte)moveSpline.facing.type, 2); // Face
bool hasFadeObjectTime = data.WriteBit(moveSpline.splineflags.HasFlag(SplineFlag.FadeObject) && moveSpline.effect_start_time < moveSpline.Duration());
data.WriteBits(moveSpline.GetPath().Length, 16);
data.WriteBit(false); // HasSplineFilter
data.WriteBit(moveSpline.spell_effect_extra != null); // HasSpellEffectExtraData
bool hasJumpExtraData = data.WriteBit(moveSpline.splineflags.HasFlag(SplineFlag.Parabolic) && (moveSpline.spell_effect_extra == null || moveSpline.effect_start_time != 0));
data.WriteBit(moveSpline.anim_tier != null); // HasAnimTierTransition
data.WriteBit(false); // HasUnknown901
data.FlushBits();
//if (HasSplineFilterKey)
//{
// data << uint32(FilterKeysCount);
// for (var i = 0; i < FilterKeysCount; ++i)
// {
// data << float(In);
// data << float(Out);
// }
// data.WriteBits(FilterFlags, 2);
// data.FlushBits();
//}
switch (moveSpline.facing.type)
{
case MonsterMoveType.FacingSpot:
data.WriteVector3(moveSpline.facing.f); // FaceSpot
break;
case MonsterMoveType.FacingTarget:
data.WritePackedGuid(moveSpline.facing.target); // FaceGUID
break;
case MonsterMoveType.FacingAngle:
data.WriteFloat(moveSpline.facing.angle); // FaceDirection
break;
}
if (hasFadeObjectTime)
data.WriteInt32(moveSpline.effect_start_time); // FadeObjectTime
foreach (var vec in moveSpline.GetPath())
data.WriteVector3(vec);
if (moveSpline.spell_effect_extra != null)
{
data.WritePackedGuid(moveSpline.spell_effect_extra.Target);
data.WriteUInt32(moveSpline.spell_effect_extra.SpellVisualId);
data.WriteUInt32(moveSpline.spell_effect_extra.ProgressCurveId);
data.WriteUInt32(moveSpline.spell_effect_extra.ParabolicCurveId);
}
if (hasJumpExtraData)
{
data.WriteFloat(moveSpline.vertical_acceleration);
data.WriteInt32(moveSpline.effect_start_time);
data.WriteUInt32(0); // Duration (override)
}
if (moveSpline.anim_tier != null)
{
data.WriteUInt32(moveSpline.anim_tier.TierTransitionId);
data.WriteInt32(moveSpline.effect_start_time);
data.WriteUInt32(0);
data.WriteUInt8(moveSpline.anim_tier.AnimTier);
}
//if (HasUnknown901)
//{
// for (WorldPackets::Movement::MonsterSplineUnknown901::Inner const& unkInner : unk.Data) size = 16
// {
// data << int32(unkInner.Unknown_1);
// data << int32(unkInner.Unknown_2);
// data << int32(unkInner.Unknown_3);
// data << uint32(unkInner.Unknown_4);
// }
//}
}
}
public static void WriteCreateObjectAreaTriggerSpline(Spline<int> spline, WorldPacket data)
{
data.WriteBits(spline.GetPoints().Length, 16);
foreach (var point in spline.GetPoints())
data.WriteVector3(point);
}
public static void WriteMovementForceWithDirection(MovementForce movementForce, WorldPacket data, Position objectPosition = null)
{
data.WritePackedGuid(movementForce.ID);
data.WriteVector3(movementForce.Origin);
if (movementForce.Type == MovementForceType.Gravity && objectPosition != null) // gravity
{
Vector3 direction = Vector3.Zero;
if (movementForce.Magnitude != 0.0f)
{
Position tmp = new(movementForce.Origin.X - objectPosition.GetPositionX(),
movementForce.Origin.Y - objectPosition.GetPositionY(),
movementForce.Origin.Z - objectPosition.GetPositionZ());
float lengthSquared = tmp.GetExactDistSq(0.0f, 0.0f, 0.0f);
if (lengthSquared > 0.0f)
{
float mult = 1.0f / (float)Math.Sqrt(lengthSquared) * movementForce.Magnitude;
tmp.posX *= mult;
tmp.posY *= mult;
tmp.posZ *= mult;
float minLengthSquared = (tmp.GetPositionX() * tmp.GetPositionX() * 0.04f) +
(tmp.GetPositionY() * tmp.GetPositionY() * 0.04f) +
(tmp.GetPositionZ() * tmp.GetPositionZ() * 0.04f);
if (lengthSquared > minLengthSquared)
direction = new Vector3(tmp.posX, tmp.posY, tmp.posZ);
}
}
data.WriteVector3(direction);
}
else
data.WriteVector3(movementForce.Direction);
data.WriteUInt32(movementForce.TransportID);
data.WriteFloat(movementForce.Magnitude);
data.WriteInt32(movementForce.Unused910);
data.WriteBits((byte)movementForce.Type, 2);
data.FlushBits();
}
}
public class ClientPlayerMovement : ClientPacket
{
public ClientPlayerMovement(WorldPacket packet) : base(packet) { }
public override void Read()
{
Status = MovementExtensions.ReadMovementInfo(_worldPacket);
}
public MovementInfo Status;
}
public class MoveUpdate : ServerPacket
{
public MoveUpdate() : base(ServerOpcodes.MoveUpdate, ConnectionType.Instance) { }
public override void Write()
{
MovementExtensions.WriteMovementInfo(_worldPacket, Status);
}
public MovementInfo Status;
}
public class MonsterMove : ServerPacket
{
public MonsterMove() : base(ServerOpcodes.OnMonsterMove, ConnectionType.Instance)
{
SplineData = new MovementMonsterSpline();
}
public void InitializeSplineData(MoveSpline moveSpline)
{
SplineData.Id = moveSpline.GetId();
MovementSpline movementSpline = SplineData.Move;
MoveSplineFlag splineFlags = moveSpline.splineflags;
splineFlags.SetUnsetFlag(SplineFlag.Cyclic, moveSpline.IsCyclic());
movementSpline.Flags = (uint)(splineFlags.Flags & ~SplineFlag.MaskNoMonsterMove);
movementSpline.Face = moveSpline.facing.type;
movementSpline.FaceDirection = moveSpline.facing.angle;
movementSpline.FaceGUID = moveSpline.facing.target;
movementSpline.FaceSpot = moveSpline.facing.f;
if (splineFlags.HasFlag(SplineFlag.Animation))
{
MonsterSplineAnimTierTransition animTierTransition = new();
animTierTransition.TierTransitionID = (int)moveSpline.anim_tier.TierTransitionId;
animTierTransition.StartTime = (uint)moveSpline.effect_start_time;
animTierTransition.AnimTier = moveSpline.anim_tier.AnimTier;
movementSpline.AnimTierTransition = animTierTransition;
}
movementSpline.MoveTime = (uint)moveSpline.Duration();
if (splineFlags.HasFlag(SplineFlag.Parabolic) && (moveSpline.spell_effect_extra == null || moveSpline.effect_start_time != 0))
{
MonsterSplineJumpExtraData jumpExtraData = new();
jumpExtraData.JumpGravity = moveSpline.vertical_acceleration;
jumpExtraData.StartTime = (uint)moveSpline.effect_start_time;
movementSpline.JumpExtraData = jumpExtraData;
}
if (splineFlags.HasFlag(SplineFlag.FadeObject))
movementSpline.FadeObjectTime = (uint)moveSpline.effect_start_time;
if (moveSpline.spell_effect_extra != null)
{
MonsterSplineSpellEffectExtraData spellEffectExtraData = new();
spellEffectExtraData.TargetGuid = moveSpline.spell_effect_extra.Target;
spellEffectExtraData.SpellVisualID = moveSpline.spell_effect_extra.SpellVisualId;
spellEffectExtraData.ProgressCurveID = moveSpline.spell_effect_extra.ProgressCurveId;
spellEffectExtraData.ParabolicCurveID = moveSpline.spell_effect_extra.ParabolicCurveId;
spellEffectExtraData.JumpGravity = moveSpline.vertical_acceleration;
movementSpline.SpellEffectExtraData = spellEffectExtraData;
}
var spline = moveSpline.spline;
Vector3[] array = spline.GetPoints();
if (splineFlags.HasFlag(SplineFlag.UncompressedPath))
{
if (!splineFlags.HasFlag(SplineFlag.Cyclic))
{
int count = spline.GetPointCount() - 3;
for (uint i = 0; i < count; ++i)
movementSpline.Points.Add(array[i + 2]);
}
else
{
int count = spline.GetPointCount() - 3;
movementSpline.Points.Add(array[1]);
for (uint i = 0; i < count; ++i)
movementSpline.Points.Add(array[i + 1]);
}
}
else
{
int lastIdx = spline.GetPointCount() - 3;
Span<Vector3> realPath = new Span<Vector3>(spline.GetPoints()).Slice(1);
movementSpline.Points.Add(realPath[lastIdx]);
if (lastIdx > 1)
{
Vector3 middle = (realPath[0] + realPath[lastIdx]) / 2.0f;
// first and last points already appended
for (int i = 1; i < lastIdx; ++i)
movementSpline.PackedDeltas.Add(middle - realPath[i]);
}
}
}
public override void Write()
{
_worldPacket.WritePackedGuid(MoverGUID);
_worldPacket.WriteVector3(Pos);
SplineData.Write(_worldPacket);
}
public MovementMonsterSpline SplineData;
public ObjectGuid MoverGUID;
public Vector3 Pos;
}
class FlightSplineSync : ServerPacket
{
public FlightSplineSync() : base(ServerOpcodes.FlightSplineSync, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WritePackedGuid(Guid);
_worldPacket.WriteFloat(SplineDist);
}
public ObjectGuid Guid;
public float SplineDist;
}
public class MoveSplineSetSpeed : ServerPacket
{
public MoveSplineSetSpeed(ServerOpcodes opcode) : base(opcode, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WritePackedGuid(MoverGUID);
_worldPacket.WriteFloat(Speed);
}
public ObjectGuid MoverGUID;
public float Speed = 1.0f;
}
public class MoveSetSpeed : ServerPacket
{
public MoveSetSpeed(ServerOpcodes opcode) : base(opcode, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WritePackedGuid(MoverGUID);
_worldPacket.WriteUInt32(SequenceIndex);
_worldPacket.WriteFloat(Speed);
}
public ObjectGuid MoverGUID;
public uint SequenceIndex; // Unit movement packet index, incremented each time
public float Speed = 1.0f;
}
public class MoveUpdateSpeed : ServerPacket
{
public MoveUpdateSpeed(ServerOpcodes opcode) : base(opcode, ConnectionType.Instance) { }
public override void Write()
{
MovementExtensions.WriteMovementInfo(_worldPacket, Status);
_worldPacket.WriteFloat(Speed);
}
public MovementInfo Status;
public float Speed = 1.0f;
}
public class MoveSplineSetFlag : ServerPacket
{
public MoveSplineSetFlag(ServerOpcodes opcode) : base(opcode, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WritePackedGuid(MoverGUID);
}
public ObjectGuid MoverGUID;
}
public class MoveSetFlag : ServerPacket
{
public MoveSetFlag(ServerOpcodes opcode) : base(opcode, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WritePackedGuid(MoverGUID);
_worldPacket.WriteUInt32(SequenceIndex);
}
public ObjectGuid MoverGUID;
public uint SequenceIndex; // Unit movement packet index, incremented each time
}
public class TransferPending : ServerPacket
{
public TransferPending() : base(ServerOpcodes.TransferPending) { }
public override void Write()
{
_worldPacket.WriteInt32(MapID);
_worldPacket.WriteXYZ(OldMapPosition);
_worldPacket.WriteBit(Ship.HasValue);
_worldPacket.WriteBit(TransferSpellID.HasValue);
if (Ship.HasValue)
{
_worldPacket.WriteUInt32(Ship.Value.Id);
_worldPacket.WriteInt32(Ship.Value.OriginMapID);
}
if (TransferSpellID.HasValue)
_worldPacket.WriteInt32(TransferSpellID.Value);
_worldPacket.FlushBits();
}
public int MapID = -1;
public Position OldMapPosition;
public ShipTransferPending? Ship;
public int? TransferSpellID;
public struct ShipTransferPending
{
public uint Id; // gameobject_template.entry of the transport the player is teleporting on
public int OriginMapID; // Map id the player is currently on (before teleport)
}
}
public class TransferAborted : ServerPacket
{
public TransferAborted() : base(ServerOpcodes.TransferAborted) { }
public override void Write()
{
_worldPacket.WriteUInt32(MapID);
_worldPacket.WriteUInt8(Arg);
_worldPacket.WriteUInt32(MapDifficultyXConditionID);
_worldPacket.WriteBits(TransfertAbort, 6);
_worldPacket.FlushBits();
}
public uint MapID;
public byte Arg;
public uint MapDifficultyXConditionID;
public TransferAbortReason TransfertAbort;
}
public class NewWorld : ServerPacket
{
public NewWorld() : base(ServerOpcodes.NewWorld) { }
public override void Write()
{
_worldPacket.WriteUInt32(MapID);
Loc.Write(_worldPacket);
_worldPacket.WriteUInt32(Reason);
_worldPacket.WriteXYZ(MovementOffset);
}
public uint MapID;
public uint Reason;
public TeleportLocation Loc = new();
public Position MovementOffset; // Adjusts all pending movement events by this offset
}
public class WorldPortResponse : ClientPacket
{
public WorldPortResponse(WorldPacket packet) : base(packet) { }
public override void Read() { }
}
public class MoveTeleport : ServerPacket
{
public MoveTeleport() : base(ServerOpcodes.MoveTeleport, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WritePackedGuid(MoverGUID);
_worldPacket.WriteUInt32(SequenceIndex);
_worldPacket.WriteXYZ(Pos);
_worldPacket.WriteFloat(Facing);
_worldPacket.WriteUInt8(PreloadWorld);
_worldPacket.WriteBit(TransportGUID.HasValue);
_worldPacket.WriteBit(Vehicle.HasValue);
_worldPacket.FlushBits();
if (Vehicle.HasValue)
{
_worldPacket.WriteUInt8(Vehicle.Value.VehicleSeatIndex);
_worldPacket.WriteBit(Vehicle.Value.VehicleExitVoluntary);
_worldPacket.WriteBit(Vehicle.Value.VehicleExitTeleport);
_worldPacket.FlushBits();
}
if (TransportGUID.HasValue)
_worldPacket.WritePackedGuid(TransportGUID.Value);
}
public Position Pos;
public VehicleTeleport? Vehicle;
public uint SequenceIndex;
public ObjectGuid MoverGUID;
public ObjectGuid? TransportGUID;
public float Facing;
public byte PreloadWorld;
}
public class MoveUpdateTeleport : ServerPacket
{
public MoveUpdateTeleport() : base(ServerOpcodes.MoveUpdateTeleport) { }
public override void Write()
{
MovementExtensions.WriteMovementInfo(_worldPacket, Status);
_worldPacket.WriteInt32(MovementForces != null ? MovementForces.Count : 0);
_worldPacket.WriteBit(WalkSpeed.HasValue);
_worldPacket.WriteBit(RunSpeed.HasValue);
_worldPacket.WriteBit(RunBackSpeed.HasValue);
_worldPacket.WriteBit(SwimSpeed.HasValue);
_worldPacket.WriteBit(SwimBackSpeed.HasValue);
_worldPacket.WriteBit(FlightSpeed.HasValue);
_worldPacket.WriteBit(FlightBackSpeed.HasValue);
_worldPacket.WriteBit(TurnRate.HasValue);
_worldPacket.WriteBit(PitchRate.HasValue);
_worldPacket.FlushBits();
if (MovementForces != null)
foreach (MovementForce force in MovementForces)
force.Write(_worldPacket);
if (WalkSpeed.HasValue)
_worldPacket.WriteFloat(WalkSpeed.Value);
if (RunSpeed.HasValue)
_worldPacket.WriteFloat(RunSpeed.Value);
if (RunBackSpeed.HasValue)
_worldPacket.WriteFloat(RunBackSpeed.Value);
if (SwimSpeed.HasValue)
_worldPacket.WriteFloat(SwimSpeed.Value);
if (SwimBackSpeed.HasValue)
_worldPacket.WriteFloat(SwimBackSpeed.Value);
if (FlightSpeed.HasValue)
_worldPacket.WriteFloat(FlightSpeed.Value);
if (FlightBackSpeed.HasValue)
_worldPacket.WriteFloat(FlightBackSpeed.Value);
if (TurnRate.HasValue)
_worldPacket.WriteFloat(TurnRate.Value);
if (PitchRate.HasValue)
_worldPacket.WriteFloat(PitchRate.Value);
}
public MovementInfo Status;
public List<MovementForce> MovementForces;
public float? SwimBackSpeed;
public float? FlightSpeed;
public float? SwimSpeed;
public float? WalkSpeed;
public float? TurnRate;
public float? RunSpeed;
public float? FlightBackSpeed;
public float? RunBackSpeed;
public float? PitchRate;
}
class MoveApplyMovementForce : ServerPacket
{
public MoveApplyMovementForce() : base(ServerOpcodes.MoveApplyMovementForce, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WritePackedGuid(MoverGUID);
_worldPacket.WriteInt32(SequenceIndex);
Force.Write(_worldPacket);
}
public ObjectGuid MoverGUID;
public int SequenceIndex;
public MovementForce Force;
}
class MoveApplyMovementForceAck : ClientPacket
{
public MoveApplyMovementForceAck(WorldPacket packet) : base(packet) { }
public override void Read()
{
Ack.Read(_worldPacket);
Force.Read(_worldPacket);
}
public MovementAck Ack = new();
public MovementForce Force = new();
}
class MoveRemoveMovementForce : ServerPacket
{
public MoveRemoveMovementForce() : base(ServerOpcodes.MoveRemoveMovementForce, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WritePackedGuid(MoverGUID);
_worldPacket.WriteInt32(SequenceIndex);
_worldPacket.WritePackedGuid(ID);
}
public ObjectGuid MoverGUID;
public int SequenceIndex;
public ObjectGuid ID;
}
class MoveRemoveMovementForceAck : ClientPacket
{
public MoveRemoveMovementForceAck(WorldPacket packet) : base(packet) { }
public override void Read()
{
Ack.Read(_worldPacket);
ID = _worldPacket.ReadPackedGuid();
}
public MovementAck Ack = new();
public ObjectGuid ID;
}
class MoveUpdateApplyMovementForce : ServerPacket
{
public MoveUpdateApplyMovementForce() : base(ServerOpcodes.MoveUpdateApplyMovementForce) { }
public override void Write()
{
MovementExtensions.WriteMovementInfo(_worldPacket, Status);
Force.Write(_worldPacket);
}
public MovementInfo Status = new();
public MovementForce Force = new();
}
class MoveUpdateRemoveMovementForce : ServerPacket
{
public MoveUpdateRemoveMovementForce() : base(ServerOpcodes.MoveUpdateRemoveMovementForce) { }
public override void Write()
{
MovementExtensions.WriteMovementInfo(_worldPacket, Status);
_worldPacket.WritePackedGuid(TriggerGUID);
}
public MovementInfo Status = new();
public ObjectGuid TriggerGUID;
}
class MoveTeleportAck : ClientPacket
{
public MoveTeleportAck(WorldPacket packet) : base(packet) { }
public override void Read()
{
MoverGUID = _worldPacket.ReadPackedGuid();
AckIndex = _worldPacket.ReadInt32();
MoveTime = _worldPacket.ReadInt32();
}
public ObjectGuid MoverGUID;
int AckIndex;
int MoveTime;
}
public class MovementAckMessage : ClientPacket
{
public MovementAckMessage(WorldPacket packet) : base(packet) { }
public override void Read()
{
Ack.Read(_worldPacket);
}
public MovementAck Ack;
}
public class MovementSpeedAck : ClientPacket
{
public MovementSpeedAck(WorldPacket packet) : base(packet) { }
public override void Read()
{
Ack.Read(_worldPacket);
Speed = _worldPacket.ReadFloat();
}
public MovementAck Ack;
public float Speed;
}
public class SetActiveMover : ClientPacket
{
public ObjectGuid ActiveMover;
public SetActiveMover(WorldPacket packet) : base(packet) { }
public override void Read()
{
ActiveMover = _worldPacket.ReadPackedGuid();
}
}
public class MoveSetActiveMover : ServerPacket
{
public ObjectGuid MoverGUID;
public MoveSetActiveMover() : base(ServerOpcodes.MoveSetActiveMover) { }
public override void Write()
{
_worldPacket.WritePackedGuid(MoverGUID);
}
}
class MoveKnockBack : ServerPacket
{
public MoveKnockBack() : base(ServerOpcodes.MoveKnockBack, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WritePackedGuid(MoverGUID);
_worldPacket.WriteUInt32(SequenceIndex);
_worldPacket.WriteVector2(Direction);
Speeds.Write(_worldPacket);
}
public ObjectGuid MoverGUID;
public Vector2 Direction;
public MoveKnockBackSpeeds Speeds;
public uint SequenceIndex;
}
public class MoveUpdateKnockBack : ServerPacket
{
public MoveUpdateKnockBack() : base(ServerOpcodes.MoveUpdateKnockBack) { }
public override void Write()
{
MovementExtensions.WriteMovementInfo(_worldPacket, Status);
}
public MovementInfo Status;
}
class MoveKnockBackAck : ClientPacket
{
public MoveKnockBackAck(WorldPacket packet) : base(packet) { }
public override void Read()
{
Ack.Read(_worldPacket);
if (_worldPacket.HasBit())
{
Speeds = new();
Speeds.Value.Read(_worldPacket);
}
}
public MovementAck Ack;
public MoveKnockBackSpeeds? Speeds;
}
class MoveSetCollisionHeight : ServerPacket
{
public MoveSetCollisionHeight() : base(ServerOpcodes.MoveSetCollisionHeight) { }
public override void Write()
{
_worldPacket.WritePackedGuid(MoverGUID);
_worldPacket.WriteUInt32(SequenceIndex);
_worldPacket.WriteFloat(Height);
_worldPacket.WriteFloat(Scale);
_worldPacket.WriteUInt8((byte)Reason);
_worldPacket.WriteUInt32(MountDisplayID);
_worldPacket.WriteInt32(ScaleDuration);
}
public float Scale = 1.0f;
public ObjectGuid MoverGUID;
public uint MountDisplayID;
public UpdateCollisionHeightReason Reason;
public uint SequenceIndex;
public int ScaleDuration;
public float Height = 1.0f;
}
public class MoveUpdateCollisionHeight : ServerPacket
{
public MoveUpdateCollisionHeight() : base(ServerOpcodes.MoveUpdateCollisionHeight) { }
public override void Write()
{
MovementExtensions.WriteMovementInfo(_worldPacket, Status);
_worldPacket.WriteFloat(Height);
_worldPacket.WriteFloat(Scale);
}
public MovementInfo Status;
public float Scale = 1.0f;
public float Height = 1.0f;
}
public class MoveSetCollisionHeightAck : ClientPacket
{
public MoveSetCollisionHeightAck(WorldPacket packet) : base(packet) { }
public override void Read()
{
Data.Read(_worldPacket);
Height = _worldPacket.ReadFloat();
MountDisplayID = _worldPacket.ReadUInt32();
Reason = (UpdateCollisionHeightReason)_worldPacket.ReadUInt8();
}
public MovementAck Data;
public UpdateCollisionHeightReason Reason;
public uint MountDisplayID;
public float Height = 1.0f;
}
class MoveTimeSkipped : ClientPacket
{
public MoveTimeSkipped(WorldPacket packet) : base(packet) { }
public override void Read()
{
MoverGUID = _worldPacket.ReadPackedGuid();
TimeSkipped = _worldPacket.ReadUInt32();
}
public ObjectGuid MoverGUID;
public uint TimeSkipped;
}
class MoveSkipTime : ServerPacket
{
public MoveSkipTime() : base(ServerOpcodes.MoveSkipTime, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WritePackedGuid(MoverGUID);
_worldPacket.WriteUInt32(TimeSkipped);
}
public ObjectGuid MoverGUID;
public uint TimeSkipped;
}
class SummonResponse : ClientPacket
{
public SummonResponse(WorldPacket packet) : base(packet) { }
public override void Read()
{
SummonerGUID = _worldPacket.ReadPackedGuid();
Accept = _worldPacket.HasBit();
}
public bool Accept;
public ObjectGuid SummonerGUID;
}
public class ControlUpdate : ServerPacket
{
public ControlUpdate() : base(ServerOpcodes.ControlUpdate) { }
public override void Write()
{
_worldPacket.WritePackedGuid(Guid);
_worldPacket.WriteBit(On);
_worldPacket.FlushBits();
}
public bool On;
public ObjectGuid Guid;
}
class MoveSplineDone : ClientPacket
{
public MoveSplineDone(WorldPacket packet) : base(packet) { }
public override void Read()
{
Status = MovementExtensions.ReadMovementInfo(_worldPacket);
SplineID = _worldPacket.ReadInt32();
}
public MovementInfo Status;
public int SplineID;
}
class SummonRequest : ServerPacket
{
public SummonRequest() : base(ServerOpcodes.SummonRequest, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WritePackedGuid(SummonerGUID);
_worldPacket.WriteUInt32(SummonerVirtualRealmAddress);
_worldPacket.WriteInt32(AreaID);
_worldPacket.WriteUInt8((byte)Reason);
_worldPacket.WriteBit(SkipStartingArea);
_worldPacket.FlushBits();
}
public ObjectGuid SummonerGUID;
public uint SummonerVirtualRealmAddress;
public int AreaID;
public SummonReason Reason;
public bool SkipStartingArea;
public enum SummonReason
{
Spell = 0,
Scenario = 1
}
}
class SuspendToken : ServerPacket
{
public SuspendToken() : base(ServerOpcodes.SuspendToken, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WriteUInt32(SequenceIndex);
_worldPacket.WriteBits(Reason, 2);
_worldPacket.FlushBits();
}
public uint SequenceIndex = 1;
public uint Reason = 1;
}
class SuspendTokenResponse : ClientPacket
{
public SuspendTokenResponse(WorldPacket packet) : base(packet) { }
public override void Read()
{
SequenceIndex = _worldPacket.ReadUInt32();
}
public uint SequenceIndex;
}
class ResumeToken : ServerPacket
{
public ResumeToken() : base(ServerOpcodes.ResumeToken, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WriteUInt32(SequenceIndex);
_worldPacket.WriteBits(Reason, 2);
_worldPacket.FlushBits();
}
public uint SequenceIndex = 1;
public uint Reason = 1;
}
class MoveSetCompoundState : ServerPacket
{
public MoveSetCompoundState() : base(ServerOpcodes.MoveSetCompoundState, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WritePackedGuid(MoverGUID);
_worldPacket.WriteInt32(StateChanges.Count);
foreach (MoveStateChange stateChange in StateChanges)
stateChange.Write(_worldPacket);
}
public ObjectGuid MoverGUID;
public List<MoveStateChange> StateChanges = new();
public struct CollisionHeightInfo
{
public float Height;
public float Scale;
public UpdateCollisionHeightReason Reason;
}
public struct KnockBackInfo
{
public float HorzSpeed;
public Vector2 Direction;
public float InitVertSpeed;
}
public class SpeedRange
{
public float Min;
public float Max;
}
public class MoveStateChange
{
public MoveStateChange(ServerOpcodes messageId, uint sequenceIndex)
{
MessageID = messageId;
SequenceIndex = sequenceIndex;
}
public void Write(WorldPacket data)
{
data.WriteUInt16((ushort)MessageID);
data.WriteUInt32(SequenceIndex);
data.WriteBit(Speed.HasValue);
data.WriteBit(SpeedRange != null);
data.WriteBit(KnockBack.HasValue);
data.WriteBit(VehicleRecID.HasValue);
data.WriteBit(CollisionHeight.HasValue);
data.WriteBit(@MovementForce != null);
data.WriteBit(MovementForceGUID.HasValue);
data.WriteBit(MovementInertiaID.HasValue);
data.WriteBit(MovementInertiaLifetimeMs.HasValue);
data.FlushBits();
if (@MovementForce != null)
@MovementForce.Write(data);
if (Speed.HasValue)
data.WriteFloat(Speed.Value);
if (SpeedRange != null)
{
data.WriteFloat(SpeedRange.Min);
data.WriteFloat(SpeedRange.Max);
}
if (KnockBack.HasValue)
{
data.WriteFloat(KnockBack.Value.HorzSpeed);
data.WriteVector2(KnockBack.Value.Direction);
data.WriteFloat(KnockBack.Value.InitVertSpeed);
}
if (VehicleRecID.HasValue)
data.WriteInt32(VehicleRecID.Value);
if (CollisionHeight.HasValue)
{
data.WriteFloat(CollisionHeight.Value.Height);
data.WriteFloat(CollisionHeight.Value.Scale);
data.WriteBits(CollisionHeight.Value.Reason, 2);
data.FlushBits();
}
if (MovementForceGUID.HasValue)
data.WritePackedGuid(MovementForceGUID.Value);
if (MovementInertiaID.HasValue)
data.WriteInt32(MovementInertiaID.Value);
if (MovementInertiaLifetimeMs.HasValue)
data.WriteUInt32(MovementInertiaLifetimeMs.Value);
}
public ServerOpcodes MessageID;
public uint SequenceIndex;
public float? Speed;
public SpeedRange SpeedRange;
public KnockBackInfo? KnockBack;
public int? VehicleRecID;
public CollisionHeightInfo? CollisionHeight;
public MovementForce MovementForce;
public ObjectGuid? MovementForceGUID;
public int? MovementInertiaID;
public uint? MovementInertiaLifetimeMs;
}
}
class MoveInitActiveMoverComplete : ClientPacket
{
public uint Ticks;
public MoveInitActiveMoverComplete(WorldPacket packet) : base(packet) { }
public override void Read()
{
Ticks = _worldPacket.ReadUInt32();
}
}
//Structs
public struct MovementAck
{
public void Read(WorldPacket data)
{
Status = MovementExtensions.ReadMovementInfo(data);
AckIndex = data.ReadInt32();
}
public MovementInfo Status;
public int AckIndex;
}
public struct MonsterSplineFilterKey
{
public void Write(WorldPacket data)
{
data.WriteInt16(Idx);
data.WriteUInt16(Speed);
}
public short Idx;
public ushort Speed;
}
public class MonsterSplineFilter
{
public void Write(WorldPacket data)
{
data.WriteInt32(FilterKeys.Count);
data.WriteFloat(BaseSpeed);
data.WriteInt16(StartOffset);
data.WriteFloat(DistToPrevFilterKey);
data.WriteInt16(AddedToStart);
FilterKeys.ForEach(p => p.Write(data));
data.WriteBits(FilterFlags, 2);
data.FlushBits();
}
public List<MonsterSplineFilterKey> FilterKeys = new();
public byte FilterFlags;
public float BaseSpeed;
public short StartOffset;
public float DistToPrevFilterKey;
public short AddedToStart;
}
public struct MonsterSplineSpellEffectExtraData
{
public void Write(WorldPacket data)
{
data.WritePackedGuid(TargetGuid);
data.WriteUInt32(SpellVisualID);
data.WriteUInt32(ProgressCurveID);
data.WriteUInt32(ParabolicCurveID);
data.WriteFloat(JumpGravity);
}
public ObjectGuid TargetGuid;
public uint SpellVisualID;
public uint ProgressCurveID;
public uint ParabolicCurveID;
public float JumpGravity;
}
public struct MonsterSplineJumpExtraData
{
public float JumpGravity;
public uint StartTime;
public uint Duration;
public void Write(WorldPacket data)
{
data.WriteFloat(JumpGravity);
data.WriteUInt32(StartTime);
data.WriteUInt32(Duration);
}
}
public struct MonsterSplineAnimTierTransition
{
public int TierTransitionID;
public uint StartTime;
public uint EndTime;
public byte AnimTier;
public void Write(WorldPacket data)
{
data.WriteInt32(TierTransitionID);
data .WriteUInt32(StartTime);
data.WriteUInt32(EndTime);
data.WriteUInt8(AnimTier);
}
}
public class MonsterSplineUnknown901
{
public Array<Inner> Data = new(16);
public void Write(WorldPacket data)
{
foreach (var unkInner in Data)
{
data.WriteInt32(unkInner.Unknown_1);
unkInner.Visual.Write(data);
data.WriteUInt32(unkInner.Unknown_4);
}
}
public struct Inner
{
public int Unknown_1;
public SpellCastVisual Visual;
public uint Unknown_4;
}
}
public class TeleportLocation
{
public Position Pos;
public int Unused901_1 = -1;
public int Unused901_2 = -1;
public void Write(WorldPacket data)
{
data.WriteXYZO(Pos);
data.WriteInt32(Unused901_1);
data.WriteInt32(Unused901_2);
}
}
public class MovementSpline
{
public void Write(WorldPacket data)
{
data.WriteUInt32(Flags);
data.WriteInt32(Elapsed);
data.WriteUInt32(MoveTime);
data.WriteUInt32(FadeObjectTime);
data.WriteUInt8(Mode);
data.WritePackedGuid(TransportGUID);
data.WriteInt8(VehicleSeat);
data.WriteBits((byte)Face, 2);
data.WriteBits(Points.Count, 16);
data.WriteBit(VehicleExitVoluntary);
data.WriteBit(Interpolate);
data.WriteBits(PackedDeltas.Count, 16);
data.WriteBit(SplineFilter != null);
data.WriteBit(SpellEffectExtraData.HasValue);
data.WriteBit(JumpExtraData.HasValue);
data.WriteBit(AnimTierTransition.HasValue);
data.WriteBit(Unknown901 != null);
data.FlushBits();
if (SplineFilter != null)
SplineFilter.Write(data);
switch (Face)
{
case MonsterMoveType.FacingSpot:
data.WriteVector3(FaceSpot);
break;
case MonsterMoveType.FacingTarget:
data.WriteFloat(FaceDirection);
data.WritePackedGuid(FaceGUID);
break;
case MonsterMoveType.FacingAngle:
data.WriteFloat(FaceDirection);
break;
}
foreach (Vector3 pos in Points)
data.WriteVector3(pos);
foreach (Vector3 pos in PackedDeltas)
data.WritePackXYZ(pos);
if (SpellEffectExtraData.HasValue)
SpellEffectExtraData.Value.Write(data);
if (JumpExtraData.HasValue)
JumpExtraData.Value.Write(data);
if (AnimTierTransition.HasValue)
AnimTierTransition.Value.Write(data);
if (Unknown901 != null)
Unknown901.Write(data);
}
public uint Flags; // Spline flags
public MonsterMoveType Face; // Movement direction (see MonsterMoveType enum)
public int Elapsed;
public uint MoveTime;
public uint FadeObjectTime;
public List<Vector3> Points = new(); // Spline path
public byte Mode; // Spline mode - actually always 0 in this packet - Catmullrom mode appears only in SMSG_UPDATE_OBJECT. In this packet it is determined by flags
public bool VehicleExitVoluntary;
public bool Interpolate;
public ObjectGuid TransportGUID;
public sbyte VehicleSeat = -1;
public List<Vector3> PackedDeltas = new();
public MonsterSplineFilter SplineFilter;
public MonsterSplineSpellEffectExtraData? SpellEffectExtraData;
public MonsterSplineJumpExtraData? JumpExtraData;
public MonsterSplineAnimTierTransition? AnimTierTransition;
public MonsterSplineUnknown901 Unknown901;
public float FaceDirection;
public ObjectGuid FaceGUID;
public Vector3 FaceSpot;
}
public class MovementMonsterSpline
{
public MovementMonsterSpline()
{
Move = new MovementSpline();
}
public void Write(WorldPacket data)
{
data.WriteUInt32(Id);
data.WriteVector3(Destination);
data.WriteBit(CrzTeleport);
data.WriteBits(StopDistanceTolerance, 3);
Move.Write(data);
}
public uint Id;
public Vector3 Destination;
public bool CrzTeleport;
public byte StopDistanceTolerance; // Determines how far from spline destination the mover is allowed to stop in place 0, 0, 3.0, 2.76, numeric_limits<float>::max, 1.1, float(INT_MAX); default before this field existed was distance 3.0 (index 2)
public MovementSpline Move;
}
public struct VehicleTeleport
{
public byte VehicleSeatIndex;
public bool VehicleExitVoluntary;
public bool VehicleExitTeleport;
}
public struct MoveKnockBackSpeeds
{
public void Write(WorldPacket data)
{
data.WriteFloat(HorzSpeed);
data.WriteFloat(VertSpeed);
}
public void Read(WorldPacket data)
{
HorzSpeed = data.ReadFloat();
VertSpeed = data.ReadFloat();
}
public float HorzSpeed;
public float VertSpeed;
}
}