From 110dcdf6750d1992978f67127dc2ca6c1767c366 Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 30 Aug 2017 09:35:18 -0400 Subject: [PATCH] added DetailedLockerContainerData Codec; modified PacketConverter for LockerContainer; modified tests --- .../converter/LockerContainerConverter.scala | 6 +- .../DetailedLockerContainerData.scala | 89 +++++++++++++++++++ .../game/objectcreate/InventoryData.scala | 4 +- .../game/objectcreate/ObjectClass.scala | 2 +- .../ObjectCreateDetailedMessageTest.scala | 7 +- 5 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedLockerContainerData.scala diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/LockerContainerConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/LockerContainerConverter.scala index 0ef5205b..8c578331 100644 --- a/common/src/main/scala/net/psforever/objects/definition/converter/LockerContainerConverter.scala +++ b/common/src/main/scala/net/psforever/objects/definition/converter/LockerContainerConverter.scala @@ -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)) } /** diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedLockerContainerData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedLockerContainerData.scala new file mode 100644 index 00000000..ef60dcb6 --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedLockerContainerData.scala @@ -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.
+ *
+ * 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) + } + ) +} 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 index f21590b4..c3c06c0f 100644 --- a/common/src/main/scala/net/psforever/packet/game/objectcreate/InventoryData.scala +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/InventoryData.scala @@ -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 diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala index 642a9922..f88a38e7 100644 --- a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala +++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala @@ -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) diff --git a/common/src/test/scala/game/ObjectCreateDetailedMessageTest.scala b/common/src/test/scala/game/ObjectCreateDetailedMessageTest.scala index ee9e38d1..81314a8c 100644 --- a/common/src/test/scala/game/ObjectCreateDetailedMessageTest.scala +++ b/common/src/test/scala/game/ObjectCreateDetailedMessageTest.scala @@ -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)) ::