Merge pull request #142 from Fate-JH/minor-updates

Implementing Missing Tests
This commit is contained in:
Fate-JH 2017-05-13 11:57:24 -04:00 committed by GitHub
commit 9c68a419ce
16 changed files with 294 additions and 72 deletions

View file

@ -2,7 +2,7 @@ lazy val commonSettings = Seq(
organization := "net.psforever", organization := "net.psforever",
version := "1.0.2-SNAPSHOT", version := "1.0.2-SNAPSHOT",
scalaVersion := "2.11.8", scalaVersion := "2.11.8",
scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8"), scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8", "-language:postfixOps"),
resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots", resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots",
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-actor" % "2.4.4", "com.typesafe.akka" %% "akka-actor" % "2.4.4",

View file

@ -1,10 +1,9 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.packet.game package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
import scodec.Codec import scodec.Codec
import scodec.codecs._ import scodec.codecs._
import scodec.bits._
/** /**
* Tell the server that the player is is jumping. * Tell the server that the player is is jumping.

View file

@ -26,7 +26,8 @@ object PlanetSideGUID {
* *
* @param finished True when there are no more characters to give info on * @param finished True when there are no more characters to give info on
*/ */
final case class CharacterInfoMessage(zoneId : PlanetSideZoneID, final case class CharacterInfoMessage(unk : Long,
zoneId : PlanetSideZoneID,
charId : Long, charId : Long,
guid : PlanetSideGUID, guid : PlanetSideGUID,
finished : Boolean, finished : Boolean,
@ -39,8 +40,8 @@ final case class CharacterInfoMessage(zoneId : PlanetSideZoneID,
object CharacterInfoMessage extends Marshallable[CharacterInfoMessage] { object CharacterInfoMessage extends Marshallable[CharacterInfoMessage] {
implicit val codec : Codec[CharacterInfoMessage] = ( implicit val codec : Codec[CharacterInfoMessage] = (
("unknown" | uint32L).unit(0) :: // this type is set to unit as we dont know what it is yet ("unk" | uint32L) ::
("zoneId" | PlanetSideZoneID.codec) :: ("zoneId" | PlanetSideZoneID.codec) ::
("charId" | uint32L) :: ("charId" | uint32L) ::
("charGUID" | PlanetSideGUID.codec) :: ("charGUID" | PlanetSideGUID.codec) ::
("finished" | bool) :: ("finished" | bool) ::

View file

@ -7,23 +7,26 @@ import scodec.Codec
import scodec.codecs._ import scodec.codecs._
/** /**
* For completion's sake. * Dispatched from the client to request that an object be deployed.<br>
* We've never actually sent or received this packet during session captures on Gemini Live. * <br>
* @param guid na * Information in the packet mainly reports about the surface on which the object will be coplanar when/if placed.
* The server responds with a `ObjectDeployedMessage` packet with the results.
* If successful, that is followed by an `ObjectCreateMessage` packet and a `DeployableObjectsInfoMessage` packet.
* @param object_guid the object
* @param unk1 na * @param unk1 na
* @param pos na * @param pos the location where the object is to be deployed
* @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 unk2 na * @param unk2 na
* @param unk3 na
* @param unk4 na
* @param unk5 na
*/ */
final case class DeployObjectMessage(guid : PlanetSideGUID, final case class DeployObjectMessage(object_guid : PlanetSideGUID,
unk1 : Long, unk1 : Long,
pos : Vector3, pos : Vector3,
unk2 : Int, roll : Int,
unk3 : Int, pitch : Int,
unk4 : Int, yaw : Int,
unk5 : Long) unk2 : Long)
extends PlanetSideGamePacket { extends PlanetSideGamePacket {
type Packet = DeployObjectMessage type Packet = DeployObjectMessage
def opcode = GamePacketOpcode.DeployObjectMessage def opcode = GamePacketOpcode.DeployObjectMessage
@ -32,12 +35,12 @@ final case class DeployObjectMessage(guid : PlanetSideGUID,
object DeployObjectMessage extends Marshallable[DeployObjectMessage] { object DeployObjectMessage extends Marshallable[DeployObjectMessage] {
implicit val codec : Codec[DeployObjectMessage] = ( implicit val codec : Codec[DeployObjectMessage] = (
("guid1" | PlanetSideGUID.codec) :: ("object_guid" | PlanetSideGUID.codec) ::
("unk1" | uint32L) :: ("unk1" | uint32L) ::
("pos" | Vector3.codec_pos) :: ("pos" | Vector3.codec_pos) ::
("unk2" | uint8L) :: ("roll" | uint8L) ::
("unk3" | uint8L) :: ("pitch" | uint8L) ::
("unk4" | uint8L) :: ("yaw" | uint8L) ::
("unk5" | uint32L) ("unk2" | uint32L)
).as[DeployObjectMessage] ).as[DeployObjectMessage]
} }

View file

@ -1,11 +1,11 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package net.psforever.packet.game package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
import scodec.Codec import scodec.Codec
import scodec.codecs._ import scodec.codecs._
final case class KeepAliveMessage(code : Int) extends PlanetSideGamePacket { final case class KeepAliveMessage(code : Int = 0) extends PlanetSideGamePacket {
type Packet = KeepAliveMessage type Packet = KeepAliveMessage
def opcode = GamePacketOpcode.KeepAliveMessage def opcode = GamePacketOpcode.KeepAliveMessage
def encode = KeepAliveMessage.encode(this) def encode = KeepAliveMessage.encode(this)

View file

@ -7,18 +7,18 @@ import scodec.{Attempt, Codec, Err}
import shapeless.{::, HNil} import shapeless.{::, HNil}
/** /**
* na * Update a list of (new) objects that have been detected by this client in one way or another.
* @param guid1 na * @param player_guid1 the player
* @param guid2 na; * @param player_guid2 the player(?);
* often matches with `guid1` * often matches with `player_guid1`
* @param unk1 na * @param unk na
* @param unk2 na; * @param list list of detected objects;
* normally contains at least one element * normally contains at least one element
*/ */
final case class ObjectDetectedMessage(guid1 : PlanetSideGUID, final case class ObjectDetectedMessage(player_guid1 : PlanetSideGUID,
guid2 : PlanetSideGUID, player_guid2 : PlanetSideGUID,
unk1 : Int, unk : Int,
unk2 : List[Int]) list : List[PlanetSideGUID])
extends PlanetSideGamePacket { extends PlanetSideGamePacket {
type Packet = ObjectDetectedMessage type Packet = ObjectDetectedMessage
def opcode = GamePacketOpcode.ObjectDetectedMessage def opcode = GamePacketOpcode.ObjectDetectedMessage
@ -27,22 +27,22 @@ final case class ObjectDetectedMessage(guid1 : PlanetSideGUID,
object ObjectDetectedMessage extends Marshallable[ObjectDetectedMessage] { object ObjectDetectedMessage extends Marshallable[ObjectDetectedMessage] {
implicit val codec : Codec[ObjectDetectedMessage] = ( implicit val codec : Codec[ObjectDetectedMessage] = (
("guid1" | PlanetSideGUID.codec) :: ("player_guid1" | PlanetSideGUID.codec) ::
("guid2" | PlanetSideGUID.codec) :: ("player_guid2" | PlanetSideGUID.codec) ::
("unk1" | uint8L) :: ("unk" | uint8L) ::
("unk2" | listOfN(uintL(6), uint16L)) //TODO are these uids? ("list" | listOfN(uintL(6), PlanetSideGUID.codec))
).exmap[ObjectDetectedMessage] ( ).exmap[ObjectDetectedMessage] (
{ {
case g1 :: g2 :: u1 :: u2 :: HNil => case g1 :: g2 :: u :: lst :: HNil =>
Attempt.successful(ObjectDetectedMessage(g1, g2, u1, u2)) Attempt.successful(ObjectDetectedMessage(g1, g2, u, lst))
}, },
{ {
case ObjectDetectedMessage(g1, g2, u1, u2) => case ObjectDetectedMessage(g1, g2, u, lst) =>
if(u2.size > 63) { if(lst.size > 63) {
Attempt.failure(Err(s"too many list elements (max: 63, actual: ${u2.size})")) Attempt.failure(Err(s"too many list elements (max: 63, actual: ${lst.size})"))
} }
else { else {
Attempt.successful(g1 :: g2 :: u1 :: u2 :: HNil) Attempt.successful(g1 :: g2 :: u :: lst :: HNil)
} }
} }
) )

View file

@ -0,0 +1,33 @@
// Copyright (c) 2017 PSForever
package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
import scodec.bits._
class CharacterCreateRequestMessageTest extends Specification {
val string = hex"2f 88 54006500730074004300680061007200 320590"
"decode" in {
PacketCoding.DecodePacket(string).require match {
case CharacterCreateRequestMessage(name, head, voice, gender, faction) =>
name mustEqual "TestChar"
head mustEqual 50
voice mustEqual 5
gender mustEqual CharacterGender.Female
faction mustEqual PlanetSideEmpire.NC
case _ =>
ko
}
}
"encode" in {
val msg = CharacterCreateRequestMessage("TestChar", 50, 5, CharacterGender.Female, PlanetSideEmpire.NC)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
}
}

View file

@ -0,0 +1,32 @@
// Copyright (c) 2017 PSForever
package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
import scodec.bits._
class CharacterInfoMessageTest extends Specification {
val string = hex"14 0F000000 10270000C1D87A024B00265CB08000"
"decode" in {
PacketCoding.DecodePacket(string).require match {
case CharacterInfoMessage(unk, zone, charId, guid, finished, last) =>
unk mustEqual 15L
zone mustEqual PlanetSideZoneID(10000)
charId mustEqual 41605313L
guid mustEqual PlanetSideGUID(75)
finished mustEqual false
last mustEqual 6404428L
case _ =>
ko
}
}
"encode" in {
val msg = CharacterInfoMessage(15L, PlanetSideZoneID(10000), 41605313L, PlanetSideGUID(75), false, 6404428L)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
}
}

View file

@ -0,0 +1,30 @@
// Copyright (c) 2017 PSForever
package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
import net.psforever.types.{CharacterGender, PlanetSideEmpire}
import scodec.bits._
class CharacterRequestMessageTest extends Specification {
val string = hex"30 c1d87a02 00000000"
"decode" in {
PacketCoding.DecodePacket(string).require match {
case CharacterRequestMessage(charId, action) =>
charId mustEqual 41605313L
action mustEqual CharacterRequestAction.Select
case _ =>
ko
}
}
"encode" in {
val msg = CharacterRequestMessage(41605313L, CharacterRequestAction.Select)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
}
}

View file

@ -8,28 +8,27 @@ import net.psforever.types.Vector3
import scodec.bits._ import scodec.bits._
class DeployObjectMessageTest extends Specification { class DeployObjectMessageTest extends Specification {
//fake data; see comments in packet; this test exists to maintain code coverage val string = hex"5d 740b e8030000 a644b 6e3c6 7e18 00 00 3f 01000000"
val string = hex"5D 0000 00000000 00000 00000 0000 00 00 00 00000000"
"decode" in { "decode" in {
PacketCoding.DecodePacket(string).require match { PacketCoding.DecodePacket(string).require match {
case DeployObjectMessage(guid, unk1, pos, unk2, unk3, unk4, unk5) => case DeployObjectMessage(guid, unk1, pos, roll, pitch, yaw, unk2) =>
guid mustEqual PlanetSideGUID(0) guid mustEqual PlanetSideGUID(2932)
unk1 mustEqual 0L unk1 mustEqual 1000L
pos.x mustEqual 0f pos.x mustEqual 5769.297f
pos.y mustEqual 0f pos.y mustEqual 3192.8594f
pos.z mustEqual 0f pos.z mustEqual 97.96875f
unk2 mustEqual 0 roll mustEqual 0
unk3 mustEqual 0 pitch mustEqual 0
unk4 mustEqual 0 yaw mustEqual 63
unk5 mustEqual 0L unk2 mustEqual 1L
case _ => case _ =>
ko ko
} }
} }
"encode" in { "encode" in {
val msg = DeployObjectMessage(PlanetSideGUID(0), 0L, Vector3(0f, 0f, 0f), 0, 0, 0, 0L) val msg = DeployObjectMessage(PlanetSideGUID(2932), 1000L, Vector3(5769.297f, 3192.8594f, 97.96875f), 0, 0, 63, 1L)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string pkt mustEqual string

View file

@ -0,0 +1,28 @@
// Copyright (c) 2017 PSForever
package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
import scodec.bits._
class KeepAliveMessageTest extends Specification {
val string = hex"BA 0000"
"decode" in {
PacketCoding.DecodePacket(string).require match {
case KeepAliveMessage(code) =>
code mustEqual 0
case _ =>
ko
}
}
"encode" in {
val msg = KeepAliveMessage()
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
}
}

View file

@ -11,25 +11,37 @@ class ObjectDetectedMessageTest extends Specification {
"decode" in { "decode" in {
PacketCoding.DecodePacket(string).require match { PacketCoding.DecodePacket(string).require match {
case ObjectDetectedMessage(guid1, guid2, unk1, unk2) => case ObjectDetectedMessage(guid1, guid2, unk1, list) =>
guid1 mustEqual PlanetSideGUID(4070) guid1 mustEqual PlanetSideGUID(4070)
guid2 mustEqual PlanetSideGUID(4070) guid2 mustEqual PlanetSideGUID(4070)
unk1 mustEqual 0 unk1 mustEqual 0
unk2.size mustEqual 7 list.size mustEqual 7
unk2.head mustEqual 3623 list.head mustEqual PlanetSideGUID(3623)
unk2(1) mustEqual 3198 list(1) mustEqual PlanetSideGUID(3198)
unk2(2) mustEqual 3088 list(2) mustEqual PlanetSideGUID(3088)
unk2(3) mustEqual 1579 list(3) mustEqual PlanetSideGUID(1579)
unk2(4) mustEqual 1578 list(4) mustEqual PlanetSideGUID(1578)
unk2(5) mustEqual 3341 list(5) mustEqual PlanetSideGUID(3341)
unk2(6) mustEqual 2997 list(6) mustEqual PlanetSideGUID(2997)
case _ => case _ =>
ko ko
} }
} }
"encode" in { "encode" in {
val msg = ObjectDetectedMessage(PlanetSideGUID(4070), PlanetSideGUID(4070), 0, 3623 :: 3198 :: 3088 :: 1579 :: 1578 :: 3341 :: 2997 :: Nil) val msg = ObjectDetectedMessage(
PlanetSideGUID(4070),
PlanetSideGUID(4070),
0,
PlanetSideGUID(3623) ::
PlanetSideGUID(3198) ::
PlanetSideGUID(3088) ::
PlanetSideGUID(1579) ::
PlanetSideGUID(1578) ::
PlanetSideGUID(3341) ::
PlanetSideGUID(2997) ::
Nil
)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string pkt mustEqual string

View file

@ -0,0 +1,27 @@
// Copyright (c) 2017 PSForever
package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
import scodec.bits._
class VoiceHostInfoTest extends Specification {
val string_info = hex"b2 4b00"
"decode" in {
PacketCoding.DecodePacket(string_info).require match {
case VoiceHostInfo(player, _) =>
player mustEqual PlanetSideGUID(75)
case _ =>
ko
}
}
"encode" in {
val msg = VoiceHostInfo(PlanetSideGUID(75), ByteVector.empty)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_info
}
}

View file

@ -0,0 +1,27 @@
// Copyright (c) 2017 PSForever
package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
import scodec.bits._
class VoiceHostKillTest extends Specification {
val string_kill = hex"b1"
"decode" in {
PacketCoding.DecodePacket(string_kill).require match {
case VoiceHostKill() =>
ok
case _ =>
ko
}
}
"encode" in {
val msg = VoiceHostKill()
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_kill
}
}

View file

@ -0,0 +1,28 @@
// Copyright (c) 2017 PSForever
package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
import scodec.bits._
class VoiceHostRequestTest extends Specification {
val string_request = hex"b0 2580 00"
"decode" in {
PacketCoding.DecodePacket(string_request).require match {
case VoiceHostRequest(unk, player, _) =>
unk mustEqual false
player mustEqual PlanetSideGUID(75)
case _ =>
ko
}
}
"encode" in {
val msg = VoiceHostRequest(false, PlanetSideGUID(75), ByteVector.empty)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_request
}
}

View file

@ -177,7 +177,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
// NOTE: PlanetSideZoneID just chooses the background // NOTE: PlanetSideZoneID just chooses the background
sendResponse(PacketCoding.CreateGamePacket(0, sendResponse(PacketCoding.CreateGamePacket(0,
CharacterInfoMessage(PlanetSideZoneID(1), 0, PlanetSideGUID(0), true, 0))) CharacterInfoMessage(0, PlanetSideZoneID(1), 0, PlanetSideGUID(0), true, 0)))
case msg @ CharacterRequestMessage(charId, action) => case msg @ CharacterRequestMessage(charId, action) =>
log.info("Handling " + msg) log.info("Handling " + msg)
@ -241,10 +241,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, ActionResultMessage(true, None))) sendResponse(PacketCoding.CreateGamePacket(0, ActionResultMessage(true, None)))
sendResponse(PacketCoding.CreateGamePacket(0, sendResponse(PacketCoding.CreateGamePacket(0,
CharacterInfoMessage(PlanetSideZoneID(0), 0, PlanetSideGUID(0), true, 0))) CharacterInfoMessage(0, PlanetSideZoneID(0), 0, PlanetSideGUID(0), true, 0)))
case KeepAliveMessage(code) => case KeepAliveMessage(code) =>
sendResponse(PacketCoding.CreateGamePacket(0, KeepAliveMessage(0))) sendResponse(PacketCoding.CreateGamePacket(0, KeepAliveMessage()))
case msg @ BeginZoningMessage() => case msg @ BeginZoningMessage() =>
log.info("Reticulating splines ...") log.info("Reticulating splines ...")
@ -366,10 +366,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
// TODO: This should only actually be sent to doors upon opening; may break non-door items upon use // TODO: This should only actually be sent to doors upon opening; may break non-door items upon use
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(object_guid, 16))) sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(object_guid, 16)))
} }
case msg @ UnuseItemMessage(player, item) => case msg @ UnuseItemMessage(player, item) =>
log.info("UnuseItem: " + msg) log.info("UnuseItem: " + msg)
case msg @ DeployObjectMessage(guid, unk1, pos, roll, pitch, yaw, unk2) =>
log.info("DeployObject: " + msg)
case msg @ GenericObjectStateMsg(object_guid, unk1) => case msg @ GenericObjectStateMsg(object_guid, unk1) =>
log.info("GenericObjectState: " + msg) log.info("GenericObjectState: " + msg)