using System; using System.IO; using System.Numerics; using ACE.Entity.Enum; using ACE.Server.Network.Sequence; using ACE.Server.Physics; using ACE.Server.WorldObjects; namespace ACE.Server.Network.Structure { /// /// A position with sequences /// public class PositionPack { public WorldObject WorldObject; public PositionFlags Flags; public Origin Origin; // the location of the object in the world public Quaternion Rotation; public Vector3 Velocity; public Placement? PlacementID; // really just a bunch of ushorts for these particular sequences, // but for type safety, there appear to be some other sequences that could be either uints or ulongs, // so GetCurrentSequence/GetNextSequence returns byte arrays here... public byte[] InstanceSequence; public byte[] PositionSequence; public byte[] TeleportSequence; public byte[] ForcePositionSequence; public PositionPack() { } public PositionPack(WorldObject wo, bool adminMove = false) { WorldObject = wo; Origin = new Origin(wo.Location.Cell, wo.Location.Pos); Rotation = wo.Location.Rotation; Velocity = wo.PhysicsObj != null ? wo.PhysicsObj.Velocity : Vector3.Zero; // average or instantaneous? PlacementID = wo.Placement; // note that this constructor increments the wo position sequence InstanceSequence = wo.Sequences.GetCurrentSequence(SequenceType.ObjectInstance); PositionSequence = wo.Sequences.GetNextSequence(SequenceType.ObjectPosition); if (adminMove) TeleportSequence = wo.Sequences.GetNextSequence(SequenceType.ObjectTeleport); else TeleportSequence = wo.Sequences.GetCurrentSequence(SequenceType.ObjectTeleport); ForcePositionSequence = wo.Sequences.GetCurrentSequence(SequenceType.ObjectForcePosition); Flags = BuildFlags(); } /// /// Returns the PositionFlags based on the current state /// public PositionFlags BuildFlags() { var flags = PositionFlags.None; if (Velocity != Vector3.Zero) flags |= PositionFlags.HasVelocity; if (PlacementID != null) flags |= PositionFlags.HasPlacementID; if (WorldObject.PhysicsObj != null && (WorldObject.PhysicsObj.TransientState & TransientStateFlags.OnWalkable) != 0) flags |= PositionFlags.IsGrounded; if (Rotation.W == 0.0f) flags |= PositionFlags.OrientationHasNoW; if (Rotation.X == 0.0f) flags |= PositionFlags.OrientationHasNoX; if (Rotation.Y == 0.0f) flags |= PositionFlags.OrientationHasNoY; if (Rotation.Z == 0.0f) flags |= PositionFlags.OrientationHasNoZ; return flags; } } public static class PositionPackExtensions { public static void Write(this BinaryWriter writer, PositionPack position) { writer.Write((uint)position.Flags); writer.Write(position.Origin); // choose valid sections by masking against flags if ((position.Flags & PositionFlags.OrientationHasNoW) == 0) writer.Write(position.Rotation.W); if ((position.Flags & PositionFlags.OrientationHasNoX) == 0) writer.Write(position.Rotation.X); if ((position.Flags & PositionFlags.OrientationHasNoY) == 0) writer.Write(position.Rotation.Y); if ((position.Flags & PositionFlags.OrientationHasNoZ) == 0) writer.Write(position.Rotation.Z); if ((position.Flags & PositionFlags.HasVelocity) != 0) writer.Write(position.Velocity); if ((position.Flags & PositionFlags.HasPlacementID) != 0) writer.Write((uint)position.PlacementID); writer.Write(position.InstanceSequence); writer.Write(position.PositionSequence); writer.Write(position.TeleportSequence); writer.Write(position.ForcePositionSequence); } } }