From 151e2d0ad8ce8917514d74586ffc454da337f749 Mon Sep 17 00:00:00 2001 From: FateJH Date: Sat, 7 Jan 2017 22:22:04 -0500 Subject: [PATCH] working tests for packet --- .../packet/game/PlayerStateMessage.scala | 62 +++++----- common/src/test/scala/GamePacketTest.scala | 108 ++++++++++++++++++ 2 files changed, 142 insertions(+), 28 deletions(-) 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 3d32307e..bf2e7e87 100644 --- a/common/src/main/scala/net/psforever/packet/game/PlayerStateMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/PlayerStateMessage.scala @@ -11,13 +11,27 @@ import shapeless.{::, HNil} import scala.collection.mutable /** - * The server instructs clients to render a certain avatar not operated by its player to move in a certain way.
+ * The server instructs some clients to render a player (usually not that client's avatar) to move in a certain way.
*
- * 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; - * and, from there, it moves a certain distance as according to the values. - * The repositioning always takes the same amount of time and the player model is left in running animation (in place). - * The coordinates evaluate between -256.0 and 256.0.
+ * This packet instructs the basic aspects of how the player character is positioned and how the player character moves. + * Each client keeps track of where a character "currently" is according to that client. + * `pos` reflects an update in regards to where the character should be moved. + * Data between this "currently" and "new" are interpolated over a fixed time interval. + * Position and velocity data is standard to normal PlanetSide ranges. + * All angles follow the convention that every `0x1` is about 2.8125 degrees; so, `0x10` is 45.0 degrees.
+ *
+ * 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`. + * 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.
+ *
+ * If the model must interact with the environment during a velocity-driven move, it copes with local physics. + * 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
@@ -46,22 +60,18 @@ import scala.collection.mutable * @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; - * every `0x1` is about 2.8125 degrees; * measurements are counter-clockwise from East - * @param facingPitch the angle with respect to the sky and the ground towards which the avatar is looking; - * every `0x1` is about 2.8125 degrees - * @param facingYawUpper the angle of the avatar's upper body with respect to its forward-facing direction; - * every `0x1` is about 2.8125 degrees + * @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 unk1 na - * @param fourBools set to `false` to parse the following four fields, otherwise those values will be ignored * @param isCrouching avatar is crouching; - * must remain flagged for crouch to maintain animation; - * turn off to stand up + * must remain flagged to maintain crouch * @param isJumping avatar is jumping; * must remain flagged for jump to maintain animation; * turn off to land(?) * @param unk2 na - * @param unk3 na + * @param isCloaked avatar is cloaked by virtue of an Infiltration Suit; + * must remain flagged to stay cloaked */ final case class PlayerStateMessage(guid : PlanetSideGUID, pos : Vector3, @@ -70,11 +80,10 @@ final case class PlayerStateMessage(guid : PlanetSideGUID, facingPitch : Int, facingYawUpper : Int, unk1 : Int, - fourBools : Boolean, isCrouching : Boolean = false, isJumping : Boolean = false, unk2 : Boolean = false, - unk3 : Boolean = false) + isCloaked : Boolean = false) extends PlanetSideGamePacket { type Packet = PlayerStateMessage def opcode = GamePacketOpcode.PlayerStateMessage @@ -91,7 +100,7 @@ object PlayerStateMessage extends Marshallable[PlayerStateMessage] { ("isCrouching" | bool) :: ("isJumping" | bool) :: ("unk2" | bool) :: - ("unk3" | bool) + ("isCloaked" | bool) ).as[fourBoolPattern] /** @@ -124,28 +133,25 @@ object PlayerStateMessage extends Marshallable[PlayerStateMessage] { }) ).xmap[PlayerStateMessage] ( { - case uid :: p :: true :: Some(extra) :: f1 :: f2 :: f3 :: u :: b :: _ :: b1 :: b2 :: b3 :: b4 :: HNil => - PlayerStateMessage(uid, p, Some(extra), f1, f2, f3, u, b, b1, b2, b3, b4) - case uid :: p :: false :: None :: f1 :: f2 :: f3 :: u :: b :: _ :: b1 :: b2 :: b3 :: b4 :: HNil => - PlayerStateMessage(uid, p, None, f1, f2, f3, u, b, b1, b2, b3, b4) + case uid :: pos :: _ :: vel :: f1 :: f2 :: f3 :: u :: _ :: _ :: b1 :: b2 :: b3 :: b4 :: HNil => + PlayerStateMessage(uid, pos, vel, f1, f2, f3, u, b1, b2, b3, b4) }, { - case PlayerStateMessage(uid, p, Some(extra), f1, f2, f3, u, b, b1, b2, b3, b4) => - uid :: p :: true :: Some(extra) :: f1 :: f2 :: f3 :: u :: b :: () :: b1 :: b2 :: b3 :: b4 :: HNil - case PlayerStateMessage(uid, p, None, f1, f2, f3, u, b, b1, b2, b3, b4) => - uid :: p :: false :: None :: f1 :: f2 :: f3 :: u :: b :: () :: b1 :: b2 :: b3 :: b4 :: HNil + case PlayerStateMessage(uid, pos, vel, f1, f2, f3, u, b1, b2, b3, b4) => + val b : Boolean = !(b1 || b2 || b3 || b4) + uid :: pos :: vel.isDefined :: vel :: f1 :: f2 :: f3 :: u :: b :: () :: b1 :: b2 :: b3 :: b4 :: HNil } ) } //TODO the following logic is unimplemented /* -There is a bool that is currently unhandled that determines if the packet is aware that this code would run. +There is a boolean that is currently unhandled(?) that determines if the packet is aware that this code would run. If it passes, the first 8-bit value is the number of times the data will be iterated over. On each pass, a 4-bit value is extracted from the packet and compared against 15. When 15 is read, an 8-bit value is read on that same turn. On each subsequent turn, 8-bit values will be read until the number of iterations or there is an exception. -I have no clue what any of this is supposed to do. +Until I find a packet that responds somehow, I have no clue what any of this is supposed to do. */ /** * na diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index 2f7e8a11..9a2ca0c7 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -118,6 +118,114 @@ class GamePacketTest extends Specification { } } + "PlayerStateMessage" should { + val string_short = hex"08 A006 DFD17 B5AEB 380B 0F80002990" + val string_mod = hex"08 A006 DFD17 B5AEB 380B 0F80002985" //slightly modified from above to demonstrate active booleans + val string_vel = hex"08 A006 4DD47 CDB1B 0C0B A8C1A5000403008014A4" + + "decode (short)" in { + PacketCoding.DecodePacket(string_short).require match { + case PlayerStateMessage(guid, pos, vel, facingYaw, facingPitch, facingUpper, unk1, crouching, jumping, unk2, unk3) => + guid mustEqual PlanetSideGUID(1696) + pos.x mustEqual 4003.7422f + pos.y mustEqual 5981.414f + pos.z mustEqual 44.875f + vel.isDefined mustEqual false + facingYaw mustEqual 31 + facingPitch mustEqual 0 + facingUpper mustEqual 0 + unk1 mustEqual 83 + crouching mustEqual false + jumping mustEqual false + unk2 mustEqual false + unk3 mustEqual false + case default => + ko + } + } + + "decode (mod)" in { + PacketCoding.DecodePacket(string_mod).require match { + case PlayerStateMessage(guid, pos, vel, facingYaw, facingPitch, facingUpper, unk1, crouching, jumping, unk2, unk3) => + guid mustEqual PlanetSideGUID(1696) + pos.x mustEqual 4003.7422f + pos.y mustEqual 5981.414f + pos.z mustEqual 44.875f + vel.isDefined mustEqual false + facingYaw mustEqual 31 + facingPitch mustEqual 0 + facingUpper mustEqual 0 + unk1 mustEqual 83 + crouching mustEqual false + jumping mustEqual true + unk2 mustEqual false + unk3 mustEqual true + case default => + ko + } + } + + "decode (vel)" in { + PacketCoding.DecodePacket(string_vel).require match { + case PlayerStateMessage(guid, pos, vel, facingYaw, facingPitch, facingUpper, unk1, crouching, jumping, unk2, unk3) => + guid mustEqual PlanetSideGUID(1696) + pos.x mustEqual 4008.6016f + pos.y mustEqual 5987.6016f + pos.z mustEqual 44.1875f + vel.isDefined mustEqual true + 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 + unk1 mustEqual 165 + crouching mustEqual false + jumping mustEqual false + unk2 mustEqual false + unk3 mustEqual false + case default => + ko + } + } + + "encode (short)" in { + val msg = PlayerStateMessage( + PlanetSideGUID(1696), + Vector3(4003.7422f, 5981.414f, 44.875f), + None, + 31, 0, 0, 83, + false, false, false, false) + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual string_short + } + + "encode (mod)" in { + val msg = PlayerStateMessage( + PlanetSideGUID(1696), + Vector3(4003.7422f, 5981.414f, 44.875f), + None, + 31, 0, 0, 83, + false, true, false, true) + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual string_mod + } + + "encode (vel)" in { + val msg = PlayerStateMessage( + PlanetSideGUID(1696), + Vector3(4008.6016f, 5987.6016f, 44.1875f), + Some(Vector3(2.53125f, 6.5625f, 0f)), + 24, 4, 0, 165, + false, false, false, false) + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual string_vel + } + } + "ActionResultMessage" should { "decode" in { PacketCoding.DecodePacket(hex"1f 80").require match {