From b6eed0dbbc8ffdcb164c32225d7319d6a6d5685f Mon Sep 17 00:00:00 2001 From: FateJH Date: Sat, 3 Dec 2016 20:55:19 -0500 Subject: [PATCH] split all the classes formerly in ObjectCreateMessage into separate files and placed them into their own package; this restored compiled time down to normal duration from ~20 min (seriously); test failing for known reason --- .../packet/game/ObjectCreateMessage.scala | 308 +----------------- .../game/objectcreate/AmmoBoxData.scala | 28 ++ .../game/objectcreate/CharacterData.scala | 101 ++++++ .../game/objectcreate/ConstructorData.scala | 4 + .../game/objectcreate/InternalSlot.scala | 58 ++++ .../game/objectcreate/InventoryData.scala | 21 ++ .../game/objectcreate/InventoryItem.scala | 17 + .../packet/game/objectcreate/Mold.scala | 113 +++++++ .../packet/game/objectcreate/REKData.scala | 32 ++ .../packet/game/objectcreate/RibbonBars.scala | 20 ++ .../packet/game/objectcreate/WeaponData.scala | 37 +++ common/src/test/scala/GamePacketTest.scala | 14 +- 12 files changed, 443 insertions(+), 310 deletions(-) create mode 100644 common/src/main/scala/net/psforever/packet/game/objectcreate/AmmoBoxData.scala create mode 100644 common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterData.scala create mode 100644 common/src/main/scala/net/psforever/packet/game/objectcreate/ConstructorData.scala create mode 100644 common/src/main/scala/net/psforever/packet/game/objectcreate/InternalSlot.scala create mode 100644 common/src/main/scala/net/psforever/packet/game/objectcreate/InventoryData.scala create mode 100644 common/src/main/scala/net/psforever/packet/game/objectcreate/InventoryItem.scala create mode 100644 common/src/main/scala/net/psforever/packet/game/objectcreate/Mold.scala create mode 100644 common/src/main/scala/net/psforever/packet/game/objectcreate/REKData.scala create mode 100644 common/src/main/scala/net/psforever/packet/game/objectcreate/RibbonBars.scala create mode 100644 common/src/main/scala/net/psforever/packet/game/objectcreate/WeaponData.scala diff --git a/common/src/main/scala/net/psforever/packet/game/ObjectCreateMessage.scala b/common/src/main/scala/net/psforever/packet/game/ObjectCreateMessage.scala index 05e15926..32af6453 100644 --- a/common/src/main/scala/net/psforever/packet/game/ObjectCreateMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/ObjectCreateMessage.scala @@ -1,314 +1,12 @@ +// Copyright (c) 2016 PSForever.net to present package net.psforever.packet.game +import net.psforever.packet.game.objectcreate.Mold import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} -import scodec.DecodeResult -import net.psforever.types.Vector3 import scodec.bits._ import scodec.{Attempt, Codec, Err} import scodec.codecs._ -import shapeless._ - -import scala.annotation.switch - -abstract class ConstructorData - -case class AmmoBoxData(magazine : Int) extends ConstructorData - -object AmmoBoxData extends Marshallable[AmmoBoxData] { - implicit val codec : Codec[AmmoBoxData] = ( - uintL(8) :: - ignore(15) :: - ("magazine" | uint16L) - ).exmap[AmmoBoxData] ( - { - case 0xC8 :: _ :: mag :: HNil => - Attempt.successful(AmmoBoxData(mag)) - case x :: _ :: _ :: HNil => - Attempt.failure(Err("looking for 200, found "+x)) - }, - { - case AmmoBoxData(mag) => - Attempt.successful(0xC8 :: () :: mag :: HNil) - } - ).as[AmmoBoxData] -} - -case class WeaponData(unk : Int, - ammo : InternalSlot) extends ConstructorData - -object WeaponData extends Marshallable[WeaponData] { - def apply(unk : Int, cls : Int, guid : PlanetSideGUID, parentSlot : Int, ammo : AmmoBoxData) : WeaponData = - new WeaponData(unk, InternalSlot(cls, guid, parentSlot, Some(ammo))) - - implicit val codec : Codec[WeaponData] = ( - ("unk" | uint4L) :: - uint4L :: - ignore(20) :: - uint4L :: - ignore(16) :: - uintL(11) :: - ("ammo" | InternalSlot.codec) - ).exmap[WeaponData] ( - { - case code :: 8 :: _ :: 2 :: _ :: 0x2C0 :: ammo :: HNil => - Attempt.successful(WeaponData(code, ammo)) - case _ :: x :: _ :: y :: _ :: z :: _ :: HNil => - Attempt.failure(Err("looking for 8-2-704 pattern, found %d-%d-%d".format(x,y,z))) //TODO I actually don't know what of this is actually important - }, - { - case WeaponData(code, ammo) => - Attempt.successful(code :: 8 :: () :: 2 :: () :: 0x2C0 :: ammo :: HNil) - } - ).as[WeaponData] -} - -case class RibbonBars(upper : Int, //0xFFFF means no merit (for all ...) - middle : Int, - lower : Int, - tos : Int) - -object RibbonBars extends Marshallable[RibbonBars] { - implicit val codec : Codec[RibbonBars] = ( - ("upper" | uint16L) :: - ("middle" | uint16L) :: - ("lower" | uint16L) :: - ("tos" | uint16L) - ).as[RibbonBars] -} - -case class CharacterData(pos : Vector3, - objYaw : Int, - faction : Int, - bops : Boolean, - name : String, - exosuit : Int, - sex : Int, - face1 : Int, - face2 : Int, - voice : Int, - unk1 : Int, //0x8080 - unk2 : Int, //0xFFFF or 0x0 - unk3 : Int, //2 - viewPitch : Int, - viewYaw : Int, - upperMerit : Long, //0xFFFFFFFF means no merit (for all ...) - middleMerit : Long, - lowerMerit : Long, - termOfServiceMerit : Long, - healthMax : Int, - health : Int, - armor : Int, - unk4 : Int, //1 - unk5 : Int, //7 - unk6 : Int, //7 - staminaMax : Int, - stamina : Int, - unk7 : Int, // 28 - unk8 : Int, //4 - unk9 : Int, //44 - unk10 : Int, //84 - unk11 : Int, //104 - unk12 : Int, //1900 - firstTimeEvent_length : Long, - firstEntry : Option[String], - firstTimeEvent_list : List[String], - tutorial_list : List[String], - inventory : BitVector - ) extends ConstructorData - -object CharacterData extends Marshallable[CharacterData] { - val ribbonBars : Codec[RibbonBars] = ( - ("upper" | uint16L) :: - ("middle" | uint16L) :: - ("lower" | uint16L) :: - ("tos" | uint16L) - ).as[RibbonBars] - - implicit val codec : Codec[CharacterData] = ( - ("pos" | Vector3.codec_pos) :: - ignore(16) :: - ("objYaw" | uint8L) :: - ignore(1) :: - ("faction" | uintL(2)) :: - ("bops" | bool) :: - ignore(20) :: - ("name" | PacketHelpers.encodedWideStringAligned(4)) :: - ("exosuit" | uintL(3)) :: - ignore(2) :: - ("sex" | uintL(2)) :: - ("face1" | uint4L) :: - ("face2" | uint4L) :: - ("voice" | uintL(3)) :: - ignore(22) :: - ("unk1" | uint16L) :: - ignore(42) :: - ("unk2" | uint16L) :: - ignore(30) :: - ("unk3" | uintL(4)) :: - ignore(24) :: - ("viewPitch" | uint8L) :: - ("viewYaw" | uint8L) :: - ignore(10) :: - ("upperMerit" | uint32L) :: - ("middleMerit" | uint32L) :: - ("lowerMerit" | uint32L) :: - ("termOfServiceMerit" | uint32L) :: - ignore(160) :: - ("healthMax" | uint16L) :: - ("health" | uint16L) :: - ignore(1) :: - ("armor" | uint16L) :: - ignore(9) :: - ("unk4" | uint8L) :: - ignore(8) :: - ("unk5" | uint4L) :: - ("unk6" | uintL(3)) :: - ("staminaMax" | uint16L) :: - ("stamina" | uint16L) :: - ignore(149) :: - ("unk7" | uint16L) :: - ("unk8" | uint8L) :: - ("unk9" | uint8L) :: - ("unk10" | uint8L) :: - ("unk11" | uint8L) :: - ("unk12" | uintL(12)) :: - ignore(19) :: - (("firstTimeEvent_length" | uint32L) >>:~ { len => - conditional(len > 0, "firstEntry" | PacketHelpers.encodedStringAligned(5)) :: - ("firstTimeEvent_list" | PacketHelpers.listOfNSized(len - 1, PacketHelpers.encodedString)) :: - ("tutorial_list" | PacketHelpers.listOfNAligned(uint32L, 0, PacketHelpers.encodedString)) :: - ignore(207) :: - ("inventory" | bits) - }) - ).as[CharacterData] -} - -/** - * The same kind of data as required for a formal ObjectCreateMessage but with a required and implicit parent relationship. - * Data preceding this entry will define the existence of the parent. - * @param objectClass na - * @param guid na - * @param parentSlot na - * @param obj na - */ -case class InternalSlot(objectClass : Int, - guid : PlanetSideGUID, - parentSlot : Int, - obj : Option[ConstructorData]) - -object InternalSlot extends Marshallable[InternalSlot] { - type objPattern = Int :: PlanetSideGUID :: Int :: Option[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 constuctor 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] -} - -case class Mold(objectClass : Int, - data : BitVector) { - private var obj : Option[ConstructorData] = Mold.decode(objectClass, data) - - def isDefined : Boolean = this.obj.isDefined - - def get : ConstructorData = this.obj.get - - def set(data : ConstructorData) : Boolean = { - var ret = false - if(Some(data).isDefined) { - obj = Some(data) - ret = true - } - ret - } -} - -object Mold { - def apply(objectClass : Int, obj : ConstructorData) : Mold = - new Mold( objectClass, Mold.encode(objectClass, obj) ) - - def decode(objClass : Int, data : BitVector) : Option[ConstructorData] = { - var out : Option[ConstructorData] = None - if(!data.isEmpty) { - 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 0x46 => //beamer - outOpt = WeaponData.codec.decode(data).toOption - case 0x159 => //gauss - outOpt = WeaponData.codec.decode(data).toOption - case _ => - } - if(outOpt.isDefined) - out = Some(outOpt.get.value.asInstanceOf[ConstructorData]) - } - catch { - case ex : ClassCastException => - //TODO generate and log wrong class error message - case ex : Exception => - //TODO generic error - } - } - out - } - - def encode(objClass : Int, obj : ConstructorData) : BitVector = { - var out = BitVector.empty - try { - var outOpt : Option[BitVector] = None - (objClass : @switch) match { - case 0x1C => //9mm - outOpt = AmmoBoxData.codec.encode(obj.asInstanceOf[AmmoBoxData]).toOption - case 0x46 => //beamer - outOpt = WeaponData.codec.encode(obj.asInstanceOf[WeaponData]).toOption - case 0x159 => //gauss - outOpt = WeaponData.codec.encode(obj.asInstanceOf[WeaponData]).toOption - case _ => - throw new ClassCastException("cannot find object code - "+objClass) - } - if(outOpt.isDefined) - out = outOpt.get - } - catch { - case ex : ClassCastException => - //TODO generate and log wrong class error message - case ex : Exception => - //TODO generic error - } - out - } -} +import shapeless.{::, HNil} /** * The parent information of a created object.
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/AmmoBoxData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/AmmoBoxData.scala new file mode 100644 index 00000000..faa582b6 --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/AmmoBoxData.scala @@ -0,0 +1,28 @@ +// 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 shapeless.{::, HNil} + +case class AmmoBoxData(magazine : Int) extends ConstructorData + +object AmmoBoxData extends Marshallable[AmmoBoxData] { + implicit val codec : Codec[AmmoBoxData] = ( + uintL(8) :: + ignore(15) :: + ("magazine" | uint16L) + ).exmap[AmmoBoxData] ( + { + case 0xC8 :: _ :: mag :: HNil => + Attempt.successful(AmmoBoxData(mag)) + case x :: _ :: _ :: HNil => + Attempt.failure(Err("looking for 200, found "+x)) + }, + { + case AmmoBoxData(mag) => + Attempt.successful(0xC8 :: () :: mag :: HNil) + } + ).as[AmmoBoxData] +} diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterData.scala new file mode 100644 index 00000000..ddf8ca6c --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterData.scala @@ -0,0 +1,101 @@ +// Copyright (c) 2016 PSForever.net to present +package net.psforever.packet.game.objectcreate + +import net.psforever.packet.{Marshallable, PacketHelpers} +import net.psforever.types.Vector3 +import scodec.Codec +import scodec.codecs._ + +case class CharacterData(pos : Vector3, + objYaw : Int, + faction : Int, + bops : Boolean, + name : String, + exosuit : Int, + sex : Int, + face1 : Int, + face2 : Int, + voice : Int, + unk1 : Int, //0x8080 + unk2 : Int, //0xFFFF or 0x0 + unk3 : Int, //2 + viewPitch : Int, + viewYaw : Int, + ribbons : RibbonBars, + healthMax : Int, + health : Int, + armor : Int, + unk4 : Int, //1 + unk5 : Int, //7 + unk6 : Int, //7 + staminaMax : Int, + stamina : Int, + unk7 : Int, // 28 + unk8 : Int, //4 + unk9 : Int, //44 + unk10 : Int, //84 + unk11 : Int, //104 + unk12 : Int, //1900 + firstTimeEvent_length : Long, + firstEntry : Option[String], + firstTimeEvent_list : List[String], + tutorial_list : List[String], + inventory : InventoryData + ) extends ConstructorData + +object CharacterData extends Marshallable[CharacterData] { + implicit val codec : Codec[CharacterData] = ( + ("pos" | Vector3.codec_pos) :: + ignore(16) :: + ("objYaw" | uint8L) :: + ignore(1) :: + ("faction" | uintL(2)) :: + ("bops" | bool) :: + ignore(20) :: + ("name" | PacketHelpers.encodedWideStringAligned(4)) :: + ("exosuit" | uintL(3)) :: + ignore(2) :: + ("sex" | uintL(2)) :: + ("face1" | uint8L) :: + ("face2" | uint4L) :: + ("voice" | uintL(3)) :: + ignore(22) :: + ("unk1" | uint16L) :: + ignore(42) :: + ("unk2" | uint16L) :: + ignore(30) :: + ("unk3" | uintL(4)) :: + ignore(24) :: + ("viewPitch" | uint8L) :: + ("viewYaw" | uint8L) :: + ignore(10) :: + ("ribbons" | RibbonBars.codec) :: + ignore(160) :: + ("healthMax" | uint16L) :: + ("health" | uint16L) :: + ignore(1) :: + ("armor" | uint16L) :: + ignore(9) :: + ("unk4" | uint8L) :: + ignore(8) :: + ("unk5" | uint4L) :: + ("unk6" | uintL(3)) :: + ("staminaMax" | uint16L) :: + ("stamina" | uint16L) :: + ignore(149) :: + ("unk7" | uint16L) :: + ("unk8" | uint8L) :: + ("unk9" | uint8L) :: + ("unk10" | uint8L) :: + ("unk11" | uint8L) :: + ("unk12" | uintL(12)) :: + ignore(19) :: + (("firstTimeEvent_length" | uint32L) >>:~ { len => + conditional(len > 0, "firstEntry" | PacketHelpers.encodedStringAligned(5)) :: + ("firstTimeEvent_list" | PacketHelpers.listOfNSized(len - 1, PacketHelpers.encodedString)) :: + ("tutorial_list" | PacketHelpers.listOfNAligned(uint32L, 0, PacketHelpers.encodedString)) :: + ignore(207) :: + ("inventory" | InventoryData.codec) + }) + ).as[CharacterData] +} diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/ConstructorData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/ConstructorData.scala new file mode 100644 index 00000000..2b4d9cf6 --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/ConstructorData.scala @@ -0,0 +1,4 @@ +// Copyright (c) 2016 PSForever.net to present +package net.psforever.packet.game.objectcreate + +abstract class ConstructorData diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/InternalSlot.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/InternalSlot.scala new file mode 100644 index 00000000..704cdfe5 --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/InternalSlot.scala @@ -0,0 +1,58 @@ +// Copyright (c) 2016 PSForever.net to present +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.codecs._ +import shapeless.{::, HNil} + +/** + * The same kind of data as required for a formal ObjectCreateMessage but with a required and implicit parent relationship. + * Data preceding this entry will define the existence of the parent. + * @param objectClass na + * @param guid na + * @param parentSlot na + * @param obj na + */ +case class InternalSlot(objectClass : Int, + guid : PlanetSideGUID, + parentSlot : Int, + obj : Option[ConstructorData]) + +object InternalSlot extends Marshallable[InternalSlot] { + type objPattern = Int :: PlanetSideGUID :: Int :: Option[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] +} diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/InventoryData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/InventoryData.scala new file mode 100644 index 00000000..8e4f90e8 --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/InventoryData.scala @@ -0,0 +1,21 @@ +// Copyright (c) 2016 PSForever.net to present +package net.psforever.packet.game.objectcreate + +import net.psforever.packet.{Marshallable, PacketHelpers} +import scodec.Codec +import scodec.codecs._ + +case class InventoryData(unk1 : Boolean, + size : Int, + unk2 : Boolean, + inv : List[InventoryItem]) + +object InventoryData extends Marshallable[InventoryData] { + implicit val codec : Codec[InventoryData] = ( + ("unk1" | bool) :: + (("size" | uint8L) >>:~ { len => + ("unk2" | bool) :: + ("inv" | PacketHelpers.listOfNSized(len, InventoryItem.codec)) + }) + ).as[InventoryData] +} diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/InventoryItem.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/InventoryItem.scala new file mode 100644 index 00000000..5f1a2a90 --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/InventoryItem.scala @@ -0,0 +1,17 @@ +// Copyright (c) 2016 PSForever.net to present +package net.psforever.packet.game.objectcreate + +import net.psforever.packet.Marshallable +import scodec.Codec +import scodec.codecs._ + +case class InventoryItem(item : InternalSlot, + na : Option[Boolean] = None) + +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 + } + ).as[InventoryItem] +} diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/Mold.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/Mold.scala new file mode 100644 index 00000000..f5d3437e --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/Mold.scala @@ -0,0 +1,113 @@ +// Copyright (c) 2016 PSForever.net to present +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) + + def isDefined : Boolean = this.obj.isDefined + + def get : ConstructorData = this.obj.get + + def set(data : ConstructorData) : Boolean = { + var ret = false + if(Some(data).isDefined) { + obj = Some(data) + ret = true + } + ret + } +} + +object Mold { + def apply(objectClass : Int, obj : ConstructorData) : Mold = + new Mold( objectClass, Mold.encode(objectClass, obj) ) + + def decode(objClass : Int, data : BitVector) : Option[ConstructorData] = { + var out : Option[ConstructorData] = None + if(!data.isEmpty) { + 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 _ => + } + if(outOpt.isDefined) + out = Some(outOpt.get.value.asInstanceOf[ConstructorData]) + } + catch { + case ex : ClassCastException => + //TODO generate and log wrong class error message + case ex : Exception => + //TODO generic error + } + } + out + } + + def encode(objClass : Int, obj : ConstructorData) : BitVector = { + var out = BitVector.empty + try { + 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) + } + if(outOpt.isDefined) + out = outOpt.get + } + catch { + case ex : ClassCastException => + //TODO generate and log wrong class error message + case ex : Exception => + //TODO generic error + } + out + } +} diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/REKData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/REKData.scala new file mode 100644 index 00000000..b49786af --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/REKData.scala @@ -0,0 +1,32 @@ +// 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 shapeless.{::, HNil} + +case class REKData(unk : Int) extends ConstructorData + +object REKData extends Marshallable[REKData] { + implicit val codec : Codec[REKData] = ( + ("unk" | uint4L) :: + uint4L :: + ignore(20) :: + uint4L :: + ignore(16) :: + uint4L :: + ignore(20) + ).exmap[REKData] ( + { + case code :: 8 :: _ :: 2 :: _ :: 8 :: _ :: HNil => + Attempt.successful(REKData(code)) + case _ :: x :: _ :: y :: _ :: z :: _ :: HNil => + Attempt.failure(Err("looking for 8-2-8 pattern, found %d-%d-%d".format(x,y,z))) //TODO I actually don't know what of this is actually important + }, + { + case REKData(code) => + Attempt.successful(code :: 8 :: () :: 2 :: () :: 8 :: () :: HNil) + } + ).as[REKData] +} diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/RibbonBars.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/RibbonBars.scala new file mode 100644 index 00000000..7b9275a1 --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/RibbonBars.scala @@ -0,0 +1,20 @@ +// Copyright (c) 2016 PSForever.net to present +package net.psforever.packet.game.objectcreate + +import net.psforever.packet.Marshallable +import scodec.Codec +import scodec.codecs._ + +case class RibbonBars(upper : Long = 0xFFFFFFFFL, //0xFFFFFFFF means no merit (for all ...) + middle : Long = 0xFFFFFFFFL, + lower : Long = 0xFFFFFFFFL, + tos : Long = 0xFFFFFFFFL) + +object RibbonBars extends Marshallable[RibbonBars] { + implicit val codec : Codec[RibbonBars] = ( + ("upper" | uint32L) :: + ("middle" | uint32L) :: + ("lower" | uint32L) :: + ("tos" | uint32L) + ).as[RibbonBars] +} diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/WeaponData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/WeaponData.scala new file mode 100644 index 00000000..2248bf2a --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/WeaponData.scala @@ -0,0 +1,37 @@ +// Copyright (c) 2016 PSForever.net to present +package net.psforever.packet.game.objectcreate + +import net.psforever.packet.Marshallable +import net.psforever.packet.game.PlanetSideGUID +import scodec.{Attempt, Codec, Err} +import scodec.codecs._ +import shapeless.{::, HNil} + +case class WeaponData(unk : Int, + ammo : InternalSlot) extends ConstructorData + +object WeaponData extends Marshallable[WeaponData] { + def apply(unk : Int, cls : Int, guid : PlanetSideGUID, parentSlot : Int, ammo : AmmoBoxData) : WeaponData = + new WeaponData(unk, InternalSlot(cls, guid, parentSlot, Some(ammo))) + + implicit val codec : Codec[WeaponData] = ( + ("unk" | uint4L) :: + uint4L :: + ignore(20) :: + uint4L :: + ignore(16) :: + uintL(11) :: + ("ammo" | InternalSlot.codec) + ).exmap[WeaponData] ( + { + case code :: 8 :: _ :: 2 :: _ :: 0x2C0 :: ammo :: HNil => + Attempt.successful(WeaponData(code, ammo)) + case _ :: x :: _ :: y :: _ :: z :: _ :: HNil => + Attempt.failure(Err("looking for 8-2-704 pattern, found %d-%d-%d".format(x,y,z))) //TODO I actually don't know what of this is actually important + }, + { + case WeaponData(code, ammo) => + Attempt.successful(code :: 8 :: () :: 2 :: () :: 0x2C0 :: ammo :: HNil) + } + ).as[WeaponData] +} diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index 5355a495..f3ebd435 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -4,6 +4,7 @@ import java.net.{InetAddress, InetSocketAddress} import org.specs2.mutable._ import net.psforever.packet._ import net.psforever.packet.game._ +import net.psforever.packet.game.objectcreate._ import net.psforever.types._ import scodec.Attempt.Successful import scodec.bits._ @@ -150,7 +151,7 @@ class GamePacketTest extends Specification { val packet2Rest = packet2.bits.drop(8 + 32 + 1 + 11 + 16) 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 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 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" "decode (2)" in { PacketCoding.DecodePacket(packet2).require match { @@ -193,10 +194,10 @@ class GamePacketTest extends Specification { char.unk3 mustEqual 2 char.viewPitch mustEqual 0xFF char.viewYaw mustEqual 0x6A - char.upperMerit mustEqual 0xFFFFFFFFL //none - char.middleMerit mustEqual 0xFFFFFFFFL //none - char.lowerMerit mustEqual 0xFFFFFFFFL //none - char.termOfServiceMerit mustEqual 0xFFFFFFFFL //none + char.ribbons.upper mustEqual 0xFFFFFFFFL //none + char.ribbons.middle mustEqual 0xFFFFFFFFL //none + char.ribbons.lower mustEqual 0xFFFFFFFFL //none + char.ribbons.tos mustEqual 0xFFFFFFFFL //none char.healthMax mustEqual 100 char.health mustEqual 100 char.armor mustEqual 50 //standard exosuit value @@ -218,6 +219,9 @@ class GamePacketTest extends Specification { char.firstTimeEvent_list(1) mustEqual "used_beamer" char.firstTimeEvent_list(2) mustEqual "map13" char.tutorial_list.size mustEqual 0 + char.inventory.unk1 mustEqual true + char.inventory.size mustEqual 10 + char.inventory.unk2 mustEqual false case default => ko }