mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-19 18:14:44 +00:00
replaced hamfisted greedy Mold functionality with seamless codec functionality, though that did require a significant re-write, and I might have re-introduced slow compilation; inventory hobbled intentionally
This commit is contained in:
parent
b6eed0dbbc
commit
d66489a572
|
|
@ -1,9 +1,8 @@
|
|||
// Copyright (c) 2016 PSForever.net to present
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.game.objectcreate.Mold
|
||||
import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectClass}
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||
import scodec.bits._
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
|
@ -49,35 +48,37 @@ case class ObjectCreateMessageParent(guid : PlanetSideGUID,
|
|||
* @param objectClass the code for the type of object being constructed
|
||||
* @param guid the GUID this object will be assigned
|
||||
* @param parentInfo if defined, the relationship between this object and another object (its parent)
|
||||
* @param mold the data used to construct this type of object;
|
||||
* requires further object-specific processing
|
||||
* @param data the data used to construct this type of object
|
||||
*/
|
||||
case class ObjectCreateMessage(streamLength : Long,
|
||||
objectClass : Int,
|
||||
guid : PlanetSideGUID,
|
||||
parentInfo : Option[ObjectCreateMessageParent],
|
||||
mold : Mold)
|
||||
data : Option[ConstructorData])
|
||||
extends PlanetSideGamePacket {
|
||||
def opcode = GamePacketOpcode.ObjectCreateMessage
|
||||
def encode = ObjectCreateMessage.encode(this)
|
||||
}
|
||||
|
||||
object ObjectCreateMessage extends Marshallable[ObjectCreateMessage] {
|
||||
type Pattern = Int :: PlanetSideGUID :: Option[ObjectCreateMessageParent] :: HNil
|
||||
type Pattern = Int :: PlanetSideGUID :: Option[ObjectCreateMessageParent] :: Option[ConstructorData] :: HNil
|
||||
type outPattern = Long :: Int :: PlanetSideGUID :: Option[ObjectCreateMessageParent] :: Option[ConstructorData] :: HNil
|
||||
/**
|
||||
* Codec for formatting around the lack of parent data in the stream.
|
||||
*/
|
||||
val noParent : Codec[Pattern] = (
|
||||
("objectClass" | uintL(0xb)) :: //11u
|
||||
("guid" | PlanetSideGUID.codec) //16u
|
||||
("objectClass" | uintL(0xb)) >>:~ { cls => //11u
|
||||
("guid" | PlanetSideGUID.codec) :: //16u
|
||||
("data" | ObjectClass.selectDataCodec(cls))
|
||||
}
|
||||
).xmap[Pattern] (
|
||||
{
|
||||
case cls :: guid :: HNil =>
|
||||
cls :: guid :: None :: HNil
|
||||
case cls :: guid :: data :: HNil =>
|
||||
cls :: guid :: None :: data :: HNil
|
||||
},
|
||||
{
|
||||
case cls :: guid :: None :: HNil =>
|
||||
cls :: guid :: HNil
|
||||
case cls :: guid :: None :: data :: HNil =>
|
||||
cls :: guid :: data :: HNil
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -86,17 +87,19 @@ object ObjectCreateMessage extends Marshallable[ObjectCreateMessage] {
|
|||
*/
|
||||
val parent : Codec[Pattern] = (
|
||||
("parentGuid" | PlanetSideGUID.codec) :: //16u
|
||||
("objectClass" | uintL(0xb)) :: //11u
|
||||
("guid" | PlanetSideGUID.codec) :: //16u
|
||||
("parentSlotIndex" | PacketHelpers.encodedStringSize) //8u or 16u
|
||||
(("objectClass" | uintL(0xb)) >>:~ { cls => //11u
|
||||
("guid" | PlanetSideGUID.codec) :: //16u
|
||||
("parentSlotIndex" | PacketHelpers.encodedStringSize) :: //8u or 16u
|
||||
("data" | ObjectClass.selectDataCodec(cls))
|
||||
})
|
||||
).xmap[Pattern] (
|
||||
{
|
||||
case pguid :: cls :: guid :: slot :: HNil =>
|
||||
cls :: guid :: Some(ObjectCreateMessageParent(pguid, slot)) :: HNil
|
||||
case pguid :: cls :: guid :: slot :: data :: HNil =>
|
||||
cls :: guid :: Some(ObjectCreateMessageParent(pguid, slot)) :: data :: HNil
|
||||
},
|
||||
{
|
||||
case cls :: guid :: Some(ObjectCreateMessageParent(pguid, slot)) :: HNil =>
|
||||
pguid :: cls :: guid :: slot :: HNil
|
||||
case cls :: guid :: Some(ObjectCreateMessageParent(pguid, slot)) :: data :: HNil =>
|
||||
pguid :: cls :: guid :: slot :: data :: HNil
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -114,16 +117,16 @@ object ObjectCreateMessage extends Marshallable[ObjectCreateMessage] {
|
|||
* @param data the data length is indeterminate until it is read
|
||||
* @return the total length of the stream in bits
|
||||
*/
|
||||
private def streamLen(parentInfo : Option[ObjectCreateMessageParent], data : BitVector) : Long = {
|
||||
//known length
|
||||
val first : Long = if(parentInfo.isDefined) {
|
||||
if(parentInfo.get.slot > 127) 92L else 84L //60u + 16u + (8u or 16u)
|
||||
private def streamLen(parentInfo : Option[ObjectCreateMessageParent], data : Option[ConstructorData]) : Long = {
|
||||
//msg length
|
||||
val first : Long = if(parentInfo.isDefined) { //(32u + 1u + 11u + 16u) ?+ (16u + (8u | 16u))
|
||||
if(parentInfo.get.slot > 127) 92L else 84L
|
||||
}
|
||||
else {
|
||||
60L
|
||||
}
|
||||
//variant length
|
||||
var second : Long = data.size
|
||||
//data length
|
||||
var second : Long = if(data.isDefined) data.get.bsize else 0L
|
||||
val secondMod4 : Long = second % 4L
|
||||
if(secondMod4 > 0L) { //pad to include last whole nibble
|
||||
second += 4L - secondMod4
|
||||
|
|
@ -133,34 +136,142 @@ object ObjectCreateMessage extends Marshallable[ObjectCreateMessage] {
|
|||
|
||||
implicit val codec : Codec[ObjectCreateMessage] = (
|
||||
("streamLength" | uint32L) ::
|
||||
(either(bool, parent, noParent).exmap[Pattern] (
|
||||
either(bool, parent, noParent).exmap[Pattern] (
|
||||
{
|
||||
case Left(a :: b :: Some(c) :: HNil) =>
|
||||
Attempt.successful(a :: b :: Some(c) :: HNil) //true, _, _, Some(c)
|
||||
case Right(a :: b :: None :: HNil) =>
|
||||
Attempt.successful(a :: b :: None :: HNil) //false, _, _, None
|
||||
case Left(a :: b :: Some(c) :: d :: HNil) =>
|
||||
Attempt.successful(a :: b :: Some(c) :: d :: HNil) //true, _, _, Some(c)
|
||||
case Right(a :: b :: None :: d :: HNil) =>
|
||||
Attempt.successful(a :: b :: None :: d :: HNil) //false, _, _, None
|
||||
// failure cases
|
||||
case Left(a :: b :: None :: HNil) =>
|
||||
case Left(a :: b :: None :: _ :: HNil) =>
|
||||
Attempt.failure(Err("missing parent structure")) //true, _, _, None
|
||||
case Right(a :: b :: Some(c) :: HNil) =>
|
||||
case Right(a :: b :: Some(c) :: _ :: HNil) =>
|
||||
Attempt.failure(Err("unexpected parent structure")) //false, _, _, Some(c)
|
||||
},
|
||||
{
|
||||
case a :: b :: Some(c) :: HNil =>
|
||||
Attempt.successful(Left(a :: b :: Some(c) :: HNil))
|
||||
case a :: b :: None :: HNil =>
|
||||
Attempt.successful(Right(a :: b :: None :: HNil))
|
||||
case a :: b :: Some(c) :: d :: HNil =>
|
||||
Attempt.successful(Left(a :: b :: Some(c) :: d :: HNil))
|
||||
case a :: b :: None :: d :: HNil =>
|
||||
Attempt.successful(Right(a :: b :: None :: d :: HNil))
|
||||
}
|
||||
) :+
|
||||
("data" | bits) )
|
||||
).xmap[ObjectCreateMessage] (
|
||||
)
|
||||
).xmap[outPattern] (
|
||||
{
|
||||
case len :: cls :: guid :: info :: data :: HNil =>
|
||||
ObjectCreateMessage(len, cls, guid, info, Mold(cls, data))
|
||||
case len :: cls :: guid :: par :: data :: HNil =>
|
||||
len :: cls :: guid :: par :: data :: HNil
|
||||
},
|
||||
{
|
||||
case ObjectCreateMessage(_, cls, guid, info, mold) =>
|
||||
streamLen(info, mold.data) :: cls :: guid :: info :: mold.data :: HNil
|
||||
case _ :: cls :: guid :: par :: data :: HNil =>
|
||||
streamLen(par, data) :: cls :: guid :: par :: data :: HNil
|
||||
}
|
||||
).as[ObjectCreateMessage]
|
||||
}
|
||||
|
||||
//import net.psforever.packet.game.objectcreate.Mold
|
||||
//import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||
//import scodec.bits._
|
||||
//import scodec.{Attempt, Codec, Err}
|
||||
//import scodec.codecs._
|
||||
//import shapeless.{::, HNil}
|
||||
//
|
||||
//case class ObjectCreateMessageParent(guid : PlanetSideGUID,
|
||||
// slot : Int)
|
||||
//
|
||||
//case class ObjectCreateMessage(streamLength : Long,
|
||||
// objectClass : Int,
|
||||
// guid : PlanetSideGUID,
|
||||
// parentInfo : Option[ObjectCreateMessageParent],
|
||||
// mold : Mold)
|
||||
// extends PlanetSideGamePacket {
|
||||
// def opcode = GamePacketOpcode.ObjectCreateMessage
|
||||
// def encode = ObjectCreateMessage.encode(this)
|
||||
//}
|
||||
//
|
||||
//object ObjectCreateMessage extends Marshallable[ObjectCreateMessage] {
|
||||
// type Pattern = Int :: PlanetSideGUID :: Option[ObjectCreateMessageParent] :: HNil
|
||||
// /**
|
||||
// * Codec for formatting around the lack of parent data in the stream.
|
||||
// */
|
||||
// val noParent : Codec[Pattern] = (
|
||||
// ("objectClass" | uintL(0xb)) :: //11u
|
||||
// ("guid" | PlanetSideGUID.codec) //16u
|
||||
// ).xmap[Pattern] (
|
||||
// {
|
||||
// case cls :: guid :: HNil =>
|
||||
// cls :: guid :: None :: HNil
|
||||
// },
|
||||
// {
|
||||
// case cls :: guid :: None :: HNil =>
|
||||
// cls :: guid :: HNil
|
||||
// }
|
||||
// )
|
||||
//
|
||||
// /**
|
||||
// * Codec for reading and formatting parent data from the stream.
|
||||
// */
|
||||
// val parent : Codec[Pattern] = (
|
||||
// ("parentGuid" | PlanetSideGUID.codec) :: //16u
|
||||
// ("objectClass" | uintL(0xb)) :: //11u
|
||||
// ("guid" | PlanetSideGUID.codec) :: //16u
|
||||
// ("parentSlotIndex" | PacketHelpers.encodedStringSize) //8u or 16u
|
||||
// ).xmap[Pattern] (
|
||||
// {
|
||||
// case pguid :: cls :: guid :: slot :: HNil =>
|
||||
// cls :: guid :: Some(ObjectCreateMessageParent(pguid, slot)) :: HNil
|
||||
// },
|
||||
// {
|
||||
// case cls :: guid :: Some(ObjectCreateMessageParent(pguid, slot)) :: HNil =>
|
||||
// pguid :: cls :: guid :: slot :: HNil
|
||||
// }
|
||||
// )
|
||||
//
|
||||
// private def streamLen(parentInfo : Option[ObjectCreateMessageParent], data : BitVector) : Long = {
|
||||
// //known length
|
||||
// val first : Long = if(parentInfo.isDefined) {
|
||||
// if(parentInfo.get.slot > 127) 92L else 84L //60u + 16u + (8u or 16u)
|
||||
// }
|
||||
// else {
|
||||
// 60L
|
||||
// }
|
||||
// //variant length
|
||||
// var second : Long = data.size
|
||||
// val secondMod4 : Long = second % 4L
|
||||
// if(secondMod4 > 0L) { //pad to include last whole nibble
|
||||
// second += 4L - secondMod4
|
||||
// }
|
||||
// first + second
|
||||
// }
|
||||
//
|
||||
// implicit val codec : Codec[ObjectCreateMessage] = (
|
||||
// ("streamLength" | uint32L) ::
|
||||
// (either(bool, parent, noParent).exmap[Pattern] (
|
||||
// {
|
||||
// case Left(a :: b :: Some(c) :: HNil) =>
|
||||
// Attempt.successful(a :: b :: Some(c) :: HNil) //true, _, _, Some(c)
|
||||
// case Right(a :: b :: None :: HNil) =>
|
||||
// Attempt.successful(a :: b :: None :: HNil) //false, _, _, None
|
||||
// // failure cases
|
||||
// case Left(a :: b :: None :: HNil) =>
|
||||
// Attempt.failure(Err("missing parent structure")) //true, _, _, None
|
||||
// case Right(a :: b :: Some(c) :: HNil) =>
|
||||
// Attempt.failure(Err("unexpected parent structure")) //false, _, _, Some(c)
|
||||
// },
|
||||
// {
|
||||
// case a :: b :: Some(c) :: HNil =>
|
||||
// Attempt.successful(Left(a :: b :: Some(c) :: HNil))
|
||||
// case a :: b :: None :: HNil =>
|
||||
// Attempt.successful(Right(a :: b :: None :: HNil))
|
||||
// }
|
||||
// ) :+
|
||||
// ("data" | bits) )
|
||||
// ).xmap[ObjectCreateMessage] (
|
||||
// {
|
||||
// case len :: cls :: guid :: info :: data :: HNil =>
|
||||
// ObjectCreateMessage(len, cls, guid, info, Mold(cls, data))
|
||||
// },
|
||||
// {
|
||||
// case ObjectCreateMessage(_, cls, guid, info, mold) =>
|
||||
// streamLen(info, mold.data) :: cls :: guid :: info :: mold.data :: HNil
|
||||
// }
|
||||
// ).as[ObjectCreateMessage]
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,10 @@ import scodec.{Attempt, Codec, Err}
|
|||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
case class AmmoBoxData(magazine : Int) extends ConstructorData
|
||||
case class AmmoBoxData(magazine : Int
|
||||
) extends ConstructorData {
|
||||
override def bsize : Long = 39L
|
||||
}
|
||||
|
||||
object AmmoBoxData extends Marshallable[AmmoBoxData] {
|
||||
implicit val codec : Codec[AmmoBoxData] = (
|
||||
|
|
@ -24,5 +27,18 @@ object AmmoBoxData extends Marshallable[AmmoBoxData] {
|
|||
case AmmoBoxData(mag) =>
|
||||
Attempt.successful(0xC8 :: () :: mag :: HNil)
|
||||
}
|
||||
).as[AmmoBoxData]
|
||||
)
|
||||
|
||||
val genericCodec : Codec[ConstructorData.genericPattern] = codec.exmap[ConstructorData.genericPattern] (
|
||||
{
|
||||
case x =>
|
||||
Attempt.successful(Some(x.asInstanceOf[ConstructorData]))
|
||||
},
|
||||
{
|
||||
case Some(x) =>
|
||||
Attempt.successful(x.asInstanceOf[AmmoBoxData])
|
||||
case _ =>
|
||||
Attempt.failure(Err(""))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package net.psforever.packet.game.objectcreate
|
|||
|
||||
import net.psforever.packet.{Marshallable, PacketHelpers}
|
||||
import net.psforever.types.Vector3
|
||||
import scodec.Codec
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
|
||||
case class CharacterData(pos : Vector3,
|
||||
|
|
@ -41,9 +41,36 @@ case class CharacterData(pos : Vector3,
|
|||
firstTimeEvent_list : List[String],
|
||||
tutorial_list : List[String],
|
||||
inventory : InventoryData
|
||||
) extends ConstructorData
|
||||
) extends ConstructorData {
|
||||
override def bsize : Long = {
|
||||
//represents static fields
|
||||
val first : Long = 1194L //TODO due to changing understanding of the bit patterns in this data, this value will change
|
||||
//name
|
||||
val second : Long = CharacterData.stringBitSize(name, 16) + 4L //plus the padding
|
||||
//fte_list
|
||||
var third : Long = 32L
|
||||
if(firstEntry.isDefined) {
|
||||
third += CharacterData.stringBitSize(firstEntry.get) + 5L //plus the padding
|
||||
for(str <- firstTimeEvent_list) {
|
||||
third += CharacterData.stringBitSize(str)
|
||||
}
|
||||
}
|
||||
//tutorial list
|
||||
var fourth : Long = 32L
|
||||
for(str <- tutorial_list) {
|
||||
fourth += CharacterData.stringBitSize(str)
|
||||
}
|
||||
first + second + third + fourth + inventory.bsize
|
||||
}
|
||||
}
|
||||
|
||||
object CharacterData extends Marshallable[CharacterData] {
|
||||
private def stringBitSize(str : String, width : Int = 8) : Long = {
|
||||
val strlen = str.length
|
||||
val lenSize = if(strlen > 127) 16L else 8L
|
||||
lenSize + strlen * width
|
||||
}
|
||||
|
||||
implicit val codec : Codec[CharacterData] = (
|
||||
("pos" | Vector3.codec_pos) ::
|
||||
ignore(16) ::
|
||||
|
|
@ -56,7 +83,7 @@ object CharacterData extends Marshallable[CharacterData] {
|
|||
("exosuit" | uintL(3)) ::
|
||||
ignore(2) ::
|
||||
("sex" | uintL(2)) ::
|
||||
("face1" | uint8L) ::
|
||||
("face1" | uint4L) ::
|
||||
("face2" | uint4L) ::
|
||||
("voice" | uintL(3)) ::
|
||||
ignore(22) ::
|
||||
|
|
@ -98,4 +125,17 @@ object CharacterData extends Marshallable[CharacterData] {
|
|||
("inventory" | InventoryData.codec)
|
||||
})
|
||||
).as[CharacterData]
|
||||
|
||||
val genericCodec : Codec[ConstructorData.genericPattern] = codec.exmap[ConstructorData.genericPattern] (
|
||||
{
|
||||
case x =>
|
||||
Attempt.successful(Some(x.asInstanceOf[ConstructorData]))
|
||||
},
|
||||
{
|
||||
case Some(x) =>
|
||||
Attempt.successful(x.asInstanceOf[CharacterData])
|
||||
case _ =>
|
||||
Attempt.failure(Err(""))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,10 @@
|
|||
// Copyright (c) 2016 PSForever.net to present
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
abstract class ConstructorData
|
||||
abstract class ConstructorData() {
|
||||
def bsize : Long = 0L
|
||||
}
|
||||
|
||||
object ConstructorData {
|
||||
type genericPattern = Option[ConstructorData]
|
||||
}
|
||||
|
|
@ -3,8 +3,7 @@ package net.psforever.packet.game.objectcreate
|
|||
|
||||
import net.psforever.packet.{Marshallable, PacketHelpers}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.bits.BitVector
|
||||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
|
|
@ -19,40 +18,23 @@ import shapeless.{::, HNil}
|
|||
case class InternalSlot(objectClass : Int,
|
||||
guid : PlanetSideGUID,
|
||||
parentSlot : Int,
|
||||
obj : Option[ConstructorData])
|
||||
obj : Option[ConstructorData]) {
|
||||
def bsize : Long = {
|
||||
val first : Long = if(parentSlot > 127) 44L else 36L
|
||||
val second : Long = if(obj.isDefined) obj.get.bsize else 0L
|
||||
first + second
|
||||
}
|
||||
}
|
||||
|
||||
object InternalSlot extends Marshallable[InternalSlot] {
|
||||
type objPattern = Int :: PlanetSideGUID :: Int :: Option[ConstructorData] :: HNil
|
||||
type objPattern = Int :: PlanetSideGUID :: Int :: ConstructorData :: HNil
|
||||
|
||||
implicit val codec : Codec[InternalSlot] = (
|
||||
ignore(1) :: //TODO determine what this bit does
|
||||
("objectClass" | uintL(11)) ::
|
||||
("guid" | PlanetSideGUID.codec) ::
|
||||
("parentSlot" | PacketHelpers.encodedStringSize) ::
|
||||
bits
|
||||
).exmap[objPattern] (
|
||||
{
|
||||
case _ :: cls :: guid :: slot :: data :: HNil =>
|
||||
Attempt.successful(cls :: guid :: slot :: Mold.decode(cls, data) :: HNil)
|
||||
},
|
||||
{
|
||||
case cls :: guid :: slot :: None :: HNil =>
|
||||
Attempt.failure(Err("no constructor data could be found"))
|
||||
case cls :: guid :: slot :: mold :: HNil =>
|
||||
Attempt.successful(() :: cls :: guid :: slot :: Mold.encode(cls, mold.get) :: HNil)
|
||||
}
|
||||
).exmap[objPattern] (
|
||||
{
|
||||
case cls :: guid :: slot :: None :: HNil =>
|
||||
Attempt.failure(Err("no decoded constructor data"))
|
||||
case cls :: guid :: slot :: mold :: HNil =>
|
||||
Attempt.successful(cls :: guid :: slot :: mold :: HNil)
|
||||
},
|
||||
{
|
||||
case cls :: guid :: slot :: BitVector.empty :: HNil =>
|
||||
Attempt.failure(Err("no encoded constructor data"))
|
||||
case cls :: guid :: slot :: data :: HNil =>
|
||||
Attempt.successful(cls :: guid :: slot :: data :: HNil)
|
||||
}
|
||||
).as[InternalSlot]
|
||||
}
|
||||
(("objectClass" | uintL(11)) >>:~ { obj_cls =>
|
||||
("guid" | PlanetSideGUID.codec) ::
|
||||
("parentSlot" | PacketHelpers.encodedStringSize) ::
|
||||
("obj" | ObjectClass.selectDataCodec(obj_cls))
|
||||
})
|
||||
).as[InternalSlot]
|
||||
}
|
||||
|
|
@ -3,19 +3,24 @@ package net.psforever.packet.game.objectcreate
|
|||
|
||||
import net.psforever.packet.{Marshallable, PacketHelpers}
|
||||
import scodec.Codec
|
||||
import scodec.bits.BitVector
|
||||
import scodec.codecs._
|
||||
|
||||
case class InventoryData(unk1 : Boolean,
|
||||
size : Int,
|
||||
unk2 : Boolean,
|
||||
inv : List[InventoryItem])
|
||||
unk2 : Boolean){//,
|
||||
//inv : List[InventoryItem]) {
|
||||
def bsize : Long = {
|
||||
10L
|
||||
}
|
||||
}
|
||||
|
||||
object InventoryData extends Marshallable[InventoryData] {
|
||||
implicit val codec : Codec[InventoryData] = (
|
||||
("unk1" | bool) ::
|
||||
(("size" | uint8L) >>:~ { len =>
|
||||
("unk2" | bool) ::
|
||||
("inv" | PacketHelpers.listOfNSized(len, InventoryItem.codec))
|
||||
("unk2" | bool).hlist// ::
|
||||
//("inv" | PacketHelpers.listOfNSized(len, InventoryItem.codec))
|
||||
})
|
||||
).as[InventoryData]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,13 +5,10 @@ import net.psforever.packet.Marshallable
|
|||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
|
||||
case class InventoryItem(item : InternalSlot,
|
||||
na : Option[Boolean] = None)
|
||||
case class InventoryItem(item : InternalSlot)
|
||||
|
||||
object InventoryItem extends Marshallable[InventoryItem] {
|
||||
implicit val codec : Codec[InventoryItem] = (
|
||||
"item" | InternalSlot.codec >>:~ { item =>
|
||||
conditional(item.obj.isDefined && item.obj.get.isInstanceOf[WeaponData], bool).hlist
|
||||
}
|
||||
"item" | InternalSlot.codec
|
||||
).as[InventoryItem]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ package net.psforever.packet.game.objectcreate
|
|||
import scodec.DecodeResult
|
||||
import scodec.bits.BitVector
|
||||
|
||||
import scala.annotation.switch
|
||||
|
||||
case class Mold(objectClass : Int,
|
||||
data : BitVector) {
|
||||
private var obj : Option[ConstructorData] = Mold.decode(objectClass, data)
|
||||
|
|
@ -31,33 +29,10 @@ object Mold {
|
|||
def decode(objClass : Int, data : BitVector) : Option[ConstructorData] = {
|
||||
var out : Option[ConstructorData] = None
|
||||
if(!data.isEmpty) {
|
||||
val codec = ObjectClass.selectDataCodec(objClass)
|
||||
var outOpt : Option[DecodeResult[_]] = None
|
||||
try {
|
||||
(objClass : @switch) match {
|
||||
case 0x79 => //avatars
|
||||
outOpt = CharacterData.codec.decode(data).toOption
|
||||
case 0x1C => //9mm
|
||||
outOpt = AmmoBoxData.codec.decode(data).toOption
|
||||
case 0x1D => //9mm ap
|
||||
outOpt = AmmoBoxData.codec.decode(data).toOption
|
||||
case 0x110 => //plasma
|
||||
outOpt = AmmoBoxData.codec.decode(data).toOption
|
||||
case 0x1C8 => //slot blocker?
|
||||
outOpt = AmmoBoxData.codec.decode(data).toOption
|
||||
case 0x21C => //forceblade (ammo)
|
||||
outOpt = AmmoBoxData.codec.decode(data).toOption
|
||||
case 0x46 => //beamer
|
||||
outOpt = WeaponData.codec.decode(data).toOption
|
||||
case 0x144 => //forceblade
|
||||
outOpt = WeaponData.codec.decode(data).toOption
|
||||
case 0x159 => //gauss
|
||||
outOpt = WeaponData.codec.decode(data).toOption
|
||||
case 0x34D => //suppressor
|
||||
outOpt = WeaponData.codec.decode(data).toOption
|
||||
case 0x2D8 => //rek
|
||||
outOpt = REKData.codec.decode(data).toOption
|
||||
case _ =>
|
||||
}
|
||||
outOpt = codec.decode(data).toOption
|
||||
if(outOpt.isDefined)
|
||||
out = Some(outOpt.get.value.asInstanceOf[ConstructorData])
|
||||
}
|
||||
|
|
@ -74,31 +49,9 @@ object Mold {
|
|||
def encode(objClass : Int, obj : ConstructorData) : BitVector = {
|
||||
var out = BitVector.empty
|
||||
try {
|
||||
val codec = ObjectClass.selectDataCodec(objClass)
|
||||
var outOpt : Option[BitVector] = None
|
||||
(objClass : @switch) match {
|
||||
case 0x1C => //9mm
|
||||
outOpt = AmmoBoxData.codec.encode(obj.asInstanceOf[AmmoBoxData]).toOption
|
||||
case 0x1D => //9mm ap
|
||||
outOpt = AmmoBoxData.codec.encode(obj.asInstanceOf[AmmoBoxData]).toOption
|
||||
case 0x110 => //plasma
|
||||
outOpt = AmmoBoxData.codec.encode(obj.asInstanceOf[AmmoBoxData]).toOption
|
||||
case 0x1C8 => //slot blocker?
|
||||
outOpt = AmmoBoxData.codec.encode(obj.asInstanceOf[AmmoBoxData]).toOption
|
||||
case 0x21C => //forceblade (ammo)
|
||||
outOpt = AmmoBoxData.codec.encode(obj.asInstanceOf[AmmoBoxData]).toOption
|
||||
case 0x46 => //beamer
|
||||
outOpt = WeaponData.codec.encode(obj.asInstanceOf[WeaponData]).toOption
|
||||
case 0x144 => //forceblade
|
||||
outOpt = WeaponData.codec.encode(obj.asInstanceOf[WeaponData]).toOption
|
||||
case 0x159 => //gauss
|
||||
outOpt = WeaponData.codec.encode(obj.asInstanceOf[WeaponData]).toOption
|
||||
case 0x34D => //suppressor
|
||||
outOpt = WeaponData.codec.encode(obj.asInstanceOf[WeaponData]).toOption
|
||||
case 0x2D8 => //rek
|
||||
outOpt = REKData.codec.encode(obj.asInstanceOf[REKData]).toOption
|
||||
case _ =>
|
||||
throw new ClassCastException("cannot find object code - "+objClass)
|
||||
}
|
||||
outOpt = codec.encode(obj.asInstanceOf[ConstructorData.genericPattern]).toOption
|
||||
if(outOpt.isDefined)
|
||||
out = outOpt.get
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) 2016 PSForever.net to present
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import scodec.Codec
|
||||
|
||||
import scala.annotation.switch
|
||||
|
||||
object ObjectClass {
|
||||
//character
|
||||
final val PLAYER = 0x79
|
||||
//ammunition
|
||||
final val BULLETS_9MM = 0x1C
|
||||
final val BULLETS_9MM_AP = 0x1D
|
||||
final val ENERGY_CELL = 0x110
|
||||
final val FORCE_BLADE_AMMO = 0x21C
|
||||
//weapons
|
||||
final val BEAMER = 0x8C
|
||||
final val FORCE_BLADE = 0x144
|
||||
final val GAUSS = 0x159
|
||||
final val SUPPRESSOR = 0x34D
|
||||
//tools
|
||||
final val REK = 0x2D8
|
||||
//unknown
|
||||
final val SLOT_BLOCKER = 0x1C8
|
||||
|
||||
def selectDataCodec(objClass : Int) : Codec[ConstructorData.genericPattern] = {
|
||||
(objClass : @switch) match {
|
||||
case ObjectClass.PLAYER => CharacterData.genericCodec
|
||||
case ObjectClass.BULLETS_9MM => AmmoBoxData.genericCodec
|
||||
case ObjectClass.BULLETS_9MM_AP => AmmoBoxData.genericCodec
|
||||
case ObjectClass.ENERGY_CELL => AmmoBoxData.genericCodec
|
||||
case ObjectClass.FORCE_BLADE_AMMO => AmmoBoxData.genericCodec
|
||||
case ObjectClass.BEAMER => WeaponData.genericCodec
|
||||
case ObjectClass.FORCE_BLADE => WeaponData.genericCodec
|
||||
case ObjectClass.GAUSS => WeaponData.genericCodec
|
||||
case ObjectClass.SUPPRESSOR => WeaponData.genericCodec
|
||||
case ObjectClass.REK => REKData.genericCodec
|
||||
case ObjectClass.SLOT_BLOCKER => AmmoBoxData.genericCodec
|
||||
case _ => RecoveredData.genericCodec
|
||||
}
|
||||
}
|
||||
|
||||
// val failureCodec : Codec[ConstructorData.genericPattern] = conditional(false, bool).exmap[ConstructorData.genericPattern] (
|
||||
// {
|
||||
// case None | _ =>
|
||||
// Attempt.failure(Err("object class unrecognized during decoding"))
|
||||
// },
|
||||
// {
|
||||
// case None | _ =>
|
||||
// Attempt.failure(Err("object class unrecognized during encoding"))
|
||||
// }
|
||||
// )
|
||||
}
|
||||
|
|
@ -6,7 +6,10 @@ import scodec.{Attempt, Codec, Err}
|
|||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
case class REKData(unk : Int) extends ConstructorData
|
||||
case class REKData(unk : Int
|
||||
) extends ConstructorData {
|
||||
override def bsize : Long = 72L
|
||||
}
|
||||
|
||||
object REKData extends Marshallable[REKData] {
|
||||
implicit val codec : Codec[REKData] = (
|
||||
|
|
@ -29,4 +32,19 @@ object REKData extends Marshallable[REKData] {
|
|||
Attempt.successful(code :: 8 :: () :: 2 :: () :: 8 :: () :: HNil)
|
||||
}
|
||||
).as[REKData]
|
||||
|
||||
|
||||
|
||||
val genericCodec : Codec[ConstructorData.genericPattern] = codec.exmap[ConstructorData.genericPattern] (
|
||||
{
|
||||
case x =>
|
||||
Attempt.successful(Some(x.asInstanceOf[ConstructorData]))
|
||||
},
|
||||
{
|
||||
case Some(x) =>
|
||||
Attempt.successful(x.asInstanceOf[REKData])
|
||||
case _ =>
|
||||
Attempt.failure(Err(""))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2016 PSForever.net to present
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.packet.Marshallable
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import scodec.codecs._
|
||||
import scodec.bits.BitVector
|
||||
|
||||
case class RecoveredData(data : BitVector
|
||||
) extends ConstructorData {
|
||||
override def bsize : Long = data.size
|
||||
}
|
||||
|
||||
object RecoveredData extends Marshallable[RecoveredData] {
|
||||
implicit val codec : Codec[RecoveredData] = (
|
||||
"data" | bits
|
||||
).as[RecoveredData]
|
||||
|
||||
val genericCodec : Codec[ConstructorData.genericPattern] = codec.exmap[ConstructorData.genericPattern] (
|
||||
{
|
||||
case _ =>
|
||||
Attempt.failure(Err("un-parsed byte data preserved when decoding failed"))
|
||||
},
|
||||
{
|
||||
case _ =>
|
||||
Attempt.failure(Err("can not encode object"))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -8,7 +8,9 @@ import scodec.codecs._
|
|||
import shapeless.{::, HNil}
|
||||
|
||||
case class WeaponData(unk : Int,
|
||||
ammo : InternalSlot) extends ConstructorData
|
||||
ammo : InternalSlot) extends ConstructorData {
|
||||
override def bsize : Long = 59L + ammo.bsize
|
||||
}
|
||||
|
||||
object WeaponData extends Marshallable[WeaponData] {
|
||||
def apply(unk : Int, cls : Int, guid : PlanetSideGUID, parentSlot : Int, ammo : AmmoBoxData) : WeaponData =
|
||||
|
|
@ -34,4 +36,19 @@ object WeaponData extends Marshallable[WeaponData] {
|
|||
Attempt.successful(code :: 8 :: () :: 2 :: () :: 0x2C0 :: ammo :: HNil)
|
||||
}
|
||||
).as[WeaponData]
|
||||
|
||||
|
||||
|
||||
val genericCodec : Codec[ConstructorData.genericPattern] = codec.exmap[ConstructorData.genericPattern] (
|
||||
{
|
||||
case x =>
|
||||
Attempt.successful(Some(x.asInstanceOf[ConstructorData]))
|
||||
},
|
||||
{
|
||||
case Some(x) =>
|
||||
Attempt.successful(x.asInstanceOf[WeaponData])
|
||||
case _ =>
|
||||
Attempt.failure(Err(""))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import net.psforever.packet._
|
|||
import net.psforever.packet.game._
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types._
|
||||
import scodec.Attempt
|
||||
import scodec.Attempt.Successful
|
||||
import scodec.bits._
|
||||
|
||||
|
|
@ -147,36 +148,42 @@ class GamePacketTest extends Specification {
|
|||
|
||||
"ObjectCreateMessage" should {
|
||||
val packet = hex"18 CF 13 00 00 BC 87 00 0A F0 16 C3 43 A1 30 90 00 02 C0 40 00 08 70 43 00 68 00 6F 00 72 00 64 00 54 00 52 00 82 65 1F F5 9E 80 80 00 00 00 00 00 3F FF C0 00 00 00 20 00 00 00 20 27 03 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FC CC 10 00 03 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 90 01 90 00 00 00 00 01 00 7E C8 00 C8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 C0 00 42 C5 46 86 C7 00 00 02 A0 00 00 12 60 78 70 65 5F 77 61 72 70 5F 67 61 74 65 5F 75 73 61 67 65 92 78 70 65 5F 69 6E 73 74 61 6E 74 5F 61 63 74 69 6F 6E 92 78 70 65 5F 73 61 6E 63 74 75 61 72 79 5F 68 65 6C 70 91 78 70 65 5F 62 61 74 74 6C 65 5F 72 61 6E 6B 5F 32 8E 78 70 65 5F 66 6F 72 6D 5F 73 71 75 61 64 8E 78 70 65 5F 74 68 5F 6E 6F 6E 73 61 6E 63 8B 78 70 65 5F 74 68 5F 61 6D 6D 6F 90 78 70 65 5F 74 68 5F 66 69 72 65 6D 6F 64 65 73 8F 75 73 65 64 5F 63 68 61 69 6E 62 6C 61 64 65 9A 76 69 73 69 74 65 64 5F 62 72 6F 61 64 63 61 73 74 5F 77 61 72 70 67 61 74 65 8E 76 69 73 69 74 65 64 5F 6C 6F 63 6B 65 72 8D 75 73 65 64 5F 70 75 6E 69 73 68 65 72 88 75 73 65 64 5F 72 65 6B 8D 75 73 65 64 5F 72 65 70 65 61 74 65 72 9F 76 69 73 69 74 65 64 5F 64 65 63 6F 6E 73 74 72 75 63 74 69 6F 6E 5F 74 65 72 6D 69 6E 61 6C 8F 75 73 65 64 5F 73 75 70 70 72 65 73 73 6F 72 96 76 69 73 69 74 65 64 5F 6F 72 64 65 72 5F 74 65 72 6D 69 6E 61 6C 85 6D 61 70 31 35 85 6D 61 70 31 34 85 6D 61 70 31 32 85 6D 61 70 30 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 0A 36 13 88 04 00 40 00 00 10 00 04 00 00 4D 6E 40 10 41 00 00 00 40 00 18 08 38 1C C0 20 32 00 00 07 80 15 E1 D0 02 10 20 00 00 08 00 03 01 07 13 A8 04 06 40 00 00 10 03 20 BB 00 42 E4 00 00 01 00 0E 07 70 08 6C 80 00 06 40 01 C0 F0 01 13 90 00 00 C8 00 38 1E 40 23 32 00 00 19 00 07 03 D0 05 0E 40 00 03 20 00 E8 7B 00 A4 C8 00 00 64 00 DA 4F 80 14 E1 00 00 00 40 00 18 08 38 1F 40 20 32 00 00 0A 00 08 " //fake data?
|
||||
val packet2 = hex"18 F8 00 00 00 BC 8C 10 90 3B 45 C6 FA 94 00 9F F0 00 00 40 00 08 C0 44 00 69 00 66 00 66 00 45" //fake data
|
||||
val packet2Rest = packet2.bits.drop(8 + 32 + 1 + 11 + 16)
|
||||
var string_inventoryItem = hex"46 04 C0 08 08 80 00 00 20 00 0C 04 10 29 A0 10 19 00 00 04 00 00"
|
||||
val string_9mm = hex"18 7C000000 2580 0E0 0005 A1 C8000064000"
|
||||
val string_gauss = hex"18 DC000000 2580 2C9 B905 82 480000020000C04 1C00C0B0190000078000"
|
||||
val string_testchar = hex"18 570C0000 BC8 4B00 6C2D7 65535 CA16 0 00 01 34 40 00 0970 49006C006C006C004900490049006C006C006C0049006C0049006C006C0049006C006C006C0049006C006C004900 84 52 70 76 1E 80 80 00 00 00 00 00 3FFFC 0 00 00 00 20 00 00 0F F6 A7 03 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FD 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 90 01 90 00 64 00 00 01 00 7E C8 00 C8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 C0 00 42 C5 46 86 C7 00 00 00 80 00 00 12 40 78 70 65 5F 73 61 6E 63 74 75 61 72 79 5F 68 65 6C 70 90 78 70 65 5F 74 68 5F 66 69 72 65 6D 6F 64 65 73 8B 75 73 65 64 5F 62 65 61 6D 65 72 85 6D 61 70 31 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 01 23 02 60 04 04 40 00 00 10 00 06 02 08 14 D0 08 0C 80 00 02 00 02 6B 4E 00 82 88 00 00 02 00 00 C0 41 C0 9E 01 01 90 00 00 64 00 44 2A 00 10 91 00 00 00 40 00 18 08 38 94 40 20 32 00 00 00 80 19 05 48 02 17 20 00 00 08 00 70 29 80 43 64 00 00 32 00 0E 05 40 08 9C 80 00 06 40 01 C0 AA 01 19 90 00 00 C8 00 3A 15 80 28 72 00 00 19 00 04 0A B8 05 26 40 00 03 20 06 C2 58 00 A7 88 00 00 02 00 00 80 00 00"
|
||||
val string_testchar = hex"18 570C0000 BC8 4B00 6C2D7 65535 CA16 0 00 01 34 40 00 0970 49006C006C006C004900490049006C006C006C0049006C0049006C006C0049006C006C006C0049006C006C004900 84 52 70 76 1E 80 80 00 00 00 00 00 3FFFC 0 00 00 00 20 00 00 0F F6 A7 03 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FD 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 90 01 90 00 64 00 00 01 00 7E C8 00 C8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 C0 00 42 C5 46 86 C7 00 00 00 80 00 00 12 40 78 70 65 5F 73 61 6E 63 74 75 61 72 79 5F 68 65 6C 70 90 78 70 65 5F 74 68 5F 66 69 72 65 6D 6F 64 65 73 8B 75 73 65 64 5F 62 65 61 6D 65 72 85 6D 61 70 31 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 0A 23 02 60 04 04 40 00 00 10 00 06 02 08 14 D0 08 0C 80 00 02 00 02 6B 4E 00 82 88 00 00 02 00 00 C0 41 C0 9E 01 01 90 00 00 64 00 44 2A 00 10 91 00 00 00 40 00 18 08 38 94 40 20 32 00 00 00 80 19 05 48 02 17 20 00 00 08 00 70 29 80 43 64 00 00 32 00 0E 05 40 08 9C 80 00 06 40 01 C0 AA 01 19 90 00 00 C8 00 3A 15 80 28 72 00 00 19 00 04 0A B8 05 26 40 00 03 20 06 C2 58 00 A7 88 00 00 02 00 00 80 00 00"
|
||||
val invTest = hex"01 01 23 02 60 04 04 40 00 00 10 00 06 02 08 14 D0 08 0C 80 00 02 00 00 00"
|
||||
val invTestWep = hex"23 02 60 04 04 40 00 00 10 00 06 02 08 14 D0 08 0C 80 00 02 00 00 00"
|
||||
|
||||
"decode (2)" in {
|
||||
PacketCoding.DecodePacket(packet2).require match {
|
||||
case obj @ ObjectCreateMessage(len, cls, guid, parent, mold) =>
|
||||
len mustEqual 248 //60 + 188
|
||||
cls mustEqual 121
|
||||
guid mustEqual PlanetSideGUID(2497)
|
||||
parent mustEqual None
|
||||
mold.data mustEqual packet2Rest
|
||||
mold.isDefined mustEqual false
|
||||
case default =>
|
||||
"InventoryTest" in {
|
||||
val intSlot = InternalSlot.codec.decode(invTestWep.toBitVector.drop(1)).toOption
|
||||
intSlot.isDefined mustEqual true
|
||||
|
||||
val invData = InventoryItem.codec.decode(invTestWep.toBitVector.drop(1)).toOption
|
||||
invData.isDefined mustEqual true
|
||||
|
||||
InventoryData.codec.decode(invTest.toBitVector.drop(7)).toOption match {
|
||||
case Some(x) =>
|
||||
x.value.unk1 equals true
|
||||
x.value.size mustEqual 1
|
||||
x.value.unk2 mustEqual false
|
||||
//x.value.inv.head.item.objectClass mustEqual 0x8C
|
||||
//x.value.inv.head.na mustEqual false
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"decode (char)" in {
|
||||
PacketCoding.DecodePacket(string_testchar).require match {
|
||||
case obj @ ObjectCreateMessage(len, cls, guid, parent, mold) =>
|
||||
case obj @ ObjectCreateMessage(len, cls, guid, parent, data) =>
|
||||
len mustEqual 3159
|
||||
cls mustEqual 0x79
|
||||
guid mustEqual PlanetSideGUID(75)
|
||||
parent.isDefined mustEqual false
|
||||
mold.isDefined mustEqual true
|
||||
data.isDefined mustEqual true
|
||||
|
||||
val char = mold.get.asInstanceOf[CharacterData]
|
||||
val char = data.get.asInstanceOf[CharacterData]
|
||||
char.pos.x mustEqual 3674.8438f
|
||||
char.pos.y mustEqual 2726.789f
|
||||
char.pos.z mustEqual 91.15625f
|
||||
|
|
@ -268,24 +275,17 @@ class GamePacketTest extends Specification {
|
|||
}
|
||||
}
|
||||
|
||||
"encode (2)" in {
|
||||
val msg = ObjectCreateMessage(0, 121, PlanetSideGUID(2497), None, Mold(121, packet2Rest))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual packet2
|
||||
}
|
||||
|
||||
"encode (9mm)" in {
|
||||
val obj = Mold(28, AmmoBoxData(50))
|
||||
val msg = ObjectCreateMessage(0, 28, PlanetSideGUID(1280), Some(ObjectCreateMessageParent(PlanetSideGUID(75), 33)), obj)
|
||||
val obj : ConstructorData = AmmoBoxData(50).asInstanceOf[ConstructorData]
|
||||
val msg = ObjectCreateMessage(0, 28, PlanetSideGUID(1280), Some(ObjectCreateMessageParent(PlanetSideGUID(75), 33)), Some(obj))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string_9mm
|
||||
}
|
||||
|
||||
"encode (gauss)" in {
|
||||
val obj = Mold(345, WeaponData(4, 28, PlanetSideGUID(1286), 0, AmmoBoxData(30)))
|
||||
val msg = ObjectCreateMessage(0, 345, PlanetSideGUID(1465), Some(ObjectCreateMessageParent(PlanetSideGUID(75), 2)), obj)
|
||||
val obj : ConstructorData = WeaponData(4, 28, PlanetSideGUID(1286), 0, AmmoBoxData(30)).asInstanceOf[ConstructorData]
|
||||
val msg = ObjectCreateMessage(0, 345, PlanetSideGUID(1465), Some(ObjectCreateMessageParent(PlanetSideGUID(75), 2)), Some(obj))
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string_gauss
|
||||
|
|
|
|||
Loading…
Reference in a new issue