working tests for packet

This commit is contained in:
FateJH 2017-01-07 22:22:04 -05:00
parent 4862b1b544
commit 151e2d0ad8
2 changed files with 142 additions and 28 deletions

View file

@ -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.<br>
* The server instructs some clients to render a player (usually not that client's avatar) to move in a certain way.<br>
* <br>
* 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.<br>
* 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.<br>
* <br>
* 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.<br>
* <br>
* 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.<br>
* <br>
* facingYaw:<br>
* `0x00` -- E<br>
@ -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

View file

@ -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 {