added DetailedLockerContainerData Codec; modified PacketConverter for LockerContainer; modified tests

This commit is contained in:
FateJH 2017-08-30 09:35:18 -04:00
parent 3e5e8a2573
commit 110dcdf675
5 changed files with 100 additions and 8 deletions

View file

@ -5,7 +5,7 @@ import net.psforever.objects.LockerContainer
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.inventory.GridInventory
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.packet.game.objectcreate.{DetailedAmmoBoxData, InternalSlot, InventoryData, LockerContainerData}
import net.psforever.packet.game.objectcreate._
import scala.util.{Success, Try}
@ -14,8 +14,8 @@ class LockerContainerConverter extends ObjectCreateConverter[LockerContainer]()
Success(LockerContainerData(InventoryData(MakeInventory(obj.Inventory))))
}
override def DetailedConstructorData(obj : LockerContainer) : Try[DetailedAmmoBoxData] = {
Success(DetailedAmmoBoxData(8, 1)) //same format as AmmoBox data
override def DetailedConstructorData(obj : LockerContainer) : Try[DetailedLockerContainerData] = {
Success(DetailedLockerContainerData(8))
}
/**

View file

@ -0,0 +1,89 @@
// Copyright (c) 2017 PSForever
package net.psforever.packet.game.objectcreate
import net.psforever.packet.Marshallable
import net.psforever.packet.game.PlanetSideGUID
import scodec.codecs._
import scodec.{Attempt, Codec, Err}
import shapeless.{::, HNil}
/**
* A representation of the inventory portion of `ObjectCreateDetailedMessage` packet data that contains the items in the avatar's locker space.<br>
* <br>
* Although these items are technically always loaded and registered with globally unique identifiers for the current zone,
* the actual container for them, in grid format, can only be accessed by interacting with locker objects in the game world.
* Items are generally added and removed in the same way as with any other opened inventory.
* Unlike other inventories, however, locker space is personal to an avatar and can not be accessed by other players.
* @param unk na
* @param contents the items in the inventory
*/
final case class DetailedLockerContainerData(unk : Int,
contents : Option[List[InternalSlot]]
) extends ConstructorData {
override def bitsize : Long = {
val base : Long = 40L
var invSize : Long = 0L //length of all items in inventory
if(contents.isDefined) {
invSize = InventoryData.BaseSize
for(item <- contents.get) {
invSize += item.bitsize
}
}
base + invSize
}
}
object DetailedLockerContainerData extends Marshallable[DetailedLockerContainerData] {
/**
* Overloaded constructor for creating `DetailedLockerContainerData` without a list of contents.
* @param unk na
* @return a `DetailedLockerContainerData` object
*/
def apply(unk : Int) : DetailedLockerContainerData =
new DetailedLockerContainerData(unk, None)
/**
* Overloaded constructor for creating `DetailedLockerContainerData` containing known items.
* @param unk na
* @param contents the items in the inventory
* @return a `DetailedLockerContainerData` object
*/
def apply(unk : Int, contents : List[InternalSlot]) : DetailedLockerContainerData =
new DetailedLockerContainerData(unk, Some(contents))
/**
* Overloaded constructor for creating `DetailedLockerContainerData` while masking use of `InternalSlot`.
* @param cls the code for the type of object being constructed
* @param guid the GUID this object will be assigned
* @param parentSlot a parent-defined slot identifier that explains where the child is to be attached to the parent
* @param locker the `DetailedLockerContainerData`
* @return an `InternalSlot` object that encapsulates `DetailedLockerContainerData`
*/
def apply(cls : Int, guid : PlanetSideGUID, parentSlot : Int, locker : DetailedLockerContainerData) : InternalSlot =
new InternalSlot(cls, guid, parentSlot, locker)
implicit val codec : Codec[DetailedLockerContainerData] = (
uint4L ::
("unk" | uint4L) :: // 8 - common - 4 - safe, 2 - stream misalignment, 1 - safe, 0 - common
uint(15) ::
uint16L :: //always 1
optional(bool, InventoryData.codec_detailed)
).exmap[DetailedLockerContainerData] (
{
case 0xC :: unk :: 0 :: 1 :: None :: HNil =>
Attempt.successful(DetailedLockerContainerData(unk, None))
case 0xC :: unk :: 0 :: 1 :: Some(InventoryData(list)) :: HNil =>
Attempt.successful(DetailedLockerContainerData(unk, Some(list)))
case _ =>
Attempt.failure(Err(s"invalid locker container data format"))
},
{
case DetailedLockerContainerData(unk, None) =>
Attempt.successful(0xC :: unk :: 0 :: 1 :: None :: HNil)
case DetailedLockerContainerData(unk, Some(list)) =>
Attempt.successful(0xC :: unk :: 0 :: 1 :: Some(InventoryData(list)) :: HNil)
}
)
}

View file

@ -23,7 +23,7 @@ import shapeless.{::, HNil}
*/
final case class InventoryData(contents : List[InventoryItem] = List.empty) extends StreamBitSize {
override def bitsize : Long = {
val base : Long = 10L //8u + 1u + 1u
val base : Long = InventoryData.BaseSize
var invSize : Long = 0L //length of all items in inventory
for(item <- contents) {
invSize += item.bitsize
@ -33,6 +33,8 @@ final case class InventoryData(contents : List[InventoryItem] = List.empty) exte
}
object InventoryData {
final val BaseSize : Long = 10L //8u + 1u + 1u
/**
* The primary `Codec` that parses the common format for an inventory `List`.
* @param itemCodec a `Codec` that describes each of the contents of the list

View file

@ -638,7 +638,7 @@ object ObjectClass {
case ObjectClass.boomer_trigger => ConstructorData.genericCodec(DetailedBoomerTriggerData.codec, "boomer trigger")
//other
case ObjectClass.avatar => ConstructorData.genericCodec(DetailedCharacterData.codec, "avatar")
case ObjectClass.locker_container => ConstructorData.genericCodec(DetailedAmmoBoxData.codec, "locker container")
case ObjectClass.locker_container => ConstructorData.genericCodec(DetailedLockerContainerData.codec, "locker container")
//failure case
case _ => defaultFailureCodec(objClass)

View file

@ -260,7 +260,8 @@ class ObjectCreateDetailedMessageTest extends Specification {
inventory(3).objectClass mustEqual ObjectClass.locker_container
inventory(3).guid mustEqual PlanetSideGUID(82)
inventory(3).parentSlot mustEqual 5
inventory(3).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 1
inventory(3).obj.isInstanceOf[DetailedLockerContainerData] mustEqual true
inventory(3).obj.asInstanceOf[DetailedLockerContainerData].contents.isDefined mustEqual false
//4
inventory(4).objectClass mustEqual ObjectClass.bullet_9mm
inventory(4).guid mustEqual PlanetSideGUID(83)
@ -342,7 +343,7 @@ class ObjectCreateDetailedMessageTest extends Specification {
Nil
)(2)
val msg = ObjectCreateDetailedMessage(ObjectClass.punisher, PlanetSideGUID(1703), ObjectCreateMessageParent(PlanetSideGUID(75), 2), obj)
var pkt = PacketCoding.EncodePacket(msg).require.toByteVector
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_punisher
}
@ -394,7 +395,7 @@ class ObjectCreateDetailedMessageTest extends Specification {
val inv = InventoryItemData(ObjectClass.beamer, PlanetSideGUID(76), 0, DetailedWeaponData(4, 8, ObjectClass.energy_cell, PlanetSideGUID(77), 0, DetailedAmmoBoxData(8, 16))) ::
InventoryItemData(ObjectClass.suppressor, PlanetSideGUID(78), 2, DetailedWeaponData(4, 8, ObjectClass.bullet_9mm, PlanetSideGUID(79), 0, DetailedAmmoBoxData(8, 25))) ::
InventoryItemData(ObjectClass.forceblade, PlanetSideGUID(80), 4, DetailedWeaponData(4, 8, ObjectClass.melee_ammo, PlanetSideGUID(81), 0, DetailedAmmoBoxData(8, 1))) ::
InventoryItemData(ObjectClass.locker_container, PlanetSideGUID(82), 5, DetailedAmmoBoxData(8, 1)) ::
InventoryItemData(ObjectClass.locker_container, PlanetSideGUID(82), 5, DetailedLockerContainerData(8)) ::
InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(83), 6, DetailedAmmoBoxData(8, 50)) ::
InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(84), 9, DetailedAmmoBoxData(8, 50)) ::
InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(85), 12, DetailedAmmoBoxData(8, 50)) ::