diff --git a/common/src/main/scala/net/psforever/packet/game/ChildObjectStateMessage.scala b/common/src/main/scala/net/psforever/packet/game/ChildObjectStateMessage.scala index 4b2a3058..fdc03eea 100644 --- a/common/src/main/scala/net/psforever/packet/game/ChildObjectStateMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/ChildObjectStateMessage.scala @@ -2,6 +2,7 @@ package net.psforever.packet.game import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} +import net.psforever.types.Angular import scodec.Codec import scodec.codecs._ @@ -16,18 +17,12 @@ import scodec.codecs._ * The only concern is the direction the object is facing. * The angles are relative to the object's normal forward-facing and typically begin tracking at 0, 0 (forward-facing). * @param object_guid the object being manipulated (controlled) - * @param pitch the angle with respect to the sky and the ground towards which the object is directed; - * an 8-bit unsigned value; - * 0 is perfectly level and forward-facing and mapped to 255; - * positive rotation is downwards from forward-facing - * @param yaw the angle with respect to the horizon towards which the object is directed; - * an 8-bit unsigned value; - * 0 is forward-facing, wrapping around at 127; - * positive rotation is counter-clockwise of forward-facing + * @param pitch the amount of pitch that affects orientation from forward facing (0) + * @param yaw the amount of yaw that affects orientation from forward-facing (0) */ final case class ChildObjectStateMessage(object_guid : PlanetSideGUID, - pitch : Int, - yaw : Int) + pitch : Float, + yaw : Float) extends PlanetSideGamePacket { type Packet = ChildObjectStateMessage def opcode = GamePacketOpcode.ChildObjectStateMessage @@ -37,7 +32,7 @@ final case class ChildObjectStateMessage(object_guid : PlanetSideGUID, object ChildObjectStateMessage extends Marshallable[ChildObjectStateMessage] { implicit val codec : Codec[ChildObjectStateMessage] = ( ("object_guid" | PlanetSideGUID.codec) :: - ("pitch" | uint8L) :: - ("yaw" | uint8L) + ("pitch" | Angular.codec_pitch) :: + ("yaw" | Angular.codec_yaw(0f)) ).as[ChildObjectStateMessage] } diff --git a/common/src/main/scala/net/psforever/packet/game/ObjectDetachMessage.scala b/common/src/main/scala/net/psforever/packet/game/ObjectDetachMessage.scala index 8630fc74..4d6abda7 100644 --- a/common/src/main/scala/net/psforever/packet/game/ObjectDetachMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/ObjectDetachMessage.scala @@ -2,7 +2,7 @@ package net.psforever.packet.game import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} -import net.psforever.types.Vector3 +import net.psforever.types.{Angular, Vector3} import scodec.Codec import scodec.codecs._ @@ -23,25 +23,16 @@ import scodec.codecs._ * @param parent_guid the container/connector object * @param child_guid the contained/connected object * @param pos where the contained/connected object will be placed after it has detached - * @param roll the roll of the dropped item; - * every `0x1` is 2.813 degrees; - * every `0x10` is 45-degrees; - * it wraps at `0x0` == `0x80` == top facing up - * @param pitch the pitch of the dropped item; - * every `0x1` is 2.813 degrees; - * every `0x10` is 45-degrees; - * it wraps at `0x0` == `0x80` == top facing up - * @param yaw the yaw of the dropped item; - * every `0x1` is 2.813 degrees counter clockwise from East; - * every `0x10` is 45-degrees; - * it wraps at `0x0` == `0x80` == front facing East + * @param roll the amount of roll that affects orientation of the dropped item + * @param pitch the amount of pitch that affects orientation of the dropped item + * @param yaw the amount of yaw that affects orientation of the dropped item */ final case class ObjectDetachMessage(parent_guid : PlanetSideGUID, child_guid : PlanetSideGUID, pos : Vector3, - roll : Int, - pitch : Int, - yaw : Int) + roll : Float, + pitch : Float, + yaw : Float) extends PlanetSideGamePacket { type Packet = ObjectDetachMessage def opcode = GamePacketOpcode.ObjectDetachMessage @@ -53,8 +44,8 @@ object ObjectDetachMessage extends Marshallable[ObjectDetachMessage] { ("parent_guid" | PlanetSideGUID.codec) :: ("child_guid" | PlanetSideGUID.codec) :: ("pos" | Vector3.codec_pos) :: - ("roll" | uint8L) :: - ("pitch" | uint8L) :: - ("yaw" | uint8L) + ("roll" | Angular.codec_roll) :: + ("pitch" | Angular.codec_pitch) :: + ("yaw" | Angular.codec_yaw()) ).as[ObjectDetachMessage] } diff --git a/common/src/main/scala/net/psforever/packet/game/PlayerStateMessage.scala b/common/src/main/scala/net/psforever/packet/game/PlayerStateMessage.scala index 1def7ca6..a3758155 100644 --- a/common/src/main/scala/net/psforever/packet/game/PlayerStateMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/PlayerStateMessage.scala @@ -3,7 +3,7 @@ package net.psforever.packet.game import net.psforever.newcodecs.newcodecs import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} -import net.psforever.types.Vector3 +import net.psforever.types.{Angular, Vector3} import scodec.Codec import scodec.codecs._ import shapeless.{::, HNil} @@ -20,7 +20,7 @@ import shapeless.{::, HNil} *
* The avatar model normally moves from where it "currently" is to `pos`. * When `vel` is defined, `pos` is treated as where the avatar model starts its animation. - * In that case, it sppears to teleport to `pos` to carry out the interpolated movement according to `vel`. + * In that case, it appears to teleport to `pos` to carry out the interpolated movement according to `vel`. * After the move, it remains at essentially `pos + vel * t`. * The repositioning always takes the same amount of time. * The player model is left in a walking/running animation (in place) until directed otherwise.
@@ -29,39 +29,15 @@ import shapeless.{::, HNil} * A demonstration of this is what happens when one player "runs past"/"into" another player running up stairs. * The climbing player is frequently reported by the other to appear to bounce over that player's head. * If the other player is off the ground, passing too near to the observer can cause a rubber band effect on trajectory. - * This effect is entirely client-side to the observer and affects the moving player in no way.
- *
- * facingYaw:
- * `0x00` -- E
- * `0x10` -- NE
- * `0x20` -- N
- * `0x30` -- NW
- * `0x40` -- W
- * `0x50` -- SW
- * `0x60` -- S
- * `0x70` -- SE
- * `0x80` -- E
- *
- * facingPitch:
- * `0x00`-`0x20` -- downwards-facing angles, with `0x00` as forwards-facing
- * `0x21`-`0x40` -- downwards-facing
- * `0x41`-`0x59` -- upwards-facing
- * `0x60`-`0x80` -- upwards-facing angles, with `0x80` as forwards-facing
- *
- * facingYawUpper:
- * `0x00`-`0x20` -- turning to left, with `0x00` being forward-facing
- * `0x21`-`0x40` -- facing leftwards
- * `0x41`-`0x59` -- facing rightwards
- * `0x60`-`0x80` -- turning to right, with `0x80` being forward-facing - * + * This effect is entirely client-side to the observer and affects the moving player in no way. * @param guid the avatar's guid * @param pos the position of the avatar in the world environment (in three coordinates) * @param vel an optional velocity - * @param facingYaw the angle with respect to the horizon towards which the avatar is looking; - * the model's whole body is facing this direction; - * measurements are counter-clockwise from East - * @param facingPitch the angle with respect to the sky and the ground towards which the avatar is looking - * @param facingYawUpper the angle of the avatar's upper body with respect to its forward-facing direction + * @param facingYaw a "yaw" angle + * @param facingPitch a "pitch" angle + * @param facingYawUpper a "yaw" angle that represents the angle of the avatar's upper body with respect to its forward-facing direction; + * this number is normally 0 for forward facing; + * the range is limited between approximately 61 degrees of center turned to left or right * @param unk1 na * @param is_crouching avatar is crouching * @param is_jumping avatar is jumping; @@ -72,9 +48,9 @@ import shapeless.{::, HNil} final case class PlayerStateMessage(guid : PlanetSideGUID, pos : Vector3, vel : Option[Vector3], - facingYaw : Int, - facingPitch : Int, - facingYawUpper : Int, + facingYaw : Float, + facingPitch : Float, + facingYawUpper : Float, unk1 : Int, is_crouching : Boolean = false, is_jumping : Boolean = false, @@ -117,9 +93,9 @@ object PlayerStateMessage extends Marshallable[PlayerStateMessage] { ("guid" | PlanetSideGUID.codec) :: ("pos" | Vector3.codec_pos) :: optional(bool, "vel" | Vector3.codec_vel) :: - ("facingYaw" | uint8L) :: - ("facingPitch" | uint8L) :: - ("facingYawUpper" | uint8L) :: + ("facingYaw" | Angular.codec_yaw()) :: + ("facingPitch" | Angular.codec_pitch) :: + ("facingYawUpper" | Angular.codec_yaw(0f)) :: ("unk1" | uintL(10)) :: (bool >>:~ { fourBools => newcodecs.binary_choice(!fourBools, booleanCodec, defaultCodec) diff --git a/common/src/main/scala/net/psforever/packet/game/PlayerStateMessageUpstream.scala b/common/src/main/scala/net/psforever/packet/game/PlayerStateMessageUpstream.scala index 96cd228a..3895dce2 100644 --- a/common/src/main/scala/net/psforever/packet/game/PlayerStateMessageUpstream.scala +++ b/common/src/main/scala/net/psforever/packet/game/PlayerStateMessageUpstream.scala @@ -1,8 +1,8 @@ // Copyright (c) 2017 PSForever package net.psforever.packet.game -import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} -import net.psforever.types.Vector3 +import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} +import net.psforever.types.{Angular, Vector3} import scodec.Codec import scodec.codecs._ @@ -15,11 +15,11 @@ import scodec.codecs._ * @param avatar_guid the player's GUID * @param pos where the player is in the world * @param vel how the player is moving - * @param facingYaw the angle with respect to the horizon towards which the avatar is looking; - * the model's whole body is facing this direction; - * measurements are counter-clockwise from East - * @param facingPitch the angle with respect to the sky and the ground towards which the avatar is looking - * @param facingYawUpper the angle of the avatar's upper body with respect to its forward-facing direction + * @param facingYaw a "yaw" angle + * @param facingPitch a "pitch" angle + * @param facingYawUpper a "yaw" angle that represents the angle of the avatar's upper body with respect to its forward-facing direction; + * this number is normally 0 for forward facing; + * the range is limited between approximately 61 degrees of center turned to left or right * @param seq_time na * @param unk1 na * @param is_crouching avatar is crouching @@ -33,9 +33,9 @@ import scodec.codecs._ final case class PlayerStateMessageUpstream(avatar_guid : PlanetSideGUID, pos : Vector3, vel : Option[Vector3], - facingYaw : Int, - facingPitch : Int, - facingYawUpper : Int, + facingYaw : Float, + facingPitch : Float, + facingYawUpper : Float, seq_time : Int, unk1 : Int, is_crouching : Boolean, @@ -55,9 +55,9 @@ object PlayerStateMessageUpstream extends Marshallable[PlayerStateMessageUpstrea ("avatar_guid" | PlanetSideGUID.codec) :: ("pos" | Vector3.codec_pos) :: ("vel" | optional(bool, Vector3.codec_vel)) :: - ("facingYaw" | uint8L) :: - ("facingPitch" | uint8L) :: - ("facingYawUpper" | uint8L) :: + ("facingYaw" | Angular.codec_yaw()) :: + ("facingPitch" | Angular.codec_pitch) :: + ("facingYawUpper" | Angular.codec_yaw(0f)) :: ("seq_time" | uintL(10)) :: ("unk1" | uintL(3)) :: ("is_crouching" | bool) :: diff --git a/common/src/main/scala/net/psforever/packet/game/PlayerStateShiftMessage.scala b/common/src/main/scala/net/psforever/packet/game/PlayerStateShiftMessage.scala index df199774..f48c06fc 100644 --- a/common/src/main/scala/net/psforever/packet/game/PlayerStateShiftMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/PlayerStateShiftMessage.scala @@ -2,8 +2,8 @@ package net.psforever.packet.game import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} -import net.psforever.types.Vector3 -import scodec.{Attempt, Codec, Err} +import net.psforever.types.{Angular, Vector3} +import scodec.Codec import scodec.codecs._ import shapeless.{::, HNil} @@ -16,23 +16,25 @@ import shapeless.{::, HNil} * This external force is not accumulative. * Also, the external force is only applied once the avatar is set to the provided position.
*
- * `viewYawLim` defines a "range of angles" that the avatar may look centered on the supplied angle. - * The avatar must be facing within 60-degrees of that direction, subjectively his left or his right. - * The avatar's view is immediately set to the closest 60-degree mark if it is outside of that range. - * The absolute angular displacement of the avatar is considered before applying this corrective behavior. - * After rotating any number of times: - * stopping in a valid part of the range is acceptable; - * stopping in an invalid part of the range will cause the avatar to align to the __earliest__ still-valid 60-degree mark. - * For that reason, even if the avatar's final angle is closest to the "left mark," it may re-align to the "right mark." - * This also resets the avatar's angular displacement. + * The angle defines the center of a range of angles that count as "in front of the avatar." + * Specifically, this range is the upper body's turn limit. + * A stationary player may look left and right, rotating their upper body only, until they hit a certain angle. + * Normally, the player's whole body will then turn to accommodate turning further than this angle. + * This packet marks that limit as a hard limit for rotation and will reset the player's model and camera if necessary. + * While it is in effect, the player will not turn their whole body once they can no longer turn their upper body. * @param unk na * @param pos the position to move the character to in the world environment - * @param viewYawLim an angle with respect to the horizon towards which the avatar is looking (to some respect) + * @param viewYawLim the center of the range of upper body angles, the player's actual yaw; + * if this value is beyond its angular limit values, + * the model will attempt to snap to what it considers the closest upper body turning limit angle; + * the actual range is approximately `viewYawLimit +/- 61.8215`; * @param vel if defined, the velocity to apply to to the character at the given position + * @see `PlayerStateMessageUpstream.facingYawUpper` + * @see `PlayerStateMessage.facingYawUpper` */ final case class ShiftState(unk : Int, pos : Vector3, - viewYawLim : Int, + viewYawLim : Float, vel : Option[Vector3]) /** @@ -55,7 +57,7 @@ final case class PlayerStateShiftMessage(state : Option[ShiftState], def encode = PlayerStateShiftMessage.encode(this) } -object ShiftState extends Marshallable[ShiftState] { +object ShiftState { /** * An abbreviated constructor for creating `ShiftState`, assuming velocity is not applied. * @param unk na @@ -64,7 +66,7 @@ object ShiftState extends Marshallable[ShiftState] { * @param vel the velocity to apply to to the character at the given position * @return a `ShiftState` object */ - def apply(unk : Int, pos : Vector3, viewYawLim : Int, vel : Vector3) : ShiftState = + def apply(unk : Int, pos : Vector3, viewYawLim : Float, vel : Vector3) : ShiftState = ShiftState(unk, pos, viewYawLim, Some(vel)) /** @@ -74,24 +76,8 @@ object ShiftState extends Marshallable[ShiftState] { * @param viewYawLim an angle with respect to the horizon towards which the avatar is looking (to some respect) * @return a `ShiftState` object */ - def apply(unk : Int, pos : Vector3, viewYawLim : Int) : ShiftState = + def apply(unk : Int, pos : Vector3, viewYawLim : Float) : ShiftState = ShiftState(unk, pos, viewYawLim, None) - - implicit val codec : Codec[ShiftState] = ( - ("unk1" | uintL(3)) :: - ("pos" | Vector3.codec_pos) :: - ("viewYawLim" | uint8L) :: - optional(bool, "pos" | Vector3.codec_vel) - ).xmap[ShiftState] ( - { - case a :: b :: c :: d :: HNil => - ShiftState(a, b, c, d) - }, - { - case ShiftState(a, b, c, d) => - a :: b :: c :: d :: HNil - } - ).as[ShiftState] } object PlayerStateShiftMessage extends Marshallable[PlayerStateShiftMessage] { @@ -120,8 +106,30 @@ object PlayerStateShiftMessage extends Marshallable[PlayerStateShiftMessage] { def apply(unk : Int) : PlayerStateShiftMessage = PlayerStateShiftMessage(None, Some(unk)) + private val shift_codec : Codec[ShiftState] = ( + /* + IMPORTANT: + Packet data indicates that viewYawLimit is an 8u value. + When read as an 8u value, the resulting number does not map to directions properly. + As a 7u value, the numbers maps better so the first bit will be ignored. + */ + ("unk" | uintL(3)) :: + ("pos" | Vector3.codec_pos) :: + ("viewYawLim" | Angular.codec_yaw()) :: + optional(bool, "pos" | Vector3.codec_vel) + ).xmap[ShiftState] ( + { + case a :: b :: c :: d :: HNil => + ShiftState(a, b, c, d) + }, + { + case ShiftState(a, b, c, d) => + a :: b :: c :: d :: HNil + } + ).as[ShiftState] + implicit val codec : Codec[PlayerStateShiftMessage] = ( - optional(bool, "state" | ShiftState.codec) :: + optional(bool, "state" | shift_codec) :: optional(bool, "unk" | uintL(3)) ).xmap[PlayerStateShiftMessage] ( { diff --git a/common/src/main/scala/net/psforever/packet/game/VehicleStateMessage.scala b/common/src/main/scala/net/psforever/packet/game/VehicleStateMessage.scala index 40910c43..4f528584 100644 --- a/common/src/main/scala/net/psforever/packet/game/VehicleStateMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/VehicleStateMessage.scala @@ -1,12 +1,10 @@ // Copyright (c) 2017 PSForever package net.psforever.packet.game -import net.psforever.newcodecs.newcodecs import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} -import net.psforever.types.Vector3 +import net.psforever.types.{Angular, Vector3} import scodec.Codec import scodec.codecs._ -import shapeless.{::, HNil} //TODO write more thorough comments later. /** @@ -14,15 +12,7 @@ import shapeless.{::, HNil} * @param vehicle_guid the vehicle * @param unk1 na * @param pos the xyz-coordinate location in the world - * @param roll the amount of roll that affects orientation; - * 0.0f is flat to the ground; - * roll-right rotation increases angle - * @param pitch the amount of pitch that affects orientation; - * 0.0f is flat to the ground; - * front-up rotation increases angle - * @param yaw the amount of yaw that affects orientation; - * 0.0f is North (before the correction, 0.0f is East); - * clockwise rotation increases angle + * @param ang the orientation of the vehicle * @param vel optional movement data * @param unk2 na * @param unk3 na @@ -38,9 +28,7 @@ import shapeless.{::, HNil} final case class VehicleStateMessage(vehicle_guid : PlanetSideGUID, unk1 : Int, pos : Vector3, - roll : Float, - pitch : Float, - yaw : Float, + ang : Vector3, vel : Option[Vector3], unk2 : Option[Int], unk3 : Int, @@ -55,13 +43,23 @@ final case class VehicleStateMessage(vehicle_guid : PlanetSideGUID, } object VehicleStateMessage extends Marshallable[VehicleStateMessage] { + /** + * Calculate common orientation from little-endian bit data. + * @see `Angular.codec_roll` + * @see `Angular.codec_pitch` + * @see `Angular.codec_yaw` + */ + private val codec_orient : Codec[Vector3] = ( + ("roll" | Angular.codec_roll(10)) :: + ("pitch" | Angular.codec_pitch(10)) :: + ("yaw" | Angular.codec_yaw(10, 90f)) + ).as[Vector3] + implicit val codec : Codec[VehicleStateMessage] = ( ("vehicle_guid" | PlanetSideGUID.codec) :: ("unk1" | uintL(3)) :: ("pos" | Vector3.codec_pos) :: - ("roll" | newcodecs.q_float(0.0f, 360.0f, 10)) :: - ("pitch" | newcodecs.q_float(360.0f, 0.0f, 10)) :: - ("yaw" | newcodecs.q_float(360.0f, 0.0f, 10)) :: + ("ang" | codec_orient) :: optional(bool, "vel" | Vector3.codec_vel) :: optional(bool, "unk2" | uintL(5)) :: ("unk3" | uintL(7)) :: @@ -69,27 +67,5 @@ object VehicleStateMessage extends Marshallable[VehicleStateMessage] { ("wheel_direction" | uintL(5)) :: ("int5" | bool) :: ("int6" | bool) - ).xmap[VehicleStateMessage] ( - { - case guid :: u1 :: pos :: roll :: pitch :: yaw :: vel :: u2 :: u3 :: u4 :: wheel :: u5 :: u6 :: HNil => - var northCorrectedYaw : Float = yaw + 90f - if(northCorrectedYaw > 360f) { - northCorrectedYaw = northCorrectedYaw - 360f - } - VehicleStateMessage(guid, u1, pos, roll, pitch, northCorrectedYaw, vel, u2, u3, u4, wheel, u5, u6) - }, - - { - case VehicleStateMessage(guid, u1, pos, roll, pitch, yaw, vel, u2, u3, u4, wheel, u5, u6) => - var northCorrectedYaw : Float = yaw - 90f - //TODO this invites imprecision - while(northCorrectedYaw < 0f) { - northCorrectedYaw = 360f + northCorrectedYaw - } - if(northCorrectedYaw > 360f) { - northCorrectedYaw = northCorrectedYaw % 360f - } - guid :: u1 :: pos :: roll :: pitch :: northCorrectedYaw :: vel :: u2 :: u3 :: u4 :: wheel :: u5 :: u6 :: HNil - } - ) + ).as[VehicleStateMessage] } diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterAppearanceData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterAppearanceData.scala index 85a01340..66380b62 100644 --- a/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterAppearanceData.scala +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterAppearanceData.scala @@ -2,7 +2,7 @@ package net.psforever.packet.game.objectcreate import net.psforever.packet.{Marshallable, PacketHelpers} -import net.psforever.types.{CharacterGender, ExoSuitType, GrenadeState, PlanetSideEmpire} +import net.psforever.types._ import scodec.{Attempt, Codec, Err} import scodec.codecs._ import shapeless.{::, HNil} @@ -74,8 +74,10 @@ final case class BasicCharacterData(name : String, * if the option is selected, allies with see either "[`outfit_name`]" or "{No Outfit}" under the player's name * @param outfit_logo the decal seen on the player's exo-suit (and beret and cap) associated with the player's outfit; * if there is a variable color for that decal, the faction-appropriate one is selected - * @param facingPitch the angle with respect to the sky and the ground towards which the avatar is looking - * @param facingYawUpper the angle of the avatar's upper body with respect to its forward-facing direction + * @param facingPitch a "pitch" angle + * @param facingYawUpper a "yaw" angle that represents the angle of the avatar's upper body with respect to its forward-facing direction; + * this number is normally 0 for forward facing; + * the range is limited between approximately 61 degrees of center turned to left or right * @param lfs this player is looking for a squad; * all allies will see the phrase "[Looking for Squad]" under the player's name * @param is_cloaking avatar is cloaked by virtue of an Infiltration Suit @@ -101,8 +103,8 @@ final case class CharacterAppearanceData(pos : PlacementData, outfit_name : String, outfit_logo : Int, backpack : Boolean, - facingPitch : Int, - facingYawUpper : Int, + facingPitch : Float, + facingYawUpper : Float, lfs : Boolean, grenade_state : GrenadeState.Value, is_cloaking : Boolean, @@ -168,8 +170,8 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] { ignore(1) :: //unknown ("backpack" | bool) :: //requires alt_model flag (does NOT require health == 0) bool :: //stream misalignment when set - ("facingPitch" | uint8L) :: - ("facingYawUpper" | uint8L) :: + ("facingPitch" | Angular.codec_pitch) :: + ("facingYawUpper" | Angular.codec_yaw(0f)) :: ignore(1) :: //unknown conditional(alt_model, bool) :: //alt_model flag adds a bit before lfs ignore(1) :: //an alternate lfs? diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/PlacementData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/PlacementData.scala index 51df840a..4865b986 100644 --- a/common/src/main/scala/net/psforever/packet/game/objectcreate/PlacementData.scala +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/PlacementData.scala @@ -2,22 +2,19 @@ package net.psforever.packet.game.objectcreate import net.psforever.packet.Marshallable -import net.psforever.types.Vector3 +import net.psforever.types.{Angular, Vector3} import scodec.codecs._ import scodec.Codec +import shapeless.{::, HNil} /** * A specific location and heading in game world coordinates and game world measurements. * @param coord the xyz-coordinate location in the world - * @param roll the amount of roll that affects orientation - * @param pitch the amount of pitch that affects orientation - * @param yaw the amount of yaw that affects orientation + * @param orient the ijk-orientation around the object's center * @param vel optional movement data (that occurs upon placement) */ final case class PlacementData(coord : Vector3, - roll : Int, - pitch : Int, - yaw : Int, + orient : Vector3, vel : Option[Vector3] = None ) extends StreamBitSize { override def bitsize : Long = { @@ -35,7 +32,7 @@ object PlacementData extends Marshallable[PlacementData] { * @return a `PlacementData` object */ def apply(x : Float, y : Float, z : Float) : PlacementData = - new PlacementData(Vector3(x, y, z), 0, 0, 0) + new PlacementData(Vector3(x, y, z), Vector3(0f,0f,0f)) /** * An abbreviated constructor for creating `PlacementData`, ignoring the `Vector3` for position data, supplying other important fields. @@ -47,8 +44,8 @@ object PlacementData extends Marshallable[PlacementData] { * @param yaw the amount of yaw that affects orientation * @return a `PlacementData` object */ - def apply(x : Float, y : Float, z : Float, roll : Int, pitch : Int, yaw : Int) : PlacementData = - new PlacementData(Vector3(x, y, z), roll, pitch, yaw) + def apply(x : Float, y : Float, z : Float, roll : Float, pitch : Float, yaw : Float) : PlacementData = + new PlacementData(Vector3(x, y, z), Vector3(roll, pitch, yaw)) /** * An abbreviated constructor for creating `PlacementData`, ignoring the `Vector3` for position data, supplying all other fields. @@ -61,14 +58,23 @@ object PlacementData extends Marshallable[PlacementData] { * @param vel optional movement data that occurs upon placement * @return a `PlacementData` object */ - def apply(x : Float, y : Float, z : Float, roll : Int, pitch : Int, yaw : Int, vel : Vector3) : PlacementData = - new PlacementData(Vector3(x, y, z), roll, pitch, yaw, Some(vel)) + def apply(x : Float, y : Float, z : Float, roll : Float, pitch : Float, yaw : Float, vel : Vector3) : PlacementData = + new PlacementData(Vector3(x, y, z), Vector3(roll, pitch, yaw), Some(vel)) implicit val codec : Codec[PlacementData] = ( ("coord" | Vector3.codec_pos) :: - ("roll" | uint8L) :: - ("pitch" | uint8L) :: - ("yaw" | uint8L) :: + ("roll" | Angular.codec_roll) :: + ("pitch" | Angular.codec_pitch) :: + ("yaw" | Angular.codec_yaw()) :: optional(bool, "vel" | Vector3.codec_vel) - ).as[PlacementData] + ).xmap[PlacementData] ( + { + case xyz :: i :: j :: k :: vel :: HNil => + PlacementData(xyz, Vector3(i, j, k), vel) + }, + { + case PlacementData(xyz, Vector3(i, j, k), vel) => + xyz :: i :: j :: k :: vel :: HNil + } + ) } diff --git a/common/src/main/scala/net/psforever/types/Angular.scala b/common/src/main/scala/net/psforever/types/Angular.scala new file mode 100644 index 00000000..91919eeb --- /dev/null +++ b/common/src/main/scala/net/psforever/types/Angular.scala @@ -0,0 +1,103 @@ +// Copyright (c) 2017 PSForever +package net.psforever.types + +import net.psforever.newcodecs.newcodecs +import scodec.Codec +import scodec.codecs.ignore +import shapeless.{::, HNil} + +/** + * A series of `Codec`s designed to work with convert between 8-bit angle values in the packets and `Float` numbers. + * As far as the data is concerned, the first bit appears to be ignored when it comes to the actual angle measurement. + * The latter seven bits map between 0 to 360 perfectly (according to the game). + */ +object Angular { + //roll + val codec_roll : Codec[Float] = ( + ignore(1) :: + codec_roll(7) + ).xmap[Float] ( + { + case _ :: roll :: HNil => + roll + }, + { + case roll : Float => + () :: roll :: HNil + } + ) + + def codec_roll(bits : Int) : Codec[Float] = newcodecs.q_float(0.0f, 360.0f, bits) + + //pitch + val codec_pitch : Codec[Float] = ( + ignore(1) :: + codec_pitch(7) + ).xmap[Float] ( + { + case _ :: pitch :: HNil => + pitch + }, + { + case pitch : Float => + () :: pitch :: HNil + } + ) + + def codec_pitch(bits : Int) : Codec[Float] = newcodecs.q_float(360.0f, 0.0f, bits).xmap[Float] ( + { + case pitch => + decodeCorrectedAngle(pitch) + }, + { + case pitch : Float => + encodeCorrectedAngle(pitch) + } + ) + + //yaw + def codec_yaw(North : Float = 90.0f) : Codec[Float] = ( + ignore(1) :: + codec_yaw(7, North) + ).xmap[Float] ( + { + case _ :: yaw :: HNil => + yaw + }, + { + case yaw : Float => + () :: yaw :: HNil + } + ) + + def codec_yaw(bits : Int, North : Float) : Codec[Float] = newcodecs.q_float(360.0f, 0.0f, bits).xmap[Float] ( + { + case yaw => + decodeCorrectedAngle(yaw, North) + }, + { + case yaw : Float => + encodeCorrectedAngle(yaw, North) + } + ) + + //support + def decodeCorrectedAngle(angle : Float, correction : Float = 0f) : Float = { + var correctedAng : Float = angle + correction + if(correctedAng >= 360f) { + correctedAng = correctedAng - 360f + } + correctedAng + } + + def encodeCorrectedAngle(angle : Float, correction : Float = 0f) : Float = { + var correctedAng : Float = angle - correction + if(correctedAng <= 0f) { + correctedAng = 360f + correctedAng % 360f + } + else if(correctedAng > 360f) { + correctedAng = correctedAng % 360f + } + correctedAng + } +} diff --git a/common/src/main/scala/net/psforever/types/Vector3.scala b/common/src/main/scala/net/psforever/types/Vector3.scala index 312c9803..ac2b92fe 100644 --- a/common/src/main/scala/net/psforever/types/Vector3.scala +++ b/common/src/main/scala/net/psforever/types/Vector3.scala @@ -4,6 +4,7 @@ package net.psforever.types import net.psforever.newcodecs._ import scodec.Codec import scodec.codecs._ +import shapeless.{::, HNil} final case class Vector3(x : Float, y : Float, diff --git a/common/src/test/scala/CodecTest.scala b/common/src/test/scala/CodecTest.scala index 59c0cb3a..f213848a 100644 --- a/common/src/test/scala/CodecTest.scala +++ b/common/src/test/scala/CodecTest.scala @@ -34,23 +34,190 @@ class CodecTest extends Specification { } "Vector3" should { - val string_pos = hex"6E2D762222B616" - val string_vel = hex"857D4E0FFFC0" + "position" should { + val string_pos = hex"6E2D762222B616" - "decode position" in { - Vector3.codec_pos.decode(string_pos.bits).require.value mustEqual Vector3(3674.859375f, 1092.7656f, 90.84375f) + "decode" in { + Vector3.codec_pos.decode(string_pos.bits).require.value mustEqual Vector3(3674.859375f, 1092.7656f, 90.84375f) + } + + "encode" in { + Vector3.codec_pos.encode(Vector3(3674.859375f, 1092.7656f, 90.84375f)).require.bytes mustEqual string_pos + } } - "encode position" in { - Vector3.codec_pos.encode(Vector3(3674.859375f, 1092.7656f, 90.84375f)).require.bytes mustEqual string_pos + "velocity" should { + val string_vel = hex"857D4E0FFFC0" + + "decode" in { + Vector3.codec_vel.decode(string_vel.bits).require.value mustEqual Vector3(-3.84375f, 2.59375f, 255.96875f) + } + + "encode" in { + Vector3.codec_vel.encode(Vector3(-3.84375f, 2.59375f, 255.96875f)).require.bytes mustEqual string_vel + } + } + } + + "Angular" should { + "roll" should { + val string_roll_0 = hex"00" + val string_roll_90 = hex"20" + val string_roll_180 = hex"40" + val string_roll_270 = hex"60" + + "decode (0)" in { + Angular.codec_roll.decode(string_roll_0.bits).require.value mustEqual 0f + } + + "decode (90)" in { + Angular.codec_roll.decode(string_roll_90.bits).require.value mustEqual 90f + } + + "decode (180)" in { + Angular.codec_roll.decode(string_roll_180.bits).require.value mustEqual 180f + } + + "decode (270)" in { + Angular.codec_roll.decode(string_roll_270.bits).require.value mustEqual 270f + } + + "encode (0)" in { + Angular.codec_roll.encode(0f).require.bytes mustEqual string_roll_0 + } + + "encode (90)" in { + Angular.codec_roll.encode(90f).require.bytes mustEqual string_roll_90 + } + + "encode (180)" in { + Angular.codec_roll.encode(180f).require.bytes mustEqual string_roll_180 + } + + "encode (270)" in { + Angular.codec_roll.encode(270f).require.bytes mustEqual string_roll_270 + } } - "decode velocity" in { - Vector3.codec_vel.decode(string_vel.bits).require.value mustEqual Vector3(-3.84375f, 2.59375f, 255.96875f) + "pitch" should { + val string_pitch_0 = hex"00" + val string_pitch_90 = hex"60" + val string_pitch_180 = hex"40" + val string_pitch_270 = hex"20" + + "decode (0)" in { + Angular.codec_pitch.decode(string_pitch_0.bits).require.value mustEqual 0f + } + + "decode (90)" in { + Angular.codec_pitch.decode(string_pitch_90.bits).require.value mustEqual 90f + } + + "decode (180)" in { + Angular.codec_pitch.decode(string_pitch_180.bits).require.value mustEqual 180f + } + + "decode (270)" in { + Angular.codec_pitch.decode(string_pitch_270.bits).require.value mustEqual 270f + } + + "encode (0)" in { + Angular.codec_pitch.encode(0f).require.bytes mustEqual string_pitch_0 + } + + "encode (90)" in { + Angular.codec_pitch.encode(90f).require.bytes mustEqual string_pitch_90 + } + + "encode (180)" in { + Angular.codec_pitch.encode(180f).require.bytes mustEqual string_pitch_180 + } + + "encode (270)" in { + Angular.codec_pitch.encode(270f).require.bytes mustEqual string_pitch_270 + } } - "encode velocity" in { - Vector3.codec_vel.encode(Vector3(-3.84375f, 2.59375f, 255.96875f)).require.bytes mustEqual string_vel + "yaw, normal" should { + val string_pitch_0 = hex"00" + val string_pitch_90 = hex"60" + val string_pitch_180 = hex"40" + val string_pitch_270 = hex"20" + val string_yaw_0 = hex"20" + val string_yaw_90 = hex"00" + val string_yaw_180 = hex"60" + val string_yaw_270 = hex"40" + + "decode (0)" in { + Angular.codec_yaw(0f).decode(string_yaw_0.bits).require.value mustEqual 270f + } + + "decode (90)" in { + Angular.codec_yaw(0f).decode(string_yaw_90.bits).require.value mustEqual 0f + } + + "decode (180)" in { + Angular.codec_yaw(0f).decode(string_yaw_180.bits).require.value mustEqual 90f + } + + "decode (270)" in { + Angular.codec_yaw(0f).decode(string_yaw_270.bits).require.value mustEqual 180f + } + + "encode (0)" in { + Angular.codec_yaw(0f).encode(0f).require.bytes mustEqual string_pitch_0 + } + + "encode (90)" in { + Angular.codec_yaw(0f).encode(90f).require.bytes mustEqual string_pitch_90 + } + + "encode (180)" in { + Angular.codec_yaw(0f).encode(180f).require.bytes mustEqual string_pitch_180 + } + + "encode (270)" in { + Angular.codec_yaw(0f).encode(270f).require.bytes mustEqual string_pitch_270 + } + } + + "yaw, North-corrected" should { + val string_yaw_0 = hex"20" + val string_yaw_90 = hex"00" + val string_yaw_180 = hex"60" + val string_yaw_270 = hex"40" + + "decode (0)" in { + Angular.codec_yaw().decode(string_yaw_0.bits).require.value mustEqual 0f + } + + "decode (90)" in { + Angular.codec_yaw().decode(string_yaw_90.bits).require.value mustEqual 90f + } + + "decode (180)" in { + Angular.codec_yaw().decode(string_yaw_180.bits).require.value mustEqual 180f + } + + "decode (270)" in { + Angular.codec_yaw().decode(string_yaw_270.bits).require.value mustEqual 270f + } + + "encode (0)" in { + Angular.codec_yaw().encode(0f).require.bytes mustEqual string_yaw_0 + } + + "encode (90)" in { + Angular.codec_yaw().encode(90f).require.bytes mustEqual string_yaw_90 + } + + "encode (180)" in { + Angular.codec_yaw().encode(180f).require.bytes mustEqual string_yaw_180 + } + + "encode (270)" in { + Angular.codec_yaw().encode(270f).require.bytes mustEqual string_yaw_270 + } } } } diff --git a/common/src/test/scala/game/ChildObjectStateMessageTest.scala b/common/src/test/scala/game/ChildObjectStateMessageTest.scala index 00cba45d..12bb83e8 100644 --- a/common/src/test/scala/game/ChildObjectStateMessageTest.scala +++ b/common/src/test/scala/game/ChildObjectStateMessageTest.scala @@ -13,15 +13,15 @@ class ChildObjectStateMessageTest extends Specification { PacketCoding.DecodePacket(string).require match { case ChildObjectStateMessage(object_guid, pitch, yaw) => object_guid mustEqual PlanetSideGUID(2916) - pitch mustEqual 6 - yaw mustEqual 71 + pitch mustEqual 343.125f + yaw mustEqual 160.3125f case _ => ko } } "encode" in { - val msg = ChildObjectStateMessage(PlanetSideGUID(2916), 6, 71) + val msg = ChildObjectStateMessage(PlanetSideGUID(2916), 343.125f, 160.3125f) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string diff --git a/common/src/test/scala/game/ObjectCreateDetailedMessageTest.scala b/common/src/test/scala/game/ObjectCreateDetailedMessageTest.scala index 061db99f..f818ac40 100644 --- a/common/src/test/scala/game/ObjectCreateDetailedMessageTest.scala +++ b/common/src/test/scala/game/ObjectCreateDetailedMessageTest.scala @@ -180,9 +180,9 @@ class ObjectCreateDetailedMessageTest extends Specification { char.appearance.pos.coord.x mustEqual 3674.8438f char.appearance.pos.coord.y mustEqual 2726.789f char.appearance.pos.coord.z mustEqual 91.15625f - char.appearance.pos.roll mustEqual 0 - char.appearance.pos.pitch mustEqual 0 - char.appearance.pos.yaw mustEqual 19 + char.appearance.pos.orient.x mustEqual 0 + char.appearance.pos.orient.y mustEqual 0f + char.appearance.pos.orient.z mustEqual 36.5625f char.appearance.basic_appearance.name mustEqual "IlllIIIlllIlIllIlllIllI" char.appearance.basic_appearance.faction mustEqual PlanetSideEmpire.VS char.appearance.basic_appearance.sex mustEqual CharacterGender.Female @@ -195,8 +195,8 @@ class ObjectCreateDetailedMessageTest extends Specification { char.appearance.outfit_name mustEqual "" char.appearance.outfit_logo mustEqual 0 char.appearance.backpack mustEqual false - char.appearance.facingPitch mustEqual 127 - char.appearance.facingYawUpper mustEqual 181 + char.appearance.facingPitch mustEqual 2.8125f + char.appearance.facingYawUpper mustEqual 210.9375f char.appearance.lfs mustEqual true char.appearance.grenade_state mustEqual GrenadeState.None char.appearance.is_cloaking mustEqual false @@ -367,8 +367,7 @@ class ObjectCreateDetailedMessageTest extends Specification { val app = CharacterAppearanceData( PlacementData( Vector3(3674.8438f, 2726.789f, 91.15625f), - 0, 0, - 19 + Vector3(0f, 0f, 36.5625f) ), BasicCharacterData( "IlllIIIlllIlIllIlllIllI", @@ -384,7 +383,7 @@ class ObjectCreateDetailedMessageTest extends Specification { "", 0, false, - 127, 181, + 2.8125f, 210.9375f, true, GrenadeState.None, false, @@ -422,7 +421,8 @@ class ObjectCreateDetailedMessageTest extends Specification { val ori_bitv = string_testchar.toBitVector pkt_bitv.take(153) mustEqual ori_bitv.take(153) //skip 1 pkt_bitv.drop(154).take(422) mustEqual ori_bitv.drop(154).take(422) //skip 126 - pkt_bitv.drop(702) mustEqual ori_bitv.drop(702) + pkt_bitv.drop(702).take(29) mustEqual ori_bitv.drop(702).take(29) //skip 1 + pkt_bitv.drop(732) mustEqual ori_bitv.drop(732) //TODO work on DetailedCharacterData to make this pass as a single stream } } diff --git a/common/src/test/scala/game/ObjectCreateMessageTest.scala b/common/src/test/scala/game/ObjectCreateMessageTest.scala index e3cc4969..7b779f8b 100644 --- a/common/src/test/scala/game/ObjectCreateMessageTest.scala +++ b/common/src/test/scala/game/ObjectCreateMessageTest.scala @@ -48,9 +48,9 @@ class ObjectCreateMessageTest extends Specification { projectile.pos.coord.x mustEqual 4644.5938f projectile.pos.coord.y mustEqual 5472.0938f projectile.pos.coord.z mustEqual 82.375f - projectile.pos.roll mustEqual 0 - projectile.pos.pitch mustEqual 245 - projectile.pos.yaw mustEqual 227 + projectile.pos.orient.x mustEqual 0f + projectile.pos.orient.y mustEqual 30.9375f + projectile.pos.orient.z mustEqual 171.5625f projectile.unk1 mustEqual 0 projectile.unk2 mustEqual TrackedProjectileData.striker_missile_targetting_projectile_data case _ => @@ -88,9 +88,9 @@ class ObjectCreateMessageTest extends Specification { drop.pos.coord.x mustEqual 4579.3438f drop.pos.coord.y mustEqual 5615.0703f drop.pos.coord.z mustEqual 72.953125f - drop.pos.pitch mustEqual 0 - drop.pos.roll mustEqual 0 - drop.pos.yaw mustEqual 125 + drop.pos.orient.x mustEqual 0f + drop.pos.orient.y mustEqual 0f + drop.pos.orient.z mustEqual 98.4375f drop.obj.isInstanceOf[CommonTerminalData] mustEqual true val term = drop.obj.asInstanceOf[CommonTerminalData] term.faction mustEqual PlanetSideEmpire.NC @@ -249,9 +249,9 @@ class ObjectCreateMessageTest extends Specification { flag.pos.coord.x mustEqual 3912.0312f flag.pos.coord.y mustEqual 5169.4375f flag.pos.coord.z mustEqual 59.96875f - flag.pos.roll mustEqual 0 - flag.pos.pitch mustEqual 0 - flag.pos.yaw mustEqual 15 + flag.pos.orient.x mustEqual 0f + flag.pos.orient.y mustEqual 0f + flag.pos.orient.z mustEqual 47.8125f flag.faction mustEqual PlanetSideEmpire.NC flag.unk1 mustEqual 21 flag.unk2 mustEqual 4 @@ -274,9 +274,9 @@ class ObjectCreateMessageTest extends Specification { drop.pos.coord.x mustEqual 4708.461f drop.pos.coord.y mustEqual 5547.539f drop.pos.coord.z mustEqual 72.703125f - drop.pos.roll mustEqual 0 - drop.pos.pitch mustEqual 0 - drop.pos.yaw mustEqual 91 + drop.pos.orient.x mustEqual 0f + drop.pos.orient.y mustEqual 0f + drop.pos.orient.z mustEqual 194.0625f drop.obj.isInstanceOf[ACEData] mustEqual true val ace = drop.obj.asInstanceOf[ACEData] ace.unk1 mustEqual 8 @@ -299,9 +299,9 @@ class ObjectCreateMessageTest extends Specification { drop.pos.coord.x mustEqual 4777.633f drop.pos.coord.y mustEqual 5485.4062f drop.pos.coord.z mustEqual 85.8125f - drop.pos.roll mustEqual 0 - drop.pos.pitch mustEqual 0 - drop.pos.yaw mustEqual 27 + drop.pos.orient.x mustEqual 0f + drop.pos.orient.y mustEqual 0f + drop.pos.orient.z mustEqual 14.0625f drop.obj.isInstanceOf[CommandDetonaterData] mustEqual true case _ => ko @@ -321,9 +321,9 @@ class ObjectCreateMessageTest extends Specification { drop.pos.coord.x mustEqual 4684.7344f drop.pos.coord.y mustEqual 5547.4844f drop.pos.coord.z mustEqual 83.765625f - drop.pos.roll mustEqual 0 - drop.pos.pitch mustEqual 0 - drop.pos.yaw mustEqual 89 + drop.pos.orient.x mustEqual 0f + drop.pos.orient.y mustEqual 0f + drop.pos.orient.z mustEqual 199.6875f drop.obj.isInstanceOf[AmmoBoxData] mustEqual true val box = drop.obj.asInstanceOf[AmmoBoxData] box.unk mustEqual 0 @@ -345,9 +345,9 @@ class ObjectCreateMessageTest extends Specification { drop.pos.coord.x mustEqual 4691.1953f drop.pos.coord.y mustEqual 5537.039f drop.pos.coord.z mustEqual 65.484375f - drop.pos.roll mustEqual 0 - drop.pos.pitch mustEqual 0 - drop.pos.yaw mustEqual 32 + drop.pos.orient.x mustEqual 0f + drop.pos.orient.y mustEqual 0f + drop.pos.orient.z mustEqual 0f drop.obj.isInstanceOf[WeaponData] mustEqual true val wep = drop.obj.asInstanceOf[WeaponData] wep.unk1 mustEqual 4 @@ -377,9 +377,9 @@ class ObjectCreateMessageTest extends Specification { drop.pos.coord.x mustEqual 4789.133f drop.pos.coord.y mustEqual 5522.3125f drop.pos.coord.z mustEqual 72.3125f - drop.pos.roll mustEqual 0 - drop.pos.pitch mustEqual 0 - drop.pos.yaw mustEqual 51 + drop.pos.orient.x mustEqual 0f + drop.pos.orient.y mustEqual 0f + drop.pos.orient.z mustEqual 306.5625f drop.obj.isInstanceOf[WeaponData] mustEqual true val wep = drop.obj.asInstanceOf[WeaponData] wep.unk1 mustEqual 2 @@ -417,9 +417,9 @@ class ObjectCreateMessageTest extends Specification { dropped.pos.coord.x mustEqual 4675.039f dropped.pos.coord.y mustEqual 5506.953f dropped.pos.coord.z mustEqual 72.703125f - dropped.pos.roll mustEqual 0 - dropped.pos.pitch mustEqual 0 - dropped.pos.yaw mustEqual 78 + dropped.pos.orient.x mustEqual 0f + dropped.pos.orient.y mustEqual 0f + dropped.pos.orient.z mustEqual 230.625f dropped.obj.isInstanceOf[REKData] mustEqual true val rek = dropped.obj.asInstanceOf[REKData] rek.unk1 mustEqual 8 @@ -443,9 +443,9 @@ class ObjectCreateMessageTest extends Specification { boomer.deploy.pos.coord.x mustEqual 4704.172f boomer.deploy.pos.coord.y mustEqual 5546.4375f boomer.deploy.pos.coord.z mustEqual 82.234375f - boomer.deploy.pos.roll mustEqual 0 - boomer.deploy.pos.pitch mustEqual 0 - boomer.deploy.pos.yaw mustEqual 63 + boomer.deploy.pos.orient.x mustEqual 0f + boomer.deploy.pos.orient.y mustEqual 0f + boomer.deploy.pos.orient.z mustEqual 272.8125f boomer.deploy.unk mustEqual 0 boomer.deploy.player_guid mustEqual PlanetSideGUID(4145) case _ => @@ -466,9 +466,9 @@ class ObjectCreateMessageTest extends Specification { turret.deploy.pos.coord.x mustEqual 4577.7812f turret.deploy.pos.coord.y mustEqual 5624.828f turret.deploy.pos.coord.z mustEqual 72.046875f - turret.deploy.pos.roll mustEqual 0 - turret.deploy.pos.pitch mustEqual 127 - turret.deploy.pos.yaw mustEqual 66 + turret.deploy.pos.orient.x mustEqual 0f + turret.deploy.pos.orient.y mustEqual 2.8125f + turret.deploy.pos.orient.z mustEqual 264.375f turret.deploy.faction mustEqual PlanetSideEmpire.NC turret.deploy.unk mustEqual 12 turret.deploy.player_guid mustEqual PlanetSideGUID(3871) @@ -492,9 +492,9 @@ class ObjectCreateMessageTest extends Specification { turret.deploy.pos.coord.x mustEqual 4527.633f turret.deploy.pos.coord.y mustEqual 6271.3594f turret.deploy.pos.coord.z mustEqual 70.265625f - turret.deploy.pos.roll mustEqual 0 - turret.deploy.pos.pitch mustEqual 0 - turret.deploy.pos.yaw mustEqual 105 + turret.deploy.pos.orient.x mustEqual 0f + turret.deploy.pos.orient.y mustEqual 0f + turret.deploy.pos.orient.z mustEqual 154.6875f turret.deploy.faction mustEqual PlanetSideEmpire.VS turret.deploy.unk mustEqual 4 turret.deploy.player_guid mustEqual PlanetSideGUID(4232) @@ -533,9 +533,9 @@ class ObjectCreateMessageTest extends Specification { trap.deploy.pos.coord.x mustEqual 3572.4453f trap.deploy.pos.coord.y mustEqual 3277.9766f trap.deploy.pos.coord.z mustEqual 114.0f - trap.deploy.pos.roll mustEqual 0 - trap.deploy.pos.pitch mustEqual 0 - trap.deploy.pos.yaw mustEqual 0 + trap.deploy.pos.orient.x mustEqual 0f + trap.deploy.pos.orient.y mustEqual 0f + trap.deploy.pos.orient.z mustEqual 90.0f trap.deploy.faction mustEqual PlanetSideEmpire.VS trap.deploy.unk mustEqual 4 trap.health mustEqual 255 @@ -558,9 +558,9 @@ class ObjectCreateMessageTest extends Specification { aegis.deploy.pos.coord.x mustEqual 3571.2266f aegis.deploy.pos.coord.y mustEqual 3278.0938f aegis.deploy.pos.coord.z mustEqual 114.0f - aegis.deploy.pos.roll mustEqual 0 - aegis.deploy.pos.pitch mustEqual 0 - aegis.deploy.pos.yaw mustEqual 0 + aegis.deploy.pos.orient.x mustEqual 0f + aegis.deploy.pos.orient.y mustEqual 0f + aegis.deploy.pos.orient.z mustEqual 90.0f aegis.deploy.faction mustEqual PlanetSideEmpire.VS aegis.deploy.unk mustEqual 4 aegis.health mustEqual 255 @@ -583,9 +583,9 @@ class ObjectCreateMessageTest extends Specification { omft.deploy.pos.coord.x mustEqual 3567.1406f omft.deploy.pos.coord.y mustEqual 2988.0078f omft.deploy.pos.coord.z mustEqual 71.84375f - omft.deploy.pos.roll mustEqual 0 - omft.deploy.pos.pitch mustEqual 0 - omft.deploy.pos.yaw mustEqual 94 + omft.deploy.pos.orient.x mustEqual 0f + omft.deploy.pos.orient.y mustEqual 0f + omft.deploy.pos.orient.z mustEqual 185.625f omft.deploy.faction mustEqual PlanetSideEmpire.VS omft.deploy.unk mustEqual 4 omft.deploy.player_guid mustEqual PlanetSideGUID(2502) @@ -666,9 +666,9 @@ class ObjectCreateMessageTest extends Specification { pc.appearance.pos.coord.x mustEqual 3674.8438f pc.appearance.pos.coord.y mustEqual 2726.789f pc.appearance.pos.coord.z mustEqual 91.15625f - pc.appearance.pos.roll mustEqual 0 - pc.appearance.pos.pitch mustEqual 0 - pc.appearance.pos.yaw mustEqual 9 + pc.appearance.pos.orient.x mustEqual 0f + pc.appearance.pos.orient.y mustEqual 0f + pc.appearance.pos.orient.z mustEqual 64.6875f pc.appearance.pos.vel.isDefined mustEqual true pc.appearance.pos.vel.get.x mustEqual 1.4375f pc.appearance.pos.vel.get.y mustEqual -0.4375f @@ -684,7 +684,7 @@ class ObjectCreateMessageTest extends Specification { pc.appearance.exosuit mustEqual ExoSuitType.Reinforced pc.appearance.outfit_name mustEqual "Black Beret Armoured Corps" pc.appearance.outfit_logo mustEqual 23 - pc.appearance.facingPitch mustEqual 7 + pc.appearance.facingPitch mustEqual 340.3125f pc.appearance.facingYawUpper mustEqual 0 pc.appearance.lfs mustEqual false pc.appearance.grenade_state mustEqual GrenadeState.None @@ -765,9 +765,9 @@ class ObjectCreateMessageTest extends Specification { pc.appearance.pos.coord.x mustEqual 4629.8906f pc.appearance.pos.coord.y mustEqual 6316.4453f pc.appearance.pos.coord.z mustEqual 54.734375f - pc.appearance.pos.roll mustEqual 0 - pc.appearance.pos.pitch mustEqual 0 - pc.appearance.pos.yaw mustEqual 115 + pc.appearance.pos.orient.x mustEqual 0f + pc.appearance.pos.orient.y mustEqual 0f + pc.appearance.pos.orient.z mustEqual 126.5625f pc.appearance.pos.vel.isDefined mustEqual false pc.appearance.basic_appearance.name mustEqual "Angello" pc.appearance.basic_appearance.faction mustEqual PlanetSideEmpire.VS @@ -781,7 +781,7 @@ class ObjectCreateMessageTest extends Specification { pc.appearance.outfit_name mustEqual "Original District" pc.appearance.outfit_logo mustEqual 23 pc.appearance.facingPitch mustEqual 0 - pc.appearance.facingYawUpper mustEqual 192 + pc.appearance.facingYawUpper mustEqual 180.0f pc.appearance.lfs mustEqual false pc.appearance.grenade_state mustEqual GrenadeState.None pc.appearance.is_cloaking mustEqual false @@ -811,13 +811,15 @@ class ObjectCreateMessageTest extends Specification { "encode (striker projectile)" in { val obj = TrackedProjectileData.striker( - PlacementData(4644.5938f, 5472.0938f, 82.375f, 0, 245, 227), + PlacementData(4644.5938f, 5472.0938f, 82.375f, 0f, 30.9375f, 171.5625f), 0 ) val msg = ObjectCreateMessage(ObjectClass.striker_missile_targeting_projectile, PlanetSideGUID(40192), obj) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector - pkt mustEqual string_striker_projectile + pkt.toBitVector.take(132) mustEqual string_striker_projectile.toBitVector.take(132) + pkt.toBitVector.drop(133).take(7) mustEqual string_striker_projectile.toBitVector.drop(133).take(7) + pkt.toBitVector.drop(141) mustEqual string_striker_projectile.toBitVector.drop(141) } "encode (implant interface)" in { @@ -830,7 +832,7 @@ class ObjectCreateMessageTest extends Specification { "encode (order terminal a)" in { val obj = DroppedItemData( - PlacementData(4579.3438f, 5615.0703f, 72.953125f, 0, 0, 125), + PlacementData(4579.3438f, 5615.0703f, 72.953125f, 0f, 0f, 98.4375f), CommonTerminalData(PlanetSideEmpire.NC) ) val msg = ObjectCreateMessage(ObjectClass.order_terminala, PlanetSideGUID(3827), obj) @@ -893,7 +895,7 @@ class ObjectCreateMessageTest extends Specification { } "encode (capture flag)" in { - val obj = CaptureFlagData(PlacementData(3912.0312f, 5169.4375f, 59.96875f, 0, 0, 15), PlanetSideEmpire.NC, 21, 4, 2838, 9) + val obj = CaptureFlagData(PlacementData(3912.0312f, 5169.4375f, 59.96875f, 0f, 0f, 47.8125f), PlanetSideEmpire.NC, 21, 4, 2838, 9) val msg = ObjectCreateMessage(ObjectClass.capture_flag, PlanetSideGUID(4330), obj) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector @@ -902,7 +904,7 @@ class ObjectCreateMessageTest extends Specification { "encode (ace, dropped)" in { val obj = DroppedItemData( - PlacementData(Vector3(4708.461f, 5547.539f, 72.703125f), 0, 0, 91), + PlacementData(4708.461f, 5547.539f, 72.703125f, 0f, 0f, 194.0625f), ACEData(8, 8) ) val msg = ObjectCreateMessage(ObjectClass.ace, PlanetSideGUID(4388), obj) @@ -913,7 +915,7 @@ class ObjectCreateMessageTest extends Specification { "encode (detonator, dropped)" in { val obj = DroppedItemData( - PlacementData(Vector3(4777.633f, 5485.4062f, 85.8125f), 0, 0, 27), + PlacementData(4777.633f, 5485.4062f, 85.8125f, 0f, 0f, 14.0625f), CommandDetonaterData() ) val msg = ObjectCreateMessage(ObjectClass.command_detonater, PlanetSideGUID(3682), obj) @@ -924,7 +926,7 @@ class ObjectCreateMessageTest extends Specification { "encode (shotgun shells, dropped)" in { val obj = DroppedItemData( - PlacementData(Vector3(4684.7344f, 5547.4844f, 83.765625f), 0, 0, 89), + PlacementData(4684.7344f, 5547.4844f, 83.765625f, 0f, 0f, 199.6875f), AmmoBoxData() ) val msg = ObjectCreateMessage(ObjectClass.shotgun_shell, PlanetSideGUID(3453), obj) @@ -935,7 +937,7 @@ class ObjectCreateMessageTest extends Specification { "encode (lasher, dropped)" in { val obj = DroppedItemData( - PlacementData(Vector3(4691.1953f, 5537.039f, 65.484375f), 0, 0, 32), + PlacementData(4691.1953f, 5537.039f, 65.484375f, 0.0f, 0.0f, 0.0f), WeaponData(4, 0, ObjectClass.energy_cell, PlanetSideGUID(3268), 0, AmmoBoxData()) ) val msg = ObjectCreateMessage(ObjectClass.lasher, PlanetSideGUID(3074), obj) @@ -946,7 +948,7 @@ class ObjectCreateMessageTest extends Specification { "encode (punisher, dropped)" in { val obj = DroppedItemData( - PlacementData(Vector3(4789.133f, 5522.3125f, 72.3125f), 0, 0, 51), + PlacementData(4789.133f, 5522.3125f, 72.3125f, 0f, 0f, 306.5625f), WeaponData(2, 0, 0, AmmoBoxData(ObjectClass.bullet_9mm, PlanetSideGUID(3528), 0, AmmoBoxData()) :: AmmoBoxData(ObjectClass.rocket, PlanetSideGUID(3031), 1, AmmoBoxData()) :: @@ -961,7 +963,7 @@ class ObjectCreateMessageTest extends Specification { "encode (REK, dropped)" in { val obj = DroppedItemData( - PlacementData(Vector3(4675.039f, 5506.953f, 72.703125f), 0, 0, 78), + PlacementData(4675.039f, 5506.953f, 72.703125f, 0f, 0f, 230.625f), REKData(8, 0, 3) ) val msg = ObjectCreateMessage(ObjectClass.remote_electronics_kit, PlanetSideGUID(4355), obj) @@ -973,7 +975,7 @@ class ObjectCreateMessageTest extends Specification { "encode (boomer)" in { val obj = SmallDeployableData( CommonFieldData( - PlacementData(Vector3(4704.172f, 5546.4375f, 82.234375f), 0, 0, 63), + PlacementData(4704.172f, 5546.4375f, 82.234375f, 0f, 0f, 272.8125f), PlanetSideEmpire.TR, 0, PlanetSideGUID(4145) ) ) @@ -986,7 +988,7 @@ class ObjectCreateMessageTest extends Specification { "encode (spitfire, short)" in { val obj = SmallTurretData( CommonFieldData( - PlacementData(Vector3(4577.7812f, 5624.828f, 72.046875f), 0, 127, 66), + PlacementData(4577.7812f, 5624.828f, 72.046875f, 0f, 2.8125f, 264.375f), PlanetSideEmpire.NC, 12, PlanetSideGUID(3871) ), 255 //sets to 0 @@ -1004,7 +1006,7 @@ class ObjectCreateMessageTest extends Specification { "encode (spitfire)" in { val obj = SmallTurretData( CommonFieldData( - PlacementData(Vector3(4527.633f, 6271.3594f, 70.265625f), 0, 0, 105), + PlacementData(4527.633f, 6271.3594f, 70.265625f, 0f, 0f, 154.6875f), PlanetSideEmpire.VS, 4, PlanetSideGUID(4232) ), 255, @@ -1023,7 +1025,7 @@ class ObjectCreateMessageTest extends Specification { "encode (trap)" in { val obj = TRAPData( CommonFieldData( - PlacementData(Vector3(3572.4453f, 3277.9766f, 114.0f), 0, 0, 0), + PlacementData(3572.4453f, 3277.9766f, 114.0f, 0f, 0f, 90.0f), PlanetSideEmpire.VS, 4, PlanetSideGUID(2502) ), 255 @@ -1041,7 +1043,7 @@ class ObjectCreateMessageTest extends Specification { "encode (aegis)" in { val obj = AegisShieldGeneratorData( CommonFieldData( - PlacementData(Vector3(3571.2266f, 3278.0938f, 114.0f), 0, 0, 0), + PlacementData(3571.2266f, 3278.0938f, 114.0f, 0f, 0f, 90.0f), PlanetSideEmpire.VS, 4, PlanetSideGUID(2366) ), 255 @@ -1055,7 +1057,7 @@ class ObjectCreateMessageTest extends Specification { "encode (orion)" in { val obj = OneMannedFieldTurretData( CommonFieldData( - PlacementData(Vector3(3567.1406f, 2988.0078f, 71.84375f), 0, 0, 94), + PlacementData(3567.1406f, 2988.0078f, 71.84375f, 0f, 0f, 185.625f), PlanetSideEmpire.VS, 4, PlanetSideGUID(2502) ), 255, @@ -1091,7 +1093,7 @@ class ObjectCreateMessageTest extends Specification { CharacterAppearanceData( PlacementData( Vector3(3674.8438f, 2726.789f, 91.15625f), - 0, 0, 9, + Vector3(0f, 0f, 64.6875f), Some(Vector3(1.4375f, -0.4375f, 0f)) ), BasicCharacterData( @@ -1108,7 +1110,7 @@ class ObjectCreateMessageTest extends Specification { "Black Beret Armoured Corps", 23, false, - 7, 0, + 340.3125f, 0f, false, GrenadeState.None, false, false, false, @@ -1150,7 +1152,7 @@ class ObjectCreateMessageTest extends Specification { "encode (character, backpack)" in { val obj = CharacterData( CharacterAppearanceData( - PlacementData(4629.8906f, 6316.4453f, 54.734375f, 0, 0, 115), + PlacementData(4629.8906f, 6316.4453f, 54.734375f, 0f, 0f, 126.5625f), BasicCharacterData( "Angello", PlanetSideEmpire.VS, @@ -1165,7 +1167,7 @@ class ObjectCreateMessageTest extends Specification { "Original District", 23, true, //backpack - 0, 192, + 0f, 180.0f, false, GrenadeState.None, false, false, false, @@ -1191,8 +1193,9 @@ class ObjectCreateMessageTest extends Specification { val ori_bitv = string_character_backpack.toBitVector pkt_bitv.take(300) mustEqual ori_bitv.take(300) //skip 2 pkt_bitv.drop(302).take(14) mustEqual ori_bitv.drop(302).take(14) //skip 126 - pkt_bitv.drop(442).take(317) mustEqual ori_bitv.drop(442).take(317) //skip 2 - pkt_bitv.drop(761).take(155) mustEqual ori_bitv.drop(761).take(155) //skip 1 + pkt_bitv.drop(442).take(305) mustEqual ori_bitv.drop(442).take(305) //skip 1 + pkt_bitv.drop(748).take(9) mustEqual ori_bitv.drop(748).take(9) // skip 2 + pkt_bitv.drop(759).take(157) mustEqual ori_bitv.drop(759).take(157) //skip 1 pkt_bitv.drop(917) mustEqual ori_bitv.drop(917) //TODO work on CharacterData to make this pass as a single stream } diff --git a/common/src/test/scala/game/ObjectCreateMessageVehiclesTest.scala b/common/src/test/scala/game/ObjectCreateMessageVehiclesTest.scala index f51aa2d4..cd66e598 100644 --- a/common/src/test/scala/game/ObjectCreateMessageVehiclesTest.scala +++ b/common/src/test/scala/game/ObjectCreateMessageVehiclesTest.scala @@ -33,9 +33,9 @@ class ObjectCreateMessageVehiclesTest extends Specification { fury.basic.pos.coord.x mustEqual 6531.961f fury.basic.pos.coord.y mustEqual 1872.1406f fury.basic.pos.coord.z mustEqual 24.734375f - fury.basic.pos.roll mustEqual 0 - fury.basic.pos.pitch mustEqual 0 - fury.basic.pos.yaw mustEqual 33 + fury.basic.pos.orient.x mustEqual 0f + fury.basic.pos.orient.y mustEqual 0f + fury.basic.pos.orient.z mustEqual 357.1875f fury.basic.pos.vel.isDefined mustEqual false fury.basic.faction mustEqual PlanetSideEmpire.VS fury.basic.unk mustEqual 4 @@ -78,9 +78,9 @@ class ObjectCreateMessageVehiclesTest extends Specification { ant.basic.pos.coord.x mustEqual 3674.8438f ant.basic.pos.coord.y mustEqual 2726.789f ant.basic.pos.coord.z mustEqual 91.15625f - ant.basic.pos.roll mustEqual 0 - ant.basic.pos.pitch mustEqual 0 - ant.basic.pos.yaw mustEqual 0 + ant.basic.pos.orient.x mustEqual 0f + ant.basic.pos.orient.y mustEqual 0f + ant.basic.pos.orient.z mustEqual 90.0f ant.basic.faction mustEqual PlanetSideEmpire.VS ant.basic.unk mustEqual 4 ant.basic.player_guid mustEqual PlanetSideGUID(0) @@ -104,9 +104,9 @@ class ObjectCreateMessageVehiclesTest extends Specification { lightning.basic.pos.coord.x mustEqual 3674.8438f lightning.basic.pos.coord.y mustEqual 2726.789f lightning.basic.pos.coord.z mustEqual 91.15625f - lightning.basic.pos.roll mustEqual 0 - lightning.basic.pos.pitch mustEqual 0 - lightning.basic.pos.yaw mustEqual 0 + lightning.basic.pos.orient.x mustEqual 0f + lightning.basic.pos.orient.y mustEqual 0f + lightning.basic.pos.orient.z mustEqual 90.0f lightning.basic.faction mustEqual PlanetSideEmpire.VS lightning.basic.unk mustEqual 4 lightning.basic.player_guid mustEqual PlanetSideGUID(0) @@ -155,9 +155,9 @@ class ObjectCreateMessageVehiclesTest extends Specification { deliverer.basic.pos.coord.x mustEqual 6531.961f deliverer.basic.pos.coord.y mustEqual 1872.1406f deliverer.basic.pos.coord.z mustEqual 24.734375f - deliverer.basic.pos.roll mustEqual 0 - deliverer.basic.pos.pitch mustEqual 0 - deliverer.basic.pos.yaw mustEqual 33 + deliverer.basic.pos.orient.x mustEqual 0f + deliverer.basic.pos.orient.y mustEqual 0f + deliverer.basic.pos.orient.z mustEqual 357.1875f deliverer.basic.faction mustEqual PlanetSideEmpire.NC deliverer.basic.unk mustEqual 4 deliverer.basic.player_guid mustEqual PlanetSideGUID(0) @@ -221,9 +221,9 @@ class ObjectCreateMessageVehiclesTest extends Specification { ams.basic.pos.coord.x mustEqual 3674.0f ams.basic.pos.coord.y mustEqual 2726.789f ams.basic.pos.coord.z mustEqual 91.15625f - ams.basic.pos.roll mustEqual 0 - ams.basic.pos.pitch mustEqual 0 - ams.basic.pos.yaw mustEqual 0 + ams.basic.pos.orient.x mustEqual 0f + ams.basic.pos.orient.y mustEqual 0f + ams.basic.pos.orient.z mustEqual 90.0f ams.basic.faction mustEqual PlanetSideEmpire.VS ams.basic.unk mustEqual 0 ams.basic.player_guid mustEqual PlanetSideGUID(34082) @@ -253,9 +253,9 @@ class ObjectCreateMessageVehiclesTest extends Specification { dams.pos.coord.x mustEqual 3674.0f dams.pos.coord.y mustEqual 2726.789f dams.pos.coord.z mustEqual 91.15625f - dams.pos.roll mustEqual 0 - dams.pos.pitch mustEqual 0 - dams.pos.yaw mustEqual 0 + dams.pos.orient.x mustEqual 0f + dams.pos.orient.y mustEqual 0f + dams.pos.orient.z mustEqual 90.0f case _ => ko } @@ -274,9 +274,9 @@ class ObjectCreateMessageVehiclesTest extends Specification { switchblade.basic.pos.coord.x mustEqual 6531.961f switchblade.basic.pos.coord.y mustEqual 1872.1406f switchblade.basic.pos.coord.z mustEqual 24.734375f - switchblade.basic.pos.roll mustEqual 0 - switchblade.basic.pos.pitch mustEqual 0 - switchblade.basic.pos.yaw mustEqual 33 + switchblade.basic.pos.orient.x mustEqual 0f + switchblade.basic.pos.orient.y mustEqual 0f + switchblade.basic.pos.orient.z mustEqual 357.1875f switchblade.basic.faction mustEqual PlanetSideEmpire.VS switchblade.basic.unk mustEqual 4 switchblade.health mustEqual 255 @@ -321,9 +321,9 @@ class ObjectCreateMessageVehiclesTest extends Specification { droppod.basic.pos.coord.x mustEqual 5108.0f droppod.basic.pos.coord.y mustEqual 6164.0f droppod.basic.pos.coord.z mustEqual 1023.9844f - droppod.basic.pos.roll mustEqual 0 - droppod.basic.pos.pitch mustEqual 0 - droppod.basic.pos.yaw mustEqual 0 + droppod.basic.pos.orient.x mustEqual 0f + droppod.basic.pos.orient.y mustEqual 0f + droppod.basic.pos.orient.z mustEqual 90.0f droppod.basic.unk mustEqual 4 droppod.basic.player_guid mustEqual PlanetSideGUID(0) droppod.burn mustEqual false @@ -366,9 +366,9 @@ class ObjectCreateMessageVehiclesTest extends Specification { shuttle.pos.get.coord.x mustEqual 5610.0156f shuttle.pos.get.coord.y mustEqual 4255.258f shuttle.pos.get.coord.z mustEqual 134.1875f - shuttle.pos.get.roll mustEqual 0 - shuttle.pos.get.pitch mustEqual 0 - shuttle.pos.get.yaw mustEqual 96 + shuttle.pos.get.orient.x mustEqual 0f + shuttle.pos.get.orient.y mustEqual 0f + shuttle.pos.get.orient.z mustEqual 180.0f case _ => ko } @@ -377,7 +377,7 @@ class ObjectCreateMessageVehiclesTest extends Specification { "encode (fury)" in { val obj = VehicleData( CommonFieldData( - PlacementData(6531.961f, 1872.1406f, 24.734375f, 0, 0, 33), + PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f), PlanetSideEmpire.VS, 4 ), 255, @@ -394,7 +394,7 @@ class ObjectCreateMessageVehiclesTest extends Specification { "encode (ant)" in { val obj = ANTData( CommonFieldData( - PlacementData(3674.8438f, 2726.789f, 91.15625f), + PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f), PlanetSideEmpire.VS, 4 ), 255, @@ -409,7 +409,7 @@ class ObjectCreateMessageVehiclesTest extends Specification { "encode (lightning)" in { val obj = VehicleData( CommonFieldData( - PlacementData(3674.8438f, 2726.789f, 91.15625f), + PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f), PlanetSideEmpire.VS, 4 ), 255, @@ -423,10 +423,10 @@ class ObjectCreateMessageVehiclesTest extends Specification { pkt mustEqual string_lightning } - "encode (deliverer)" in { + "encode (medium transport)" in { val obj = VehicleData( CommonFieldData( - PlacementData(6531.961f, 1872.1406f, 24.734375f, 0, 0, 33), + PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f), PlanetSideEmpire.NC, 4 ), 0, @@ -455,7 +455,8 @@ class ObjectCreateMessageVehiclesTest extends Specification { "encode (ams)" in { val obj = AMSData( - CommonFieldData(PlacementData(3674.0f, 2726.789f, 91.15625f, 0, 0, 0), + CommonFieldData( + PlacementData(3674.0f, 2726.789f, 91.15625f, 0f, 0f, 90.0f), PlanetSideEmpire.VS, 0, PlanetSideGUID(34082) ), @@ -476,17 +477,19 @@ class ObjectCreateMessageVehiclesTest extends Specification { } "encode (ams, destroyed)" in { - val obj = DestroyedVehicleData(PlacementData(3674.0f, 2726.789f, 91.15625f)) + val obj = DestroyedVehicleData(PlacementData(3674.0f, 2726.789f, 91.15625f, 0f, 0f, 90.0f)) val msg = ObjectCreateMessage(ObjectClass.ams_destroyed, PlanetSideGUID(4157), obj) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string_ams_destroyed } - "encode (switchblade(" in { + "encode (switchblade)" in { val obj = Vehicle2Data( - CommonFieldData(PlacementData(6531.961f, 1872.1406f, 24.734375f ,0 ,0 ,33), - PlanetSideEmpire.VS, 4 + CommonFieldData( + PlacementData(6531.961f, 1872.1406f, 24.734375f, 0f, 0f, 357.1875f), + PlanetSideEmpire.VS, + 4 ), 255, DriveState.Mobile, @@ -503,8 +506,9 @@ class ObjectCreateMessageVehiclesTest extends Specification { "encode (droppod)" in { val obj = DroppodData( CommonFieldData( - PlacementData(5108.0f, 6164.0f, 1023.9844f), - PlanetSideEmpire.VS, 4 + PlacementData(5108.0f, 6164.0f, 1023.9844f, 0f, 0f, 90.0f), + PlanetSideEmpire.VS, + 4 ) ) val msg = ObjectCreateMessage(ObjectClass.droppod, PlanetSideGUID(3595), obj) @@ -522,7 +526,7 @@ class ObjectCreateMessageVehiclesTest extends Specification { } "encode (shuttle 2)" in { - val obj = OrbitalShuttleData(PlacementData(5610.0156f, 4255.258f, 134.1875f, 0, 0, 96), PlanetSideEmpire.VS) + val obj = OrbitalShuttleData(PlacementData(5610.0156f, 4255.258f, 134.1875f, 0f, 0f, 180.0f), PlanetSideEmpire.VS) val msg = ObjectCreateMessage(ObjectClass.orbital_shuttle, PlanetSideGUID(1127), obj) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector diff --git a/common/src/test/scala/game/ObjectDetachMessageTest.scala b/common/src/test/scala/game/ObjectDetachMessageTest.scala index 0b2b8605..f7ee8b52 100644 --- a/common/src/test/scala/game/ObjectDetachMessageTest.scala +++ b/common/src/test/scala/game/ObjectDetachMessageTest.scala @@ -18,16 +18,16 @@ class ObjectDetachMessageTest extends Specification { pos.x mustEqual 3567.1406f pos.y mustEqual 2988.0078f pos.z mustEqual 71.84375f - roll mustEqual 0 - pitch mustEqual 0 - yaw mustEqual 64 + roll mustEqual 0f + pitch mustEqual 0f + yaw mustEqual 270f case _ => ko } } "encode" in { - val msg = ObjectDetachMessage(PlanetSideGUID(2916), PlanetSideGUID(2502), Vector3(3567.1406f, 2988.0078f, 71.84375f), 0, 0, 64) + val msg = ObjectDetachMessage(PlanetSideGUID(2916), PlanetSideGUID(2502), Vector3(3567.1406f, 2988.0078f, 71.84375f), 0f, 0f, 270f) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string diff --git a/common/src/test/scala/game/PlayerStateMessageTest.scala b/common/src/test/scala/game/PlayerStateMessageTest.scala index e8ab270d..ab5441c7 100644 --- a/common/src/test/scala/game/PlayerStateMessageTest.scala +++ b/common/src/test/scala/game/PlayerStateMessageTest.scala @@ -20,9 +20,9 @@ class PlayerStateMessageTest extends Specification { pos.y mustEqual 5981.414f pos.z mustEqual 44.875f vel.isDefined mustEqual false - facingYaw mustEqual 31 - facingPitch mustEqual 0 - facingUpper mustEqual 0 + facingYaw mustEqual 2.8125f + facingPitch mustEqual 0f + facingUpper mustEqual 0f unk1 mustEqual 83 crouching mustEqual false jumping mustEqual false @@ -41,9 +41,9 @@ class PlayerStateMessageTest extends Specification { pos.y mustEqual 5981.414f pos.z mustEqual 44.875f vel.isDefined mustEqual false - facingYaw mustEqual 31 - facingPitch mustEqual 0 - facingUpper mustEqual 0 + facingYaw mustEqual 2.8125f + facingPitch mustEqual 0f + facingUpper mustEqual 0f unk1 mustEqual 83 crouching mustEqual false jumping mustEqual true @@ -65,9 +65,9 @@ class PlayerStateMessageTest extends Specification { vel.get.x mustEqual 2.53125f vel.get.y mustEqual 6.5625f vel.get.z mustEqual 0.0f - facingYaw mustEqual 24 - facingPitch mustEqual 4 - facingUpper mustEqual 0 + facingYaw mustEqual 22.5f + facingPitch mustEqual 348.75f + facingUpper mustEqual 0f unk1 mustEqual 165 crouching mustEqual false jumping mustEqual false @@ -83,7 +83,7 @@ class PlayerStateMessageTest extends Specification { PlanetSideGUID(1696), Vector3(4003.7422f, 5981.414f, 44.875f), None, - 31, 0, 0, 83, + 2.8125f, 0f, 0f, 83, false, false, false, false) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string_short @@ -94,7 +94,7 @@ class PlayerStateMessageTest extends Specification { PlanetSideGUID(1696), Vector3(4003.7422f, 5981.414f, 44.875f), None, - 31, 0, 0, 83, + 2.8125f, 0f, 0f, 83, false, true, false, true) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string_mod @@ -105,7 +105,7 @@ class PlayerStateMessageTest extends Specification { PlanetSideGUID(1696), Vector3(4008.6016f, 5987.6016f, 44.1875f), Some(Vector3(2.53125f, 6.5625f, 0f)), - 24, 4, 0, 165, + 22.5f, 348.75f, 0f, 165, false, false, false, false) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string_vel diff --git a/common/src/test/scala/game/PlayerStateMessageUpstreamTest.scala b/common/src/test/scala/game/PlayerStateMessageUpstreamTest.scala index a95286da..d3402697 100644 --- a/common/src/test/scala/game/PlayerStateMessageUpstreamTest.scala +++ b/common/src/test/scala/game/PlayerStateMessageUpstreamTest.scala @@ -16,9 +16,9 @@ class PlayerStateMessageUpstreamTest extends Specification { avatar_guid mustEqual PlanetSideGUID(75) pos mustEqual Vector3(3694.1094f, 2735.4531f, 90.84375f) vel mustEqual Some(Vector3(4.375f, 2.59375f, 0.0f)) - facingYaw mustEqual 10 - facingPitch mustEqual 3 - facingYawUpper mustEqual 0 + facingYaw mustEqual 61.875f + facingPitch mustEqual 351.5625f + facingYawUpper mustEqual 0.0f seq_time mustEqual 136 unk1 mustEqual 0 is_crouching mustEqual false @@ -33,7 +33,7 @@ class PlayerStateMessageUpstreamTest extends Specification { } "encode" in { - val msg = PlayerStateMessageUpstream(PlanetSideGUID(75), Vector3(3694.1094f, 2735.4531f, 90.84375f), Some(Vector3(4.375f, 2.59375f, 0.0f)), 10, 3, 0, 136, 0, false, false, false, false, 112, 0) + val msg = PlayerStateMessageUpstream(PlanetSideGUID(75), Vector3(3694.1094f, 2735.4531f, 90.84375f), Some(Vector3(4.375f, 2.59375f, 0.0f)), 61.875f, 351.5625f, 0.0f, 136, 0, false, false, false, false, 112, 0) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string diff --git a/common/src/test/scala/game/PlayerStateShiftMessageTest.scala b/common/src/test/scala/game/PlayerStateShiftMessageTest.scala index c7a3fd28..db411a31 100644 --- a/common/src/test/scala/game/PlayerStateShiftMessageTest.scala +++ b/common/src/test/scala/game/PlayerStateShiftMessageTest.scala @@ -9,7 +9,7 @@ import scodec.bits._ class PlayerStateShiftMessageTest extends Specification { val string_short = hex"BE 68" - val string_pos = hex"BE 95 A0 89 13 91 B8 B0 BF F0" + val string_pos = hex"BE 95 A0 89 13 91 B8 B0 B7 F0" //orig: ... B0 BF F0 val string_posAndVel = hex"BE AE 01 29 CD 59 B9 40 C0 EA D4 00 0F 86 40" "decode (short)" in { @@ -31,7 +31,7 @@ class PlayerStateShiftMessageTest extends Specification { state.get.pos.x mustEqual 4624.703f state.get.pos.y mustEqual 5922.1484f state.get.pos.z mustEqual 46.171875f - state.get.viewYawLim mustEqual 255 + state.get.viewYawLim mustEqual 92.8125f state.get.vel.isDefined mustEqual false unk.isDefined mustEqual false case _ => @@ -47,7 +47,7 @@ class PlayerStateShiftMessageTest extends Specification { state.get.pos.x mustEqual 4645.75f state.get.pos.y mustEqual 5811.6016f state.get.pos.z mustEqual 50.3125f - state.get.viewYawLim mustEqual 14 + state.get.viewYawLim mustEqual 50.625f state.get.vel.isDefined mustEqual true state.get.vel.get.x mustEqual 2.8125f state.get.vel.get.y mustEqual -8.0f @@ -66,14 +66,14 @@ class PlayerStateShiftMessageTest extends Specification { } "encode (pos)" in { - val msg = PlayerStateShiftMessage(ShiftState(1, Vector3(4624.703f, 5922.1484f, 46.171875f), 255)) + val msg = PlayerStateShiftMessage(ShiftState(1, Vector3(4624.703f, 5922.1484f, 46.171875f), 92.8125f)) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string_pos } "encode (pos and vel)" in { - val msg = PlayerStateShiftMessage(ShiftState(2, Vector3(4645.75f, 5811.6016f, 50.3125f), 14, Vector3(2.8125f, -8.0f, 0.375f))) + val msg = PlayerStateShiftMessage(ShiftState(2, Vector3(4645.75f, 5811.6016f, 50.3125f), 50.625f, Vector3(2.8125f, -8.0f, 0.375f))) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string_posAndVel diff --git a/common/src/test/scala/game/VehicleStateMessageTest.scala b/common/src/test/scala/game/VehicleStateMessageTest.scala index d9290863..1903fee9 100644 --- a/common/src/test/scala/game/VehicleStateMessageTest.scala +++ b/common/src/test/scala/game/VehicleStateMessageTest.scala @@ -12,15 +12,15 @@ class VehicleStateMessageTest extends Specification { "decode" in { PacketCoding.DecodePacket(string).require match { - case VehicleStateMessage(guid, unk1, pos, roll, pitch, yaw, vel, unk2, unk3, unk4, wheel, unk5, unk6) => + case VehicleStateMessage(guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel, unk5, unk6) => guid mustEqual PlanetSideGUID(413) unk1 mustEqual 0 pos.x mustEqual 3674.8438f pos.y mustEqual 2726.789f pos.z mustEqual 91.09375f - roll mustEqual 359.29688f - pitch mustEqual 1.0546875f - yaw mustEqual 90.35156f + ang.x mustEqual 359.29688f + ang.y mustEqual 1.0546875f + ang.z mustEqual 90.35156f vel.isDefined mustEqual true vel.get.x mustEqual 0.0f vel.get.y mustEqual 0.0f @@ -41,7 +41,7 @@ class VehicleStateMessageTest extends Specification { PlanetSideGUID(413), 0, Vector3(3674.8438f, 2726.789f, 91.09375f), - 359.29688f, 1.0546875f, 90.35156f, + Vector3(359.29688f, 1.0546875f, 90.35156f), Some(Vector3(0.0f, 0.0f, 0.03125f)), None, 0, 0, 15, diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index d44ce0f3..f3940e9f 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -1,6 +1,4 @@ // Copyright (c) 2017 PSForever -import java.net.{InetAddress, InetSocketAddress} - import akka.actor.{Actor, ActorRef, Cancellable, MDCContextAware} import net.psforever.packet.{PlanetSideGamePacket, _} import net.psforever.packet.control._ @@ -31,8 +29,8 @@ class WorldSessionActor extends Actor with MDCContextAware { def receive = Initializing def Initializing : Receive = { - case HelloFriend(sessionId, right) => - this.sessionId = sessionId + case HelloFriend(inSessionId, right) => + this.sessionId = inSessionId leftRef = sender() rightRef = right.asInstanceOf[ActorRef] @@ -81,7 +79,7 @@ class WorldSessionActor extends Actor with MDCContextAware { handlePkt(v) } case sync @ ControlSync(diff, unk, f1, f2, f3, f4, fa, fb) => - log.debug(s"SYNC: ${sync}") + log.debug(s"SYNC: $sync") val serverTick = Math.abs(System.nanoTime().toInt) // limit the size to prevent encoding error sendResponse(PacketCoding.CreateControlPacket(ControlSyncResp(diff, serverTick, fa, fb, fb, fa))) @@ -113,8 +111,7 @@ class WorldSessionActor extends Actor with MDCContextAware { val app = CharacterAppearanceData( PlacementData( Vector3(3674.8438f, 2726.789f, 91.15625f), - 0, 0, - 19 + Vector3(0f, 0f, 90f) ), BasicCharacterData( "IlllIIIlllIlIllIlllIllI", @@ -166,9 +163,9 @@ class WorldSessionActor extends Actor with MDCContextAware { def handleGamePkt(pkt : PlanetSideGamePacket) = pkt match { case ConnectToWorldRequestMessage(server, token, majorVersion, minorVersion, revision, buildDate, unk) => - val clientVersion = s"Client Version: ${majorVersion}.${minorVersion}.${revision}, ${buildDate}" + val clientVersion = s"Client Version: $majorVersion.$minorVersion.$revision, $buildDate" - log.info(s"New world login to ${server} with Token:${token}. ${clientVersion}") + log.info(s"New world login to $server with Token:$token. $clientVersion") // ObjectCreateMessage sendResponse(PacketCoding.CreateGamePacket(0, objectHex)) @@ -229,6 +226,18 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(PacketCoding.CreateGamePacket(0, CreateShortcutMessage(guid, 1, 0, true, Shortcut.MEDKIT))) sendResponse(PacketCoding.CreateGamePacket(0, ReplicationStreamMessage(5, Some(6), Vector(SquadListing())))) //clear squad list + val fury = VehicleData( + CommonFieldData( + PlacementData(3674.8438f, 2732f, 91.15625f, 0.0f, 0.0f, 90.0f), + PlanetSideEmpire.VS, 4 + ), + 255, + MountItem(ObjectClass.fury_weapon_systema, PlanetSideGUID(400), 1, + WeaponData(0x6, 0x8, 0, ObjectClass.hellfire_ammo, PlanetSideGUID(432), 0, AmmoBoxData(0x8)) + ) + ) + sendResponse(PacketCoding.CreateGamePacket(0, ObjectCreateMessage(ObjectClass.fury, PlanetSideGUID(413), fury))) + import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits.global clientKeepAlive = context.system.scheduler.schedule(0 seconds, 500 milliseconds, self, PokeClient()) @@ -249,13 +258,13 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ BeginZoningMessage() => log.info("Reticulating splines ...") - case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, unk1, aim_pitch, unk2, seq_time, unk3, is_crouching, is_jumping, unk4, is_cloaking, unk5, unk6) => + case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yawUpper, seq_time, unk3, is_crouching, is_jumping, unk4, is_cloaking, unk5, unk6) => //log.info("PlayerState: " + msg) - case msg @ ChildObjectStateMessage(object_guid : PlanetSideGUID, pitch : Int, yaw : Int) => + case msg @ ChildObjectStateMessage(object_guid, pitch, yaw) => //log.info("ChildObjectState: " + msg) - case msg @ VehicleStateMessage(vehicle_guid, unk1, pos, roll, pitch, yaw, vel, unk5, unk6, unk7, wheels, unk9, unkA) => + case msg @ VehicleStateMessage(vehicle_guid, unk1, pos, ang, vel, unk5, unk6, unk7, wheels, unk9, unkA) => //log.info("VehicleState: " + msg) case msg @ ProjectileStateMessage(projectile_guid, shot_pos, shot_vector, unk1, unk2, unk3, unk4, time_alive) => @@ -456,7 +465,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ TargetingImplantRequest(list) => log.info("TargetingImplantRequest: "+msg) - case default => log.error(s"Unhandled GamePacket ${pkt}") + case default => log.error(s"Unhandled GamePacket $pkt") } def failWithError(error : String) = {