From bf05592c91da66e240333b77c199a0503c1e0eaf Mon Sep 17 00:00:00 2001 From: FateJH Date: Sun, 18 Dec 2016 02:47:03 -0500 Subject: [PATCH 1/3] initial work on PlayerStateShiftMessage packet and working tests --- .../psforever/packet/GamePacketOpcode.scala | 2 +- .../packet/game/PlayerStateShiftMessage.scala | 119 ++++++++++++++++++ common/src/test/scala/GamePacketTest.scala | 77 ++++++++++++ 3 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 common/src/main/scala/net/psforever/packet/game/PlayerStateShiftMessage.scala diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index 6e9984e5..506c2231 100644 --- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -543,7 +543,7 @@ object GamePacketOpcode extends Enumeration { case 0xbb => noDecoder(MapObjectStateBlockMessage) case 0xbc => noDecoder(SnoopMsg) case 0xbd => game.PlayerStateMessageUpstream.decode - case 0xbe => noDecoder(PlayerStateShiftMessage) + case 0xbe => game.PlayerStateShiftMessage.decode case 0xbf => noDecoder(ZipLineMessage) // OPCODES 0xc0-cf diff --git a/common/src/main/scala/net/psforever/packet/game/PlayerStateShiftMessage.scala b/common/src/main/scala/net/psforever/packet/game/PlayerStateShiftMessage.scala new file mode 100644 index 00000000..49c8c57c --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/PlayerStateShiftMessage.scala @@ -0,0 +1,119 @@ +// Copyright (c) 2016 PSForever.net to present +package net.psforever.packet.game + +import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} +import net.psforever.types.Vector3 +import scodec.Codec +import scodec.codecs._ +import shapeless.{::, HNil} + +/** + * na + * @param pos the position to move the character to in the world environment (in three coordinates) + * @param viewYawLim an angle with respect to the horizon towards which the avatar is looking (to some respect) + * @param vel the velocity to apply to to the character at the given position (in three coordinates) + */ +final case class PlayerState(pos : Vector3, + viewYawLim : Int, + vel : Option[Vector3]) + +/** + * Force the client's character to adhere to the influence of specific external stimulus. + * @param unk1 na + * @param state the state to influence the character with respect to his environment in the current zone + + */ +final case class PlayerStateShiftMessage(unk1 : Int, + state : Option[PlayerState], + unk2 : Boolean) + extends PlanetSideGamePacket { + type Packet = TimeOfDayMessage + def opcode = GamePacketOpcode.PlayerStateShiftMessage + def encode = PlayerStateShiftMessage.encode(this) +} + +object PlayerState extends Marshallable[PlayerState] { + /** + * An abbreviated constructor for creating `PlayerState`, assuming velocity is not applied. + * @param pos the position of the character in the world environment (in three coordinates) + * @param viewYawLim an angle with respect to the horizon towards which the avatar is looking (to some respect) + * @param vel the velocity to apply to to the character at the given position (in three coordinates) + * @return a `PlayerState` object + */ + def apply(pos : Vector3, viewYawLim : Int, vel : Vector3) : PlayerState = + PlayerState(pos, viewYawLim, Some(vel)) + + /** + * An abbreviated constructor for creating `PlayerState`, removing the optional condition of all parameters. + * @param pos the position of the character in the world environment (in three coordinates) + * @param viewYawLim an angle with respect to the horizon towards which the avatar is looking (to some respect) + * @return a `PlayerState` object + */ + def apply(pos : Vector3, viewYawLim : Int) : PlayerState = + PlayerState(pos, viewYawLim, None) + + implicit val codec : Codec[PlayerState] = ( + ("pos" | Vector3.codec_pos) :: + ("unk2" | uint8L) :: + (bool >>:~ { test => + ignore(0) :: + conditional(test, "pos" | Vector3.codec_vel) + }) + ).xmap[PlayerState] ( + { + case a :: b :: false :: _ :: None :: HNil => + PlayerState(a, b, None) + case a :: b :: true :: _ :: Some(vel) :: HNil => + PlayerState(a, b, Some(vel)) + }, + { + case PlayerState(a, b, None) => + a :: b :: false :: () :: None :: HNil + case PlayerState(a, b, Some(vel)) => + a :: b :: true :: () :: Some(vel) :: HNil + } + ).as[PlayerState] +} + +object PlayerStateShiftMessage extends Marshallable[PlayerStateShiftMessage] { + private type pattern = Int :: Option[PlayerState] :: Boolean :: HNil + + /** + * An abbreviated constructor for creating `PlayerStateShiftMessage`, removing the optional condition of `state`. + * @param unk1 na + * @param state the state to which to influence the character with respect to his environment in the current zone + * @param unk2 na + * @return a `PlayerStateShiftMessage` packet + */ + def apply(unk1 : Int, state : PlayerState, unk2 : Boolean) : PlayerStateShiftMessage = + PlayerStateShiftMessage(unk1, Some(state), unk2) + + /** + * An abbreviated constructor for creating `PlayerStateShiftMessage`, assuming the parameter `state` is not defined. + * @param unk1 na + * @param unk2 na + * @return a `PlayerStateShiftMessage` packet + */ + def apply(unk1 : Int, unk2 : Boolean) : PlayerStateShiftMessage = + PlayerStateShiftMessage(unk1, None, unk2) + + implicit val codec : Codec[PlayerStateShiftMessage] = ( + bool >>:~ { test1 => + ("unk1" | uintL(3)) :: + conditional(test1, "pos" | PlayerState.codec) :: + ("unk2" | bool) + }).xmap[PlayerStateShiftMessage] ( + { + case false :: a :: None :: b :: HNil => + PlayerStateShiftMessage(a, None, b) + case true :: a :: Some(pos) :: b :: HNil => + PlayerStateShiftMessage(a, Some(pos), b) + }, + { + case PlayerStateShiftMessage(a, None, b) => + false :: a :: None :: b :: HNil + case PlayerStateShiftMessage(a, Some(pos), b) => + true :: a :: Some(pos) :: b :: HNil + } + ).as[PlayerStateShiftMessage] +} diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index 2f7e8a11..7ddeecff 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -527,6 +527,83 @@ class GamePacketTest extends Specification { } } + "PlayerStateShiftMessage" should { + val string_short = hex"BE 68" + val string_pos = hex"BE 95 A0 89 13 91 B8 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 { + PacketCoding.DecodePacket(string_short).require match { + case PlayerStateShiftMessage(unk1, state, unk2) => + unk1 mustEqual 6 + state.isDefined mustEqual false + unk2 mustEqual true + case _ => + ko + } + } + + "decode (pos)" in { + PacketCoding.DecodePacket(string_pos).require match { + case PlayerStateShiftMessage(unk1, state, unk2) => + unk1 mustEqual 1 + state.isDefined mustEqual true + 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.vel.isDefined mustEqual false + unk2 mustEqual false + case _ => + ko + } + } + + "decode (pos and vel)" in { + PacketCoding.DecodePacket(string_posAndVel).require match { + case PlayerStateShiftMessage(unk1, state, unk2) => + unk1 mustEqual 2 + state.isDefined mustEqual true + 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.vel.isDefined mustEqual true + state.get.vel.get.x mustEqual 2.8125f + state.get.vel.get.y mustEqual -8.0f + state.get.vel.get.z mustEqual 0.375f + unk2 mustEqual false + case _ => + ko + } + } + + "encode (short)" in { + val msg = PlayerStateShiftMessage(6, true) + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual string_short + } + + "encode (pos)" in { + val msg = PlayerStateShiftMessage(1, + PlayerState(Vector3(4624.703f, 5922.1484f, 46.171875f), 255), + false) + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual string_pos + } + + "encode (pos and vel)" in { + val msg = PlayerStateShiftMessage(2, + PlayerState(Vector3(4645.75f, 5811.6016f, 50.3125f), 14, Vector3(2.8125f, -8.0f, 0.375f)), + false) + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual string_posAndVel + } + } + "UseItemMessage" should { val string = hex"10 4B00 0000 7401 FFFFFFFF 4001000000000000000000000000058C803600800000" From 66a0ebe24bd8b641eaab754afd4659c19b4e92c6 Mon Sep 17 00:00:00 2001 From: FateJH Date: Sun, 18 Dec 2016 18:51:56 -0500 Subject: [PATCH 2/3] added proper comments; changed name of case class --- .../packet/game/PlayerStateShiftMessage.scala | 103 +++++++++++------- common/src/test/scala/GamePacketTest.scala | 4 +- 2 files changed, 66 insertions(+), 41 deletions(-) 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 49c8c57c..451eb6d1 100644 --- a/common/src/main/scala/net/psforever/packet/game/PlayerStateShiftMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/PlayerStateShiftMessage.scala @@ -8,23 +8,50 @@ import scodec.codecs._ import shapeless.{::, HNil} /** - * na - * @param pos the position to move the character to in the world environment (in three coordinates) + * Instructs an avatar to be stood, to look, and to move, in a certain way.
+ *
+ * The position defines a coordinate location in the avatar's current zone to which the avatar is immediately moved + * This movement is instantaneous and has no associated animation. + * If velocity is defined, the avatar is provided an "external force" that "pushes" the avatar in a given direction. + * 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. + * @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 vel the velocity to apply to to the character at the given position (in three coordinates) + * @param vel if defined, the velocity to apply to to the character at the given position */ -final case class PlayerState(pos : Vector3, +final case class ShiftState(pos : Vector3, viewYawLim : Int, vel : Option[Vector3]) /** - * Force the client's character to adhere to the influence of specific external stimulus. - * @param unk1 na - * @param state the state to influence the character with respect to his environment in the current zone - + * Push specific motion-based stimuli on a specific character.
+ *
+ * `PlayerStateMessageUpstream` involves data transmitted from a client to the server regarding its avatar. + * `PlayerStateMessage` involves data transmitted from the server to the clients regarding characters other than that client's avatar. + * `PlayerStateShiftMessage` involves data transmitted from the server to a client about that client's avatar. + * It temporarily asserts itself before normal player movement and asserts specific placement and motion. + * An application of this packet is being `/warp`ed within a zone via a non-triggering agent (like a teleporter). + * Another, more common, application of this packet is being thrown about when the target of an attempted roadkill.
+ *
+ * Exploration:
+ * What do the leading and trailing values do? + * @param unk1 na; + * seems to have different purposes depending on whether `state` is defined + * @param state if defined, the behaviors to influence the character + * @param unk2 na */ final case class PlayerStateShiftMessage(unk1 : Int, - state : Option[PlayerState], + state : Option[ShiftState], unk2 : Boolean) extends PlanetSideGamePacket { type Packet = TimeOfDayMessage @@ -32,60 +59,58 @@ final case class PlayerStateShiftMessage(unk1 : Int, def encode = PlayerStateShiftMessage.encode(this) } -object PlayerState extends Marshallable[PlayerState] { +object ShiftState extends Marshallable[ShiftState] { /** - * An abbreviated constructor for creating `PlayerState`, assuming velocity is not applied. - * @param pos the position of the character in the world environment (in three coordinates) + * An abbreviated constructor for creating `ShiftState`, assuming velocity is not applied. + * @param pos the position of the character in the world environment * @param viewYawLim an angle with respect to the horizon towards which the avatar is looking (to some respect) - * @param vel the velocity to apply to to the character at the given position (in three coordinates) - * @return a `PlayerState` object + * @param vel the velocity to apply to to the character at the given position + * @return a `ShiftState` object */ - def apply(pos : Vector3, viewYawLim : Int, vel : Vector3) : PlayerState = - PlayerState(pos, viewYawLim, Some(vel)) + def apply(pos : Vector3, viewYawLim : Int, vel : Vector3) : ShiftState = + ShiftState(pos, viewYawLim, Some(vel)) /** - * An abbreviated constructor for creating `PlayerState`, removing the optional condition of all parameters. - * @param pos the position of the character in the world environment (in three coordinates) + * An abbreviated constructor for creating `ShiftState`, removing the optional condition of all parameters. + * @param pos the position of the character in the world environment * @param viewYawLim an angle with respect to the horizon towards which the avatar is looking (to some respect) - * @return a `PlayerState` object + * @return a `ShiftState` object */ - def apply(pos : Vector3, viewYawLim : Int) : PlayerState = - PlayerState(pos, viewYawLim, None) + def apply(pos : Vector3, viewYawLim : Int) : ShiftState = + ShiftState(pos, viewYawLim, None) - implicit val codec : Codec[PlayerState] = ( + implicit val codec : Codec[ShiftState] = ( ("pos" | Vector3.codec_pos) :: - ("unk2" | uint8L) :: - (bool >>:~ { test => - ignore(0) :: - conditional(test, "pos" | Vector3.codec_vel) - }) - ).xmap[PlayerState] ( + ("unk2" | uint8L) :: + (bool >>:~ { test => + ignore(0) :: + conditional(test, "pos" | Vector3.codec_vel) + }) + ).xmap[ShiftState] ( { case a :: b :: false :: _ :: None :: HNil => - PlayerState(a, b, None) + ShiftState(a, b, None) case a :: b :: true :: _ :: Some(vel) :: HNil => - PlayerState(a, b, Some(vel)) + ShiftState(a, b, Some(vel)) }, { - case PlayerState(a, b, None) => + case ShiftState(a, b, None) => a :: b :: false :: () :: None :: HNil - case PlayerState(a, b, Some(vel)) => + case ShiftState(a, b, Some(vel)) => a :: b :: true :: () :: Some(vel) :: HNil } - ).as[PlayerState] + ).as[ShiftState] } object PlayerStateShiftMessage extends Marshallable[PlayerStateShiftMessage] { - private type pattern = Int :: Option[PlayerState] :: Boolean :: HNil - /** * An abbreviated constructor for creating `PlayerStateShiftMessage`, removing the optional condition of `state`. * @param unk1 na - * @param state the state to which to influence the character with respect to his environment in the current zone + * @param state the behaviors to influence the character * @param unk2 na * @return a `PlayerStateShiftMessage` packet */ - def apply(unk1 : Int, state : PlayerState, unk2 : Boolean) : PlayerStateShiftMessage = + def apply(unk1 : Int, state : ShiftState, unk2 : Boolean) : PlayerStateShiftMessage = PlayerStateShiftMessage(unk1, Some(state), unk2) /** @@ -98,9 +123,9 @@ object PlayerStateShiftMessage extends Marshallable[PlayerStateShiftMessage] { PlayerStateShiftMessage(unk1, None, unk2) implicit val codec : Codec[PlayerStateShiftMessage] = ( - bool >>:~ { test1 => + bool >>:~ { test => ("unk1" | uintL(3)) :: - conditional(test1, "pos" | PlayerState.codec) :: + conditional(test, "state" | ShiftState.codec) :: ("unk2" | bool) }).xmap[PlayerStateShiftMessage] ( { diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index 7ddeecff..a64b09a1 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -587,7 +587,7 @@ class GamePacketTest extends Specification { "encode (pos)" in { val msg = PlayerStateShiftMessage(1, - PlayerState(Vector3(4624.703f, 5922.1484f, 46.171875f), 255), + ShiftState(Vector3(4624.703f, 5922.1484f, 46.171875f), 255), false) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector @@ -596,7 +596,7 @@ class GamePacketTest extends Specification { "encode (pos and vel)" in { val msg = PlayerStateShiftMessage(2, - PlayerState(Vector3(4645.75f, 5811.6016f, 50.3125f), 14, Vector3(2.8125f, -8.0f, 0.375f)), + ShiftState(Vector3(4645.75f, 5811.6016f, 50.3125f), 14, Vector3(2.8125f, -8.0f, 0.375f)), false) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector From 4143732977781affc56d42dd5e559fde0f647ae7 Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 11 Jan 2017 23:56:38 -0500 Subject: [PATCH 3/3] changing the conditions for the original first int value, and the conditions for the last int value; rewrote tests --- .../packet/game/PlayerStateShiftMessage.scala | 98 +++++++++---------- common/src/test/scala/GamePacketTest.scala | 28 +++--- 2 files changed, 57 insertions(+), 69 deletions(-) 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 451eb6d1..b99b8203 100644 --- a/common/src/main/scala/net/psforever/packet/game/PlayerStateShiftMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/PlayerStateShiftMessage.scala @@ -3,7 +3,7 @@ package net.psforever.packet.game import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} import net.psforever.types.Vector3 -import scodec.Codec +import scodec.{Attempt, Codec, Err} import scodec.codecs._ import shapeless.{::, HNil} @@ -25,13 +25,15 @@ import shapeless.{::, HNil} * 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. + * @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 vel if defined, the velocity to apply to to the character at the given position */ -final case class ShiftState(pos : Vector3, - viewYawLim : Int, - vel : Option[Vector3]) +final case class ShiftState(unk : Int, + pos : Vector3, + viewYawLim : Int, + vel : Option[Vector3]) /** * Push specific motion-based stimuli on a specific character.
@@ -41,18 +43,12 @@ final case class ShiftState(pos : Vector3, * `PlayerStateShiftMessage` involves data transmitted from the server to a client about that client's avatar. * It temporarily asserts itself before normal player movement and asserts specific placement and motion. * An application of this packet is being `/warp`ed within a zone via a non-triggering agent (like a teleporter). - * Another, more common, application of this packet is being thrown about when the target of an attempted roadkill.
- *
- * Exploration:
- * What do the leading and trailing values do? - * @param unk1 na; - * seems to have different purposes depending on whether `state` is defined + * Another, more common, application of this packet is being thrown about when the target of an attempted roadkill. * @param state if defined, the behaviors to influence the character - * @param unk2 na + * @param unk na */ -final case class PlayerStateShiftMessage(unk1 : Int, - state : Option[ShiftState], - unk2 : Boolean) +final case class PlayerStateShiftMessage(state : Option[ShiftState], + unk : Option[Int] = None) extends PlanetSideGamePacket { type Packet = TimeOfDayMessage def opcode = GamePacketOpcode.PlayerStateShiftMessage @@ -62,42 +58,38 @@ final case class PlayerStateShiftMessage(unk1 : Int, object ShiftState extends Marshallable[ShiftState] { /** * An abbreviated constructor for creating `ShiftState`, assuming velocity is not applied. + * @param unk na * @param pos the position of the character in the world environment * @param viewYawLim an angle with respect to the horizon towards which the avatar is looking (to some respect) * @param vel the velocity to apply to to the character at the given position * @return a `ShiftState` object */ - def apply(pos : Vector3, viewYawLim : Int, vel : Vector3) : ShiftState = - ShiftState(pos, viewYawLim, Some(vel)) + def apply(unk : Int, pos : Vector3, viewYawLim : Int, vel : Vector3) : ShiftState = + ShiftState(unk, pos, viewYawLim, Some(vel)) /** * An abbreviated constructor for creating `ShiftState`, removing the optional condition of all parameters. + * @param unk na * @param pos the position of the character in the world environment * @param viewYawLim an angle with respect to the horizon towards which the avatar is looking (to some respect) * @return a `ShiftState` object */ - def apply(pos : Vector3, viewYawLim : Int) : ShiftState = - ShiftState(pos, viewYawLim, None) + def apply(unk : Int, pos : Vector3, viewYawLim : Int) : ShiftState = + ShiftState(unk, pos, viewYawLim, None) implicit val codec : Codec[ShiftState] = ( + ("unk1" | uintL(3)) :: ("pos" | Vector3.codec_pos) :: - ("unk2" | uint8L) :: - (bool >>:~ { test => - ignore(0) :: - conditional(test, "pos" | Vector3.codec_vel) - }) + ("viewYawLim" | uint8L) :: + optional(bool, "pos" | Vector3.codec_vel) ).xmap[ShiftState] ( { - case a :: b :: false :: _ :: None :: HNil => - ShiftState(a, b, None) - case a :: b :: true :: _ :: Some(vel) :: HNil => - ShiftState(a, b, Some(vel)) + case a :: b :: c :: d :: HNil => + ShiftState(a, b, c, d) }, { - case ShiftState(a, b, None) => - a :: b :: false :: () :: None :: HNil - case ShiftState(a, b, Some(vel)) => - a :: b :: true :: () :: Some(vel) :: HNil + case ShiftState(a, b, c, d) => + a :: b :: c :: d :: HNil } ).as[ShiftState] } @@ -105,40 +97,40 @@ object ShiftState extends Marshallable[ShiftState] { object PlayerStateShiftMessage extends Marshallable[PlayerStateShiftMessage] { /** * An abbreviated constructor for creating `PlayerStateShiftMessage`, removing the optional condition of `state`. - * @param unk1 na * @param state the behaviors to influence the character - * @param unk2 na + * @param unk na * @return a `PlayerStateShiftMessage` packet */ - def apply(unk1 : Int, state : ShiftState, unk2 : Boolean) : PlayerStateShiftMessage = - PlayerStateShiftMessage(unk1, Some(state), unk2) + def apply(state : ShiftState, unk : Int) : PlayerStateShiftMessage = + PlayerStateShiftMessage(Some(state), Some(unk)) /** - * An abbreviated constructor for creating `PlayerStateShiftMessage`, assuming the parameter `state` is not defined. - * @param unk1 na - * @param unk2 na + * An abbreviated constructor for creating `PlayerStateShiftMessage`, removing the optional condition of `unk2`. + * @param state the behaviors to influence the character * @return a `PlayerStateShiftMessage` packet */ - def apply(unk1 : Int, unk2 : Boolean) : PlayerStateShiftMessage = - PlayerStateShiftMessage(unk1, None, unk2) + def apply(state : ShiftState) : PlayerStateShiftMessage = + PlayerStateShiftMessage(Some(state), None) + + /** + * An abbreviated constructor for creating `PlayerStateShiftMessage`, assuming the parameters `unk1` and `state` are not defined. + * @param unk na + * @return a `PlayerStateShiftMessage` packet + */ + def apply(unk : Int) : PlayerStateShiftMessage = + PlayerStateShiftMessage(None, Some(unk)) implicit val codec : Codec[PlayerStateShiftMessage] = ( - bool >>:~ { test => - ("unk1" | uintL(3)) :: - conditional(test, "state" | ShiftState.codec) :: - ("unk2" | bool) - }).xmap[PlayerStateShiftMessage] ( + optional(bool, "state" | ShiftState.codec) :: + optional(bool, "unk" | uintL(3)) + ).xmap[PlayerStateShiftMessage] ( { - case false :: a :: None :: b :: HNil => - PlayerStateShiftMessage(a, None, b) - case true :: a :: Some(pos) :: b :: HNil => - PlayerStateShiftMessage(a, Some(pos), b) + case a :: b :: HNil => + PlayerStateShiftMessage(a, b) }, { - case PlayerStateShiftMessage(a, None, b) => - false :: a :: None :: b :: HNil - case PlayerStateShiftMessage(a, Some(pos), b) => - true :: a :: Some(pos) :: b :: HNil + case PlayerStateShiftMessage(a, b) => + a :: b :: HNil } ).as[PlayerStateShiftMessage] } diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index a64b09a1..75d71fcb 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -534,10 +534,10 @@ class GamePacketTest extends Specification { "decode (short)" in { PacketCoding.DecodePacket(string_short).require match { - case PlayerStateShiftMessage(unk1, state, unk2) => - unk1 mustEqual 6 + case PlayerStateShiftMessage(state, unk) => state.isDefined mustEqual false - unk2 mustEqual true + unk.isDefined mustEqual true + unk.get mustEqual 5 case _ => ko } @@ -545,15 +545,15 @@ class GamePacketTest extends Specification { "decode (pos)" in { PacketCoding.DecodePacket(string_pos).require match { - case PlayerStateShiftMessage(unk1, state, unk2) => - unk1 mustEqual 1 + case PlayerStateShiftMessage(state, unk) => state.isDefined mustEqual true + state.get.unk mustEqual 1 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.vel.isDefined mustEqual false - unk2 mustEqual false + unk.isDefined mustEqual false case _ => ko } @@ -561,9 +561,9 @@ class GamePacketTest extends Specification { "decode (pos and vel)" in { PacketCoding.DecodePacket(string_posAndVel).require match { - case PlayerStateShiftMessage(unk1, state, unk2) => - unk1 mustEqual 2 + case PlayerStateShiftMessage(state, unk) => state.isDefined mustEqual true + state.get.unk mustEqual 2 state.get.pos.x mustEqual 4645.75f state.get.pos.y mustEqual 5811.6016f state.get.pos.z mustEqual 50.3125f @@ -572,32 +572,28 @@ class GamePacketTest extends Specification { state.get.vel.get.x mustEqual 2.8125f state.get.vel.get.y mustEqual -8.0f state.get.vel.get.z mustEqual 0.375f - unk2 mustEqual false + unk.isDefined mustEqual false case _ => ko } } "encode (short)" in { - val msg = PlayerStateShiftMessage(6, true) + val msg = PlayerStateShiftMessage(5) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string_short } "encode (pos)" in { - val msg = PlayerStateShiftMessage(1, - ShiftState(Vector3(4624.703f, 5922.1484f, 46.171875f), 255), - false) + val msg = PlayerStateShiftMessage(ShiftState(1, Vector3(4624.703f, 5922.1484f, 46.171875f), 255)) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string_pos } "encode (pos and vel)" in { - val msg = PlayerStateShiftMessage(2, - ShiftState(Vector3(4645.75f, 5811.6016f, 50.3125f), 14, Vector3(2.8125f, -8.0f, 0.375f)), - false) + val msg = PlayerStateShiftMessage(ShiftState(2, Vector3(4645.75f, 5811.6016f, 50.3125f), 14, Vector3(2.8125f, -8.0f, 0.375f))) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string_posAndVel