mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-04-28 23:35:23 +00:00
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
This commit is contained in:
parent
7cd9b8f5f1
commit
b6eed0dbbc
12 changed files with 443 additions and 310 deletions
|
|
@ -1,314 +1,12 @@
|
||||||
|
// Copyright (c) 2016 PSForever.net to present
|
||||||
package net.psforever.packet.game
|
package net.psforever.packet.game
|
||||||
|
|
||||||
|
import net.psforever.packet.game.objectcreate.Mold
|
||||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||||
import scodec.DecodeResult
|
|
||||||
import net.psforever.types.Vector3
|
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
import scodec.{Attempt, Codec, Err}
|
import scodec.{Attempt, Codec, Err}
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
import shapeless._
|
import shapeless.{::, HNil}
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The parent information of a created object.<br>
|
* The parent information of a created object.<br>
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
}
|
||||||
|
|
@ -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]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
// Copyright (c) 2016 PSForever.net to present
|
||||||
|
package net.psforever.packet.game.objectcreate
|
||||||
|
|
||||||
|
abstract class ConstructorData
|
||||||
|
|
@ -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]
|
||||||
|
}
|
||||||
|
|
@ -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]
|
||||||
|
}
|
||||||
|
|
@ -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]
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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]
|
||||||
|
}
|
||||||
|
|
@ -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]
|
||||||
|
}
|
||||||
|
|
@ -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]
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import java.net.{InetAddress, InetSocketAddress}
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
import net.psforever.packet._
|
import net.psforever.packet._
|
||||||
import net.psforever.packet.game._
|
import net.psforever.packet.game._
|
||||||
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.types._
|
import net.psforever.types._
|
||||||
import scodec.Attempt.Successful
|
import scodec.Attempt.Successful
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
|
@ -150,7 +151,7 @@ class GamePacketTest extends Specification {
|
||||||
val packet2Rest = packet2.bits.drop(8 + 32 + 1 + 11 + 16)
|
val packet2Rest = packet2.bits.drop(8 + 32 + 1 + 11 + 16)
|
||||||
val string_9mm = hex"18 7C000000 2580 0E0 0005 A1 C8000064000"
|
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_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 {
|
"decode (2)" in {
|
||||||
PacketCoding.DecodePacket(packet2).require match {
|
PacketCoding.DecodePacket(packet2).require match {
|
||||||
|
|
@ -193,10 +194,10 @@ class GamePacketTest extends Specification {
|
||||||
char.unk3 mustEqual 2
|
char.unk3 mustEqual 2
|
||||||
char.viewPitch mustEqual 0xFF
|
char.viewPitch mustEqual 0xFF
|
||||||
char.viewYaw mustEqual 0x6A
|
char.viewYaw mustEqual 0x6A
|
||||||
char.upperMerit mustEqual 0xFFFFFFFFL //none
|
char.ribbons.upper mustEqual 0xFFFFFFFFL //none
|
||||||
char.middleMerit mustEqual 0xFFFFFFFFL //none
|
char.ribbons.middle mustEqual 0xFFFFFFFFL //none
|
||||||
char.lowerMerit mustEqual 0xFFFFFFFFL //none
|
char.ribbons.lower mustEqual 0xFFFFFFFFL //none
|
||||||
char.termOfServiceMerit mustEqual 0xFFFFFFFFL //none
|
char.ribbons.tos mustEqual 0xFFFFFFFFL //none
|
||||||
char.healthMax mustEqual 100
|
char.healthMax mustEqual 100
|
||||||
char.health mustEqual 100
|
char.health mustEqual 100
|
||||||
char.armor mustEqual 50 //standard exosuit value
|
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(1) mustEqual "used_beamer"
|
||||||
char.firstTimeEvent_list(2) mustEqual "map13"
|
char.firstTimeEvent_list(2) mustEqual "map13"
|
||||||
char.tutorial_list.size mustEqual 0
|
char.tutorial_list.size mustEqual 0
|
||||||
|
char.inventory.unk1 mustEqual true
|
||||||
|
char.inventory.size mustEqual 10
|
||||||
|
char.inventory.unk2 mustEqual false
|
||||||
case default =>
|
case default =>
|
||||||
ko
|
ko
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue