diff --git a/common/src/main/scala/net/psforever/packet/PSPacket.scala b/common/src/main/scala/net/psforever/packet/PSPacket.scala index 597013ff..95663dff 100644 --- a/common/src/main/scala/net/psforever/packet/PSPacket.scala +++ b/common/src/main/scala/net/psforever/packet/PSPacket.scala @@ -113,7 +113,7 @@ object PacketHelpers { // when the first bit of the byte is set, the size can be between [0, 127]. // otherwise, it is between [128, 32767] and two bytes are used for encoding // The magic in this is next level - private def encodedStringSize : Codec[Int] = either(bool, uint(15), uint(7)). + def encodedStringSize : Codec[Int] = either(bool, uint(15), uint(7)). xmap[Int]( (a : Either[Int, Int]) => a.fold[Int](a => a, a => a), (a : Int) => diff --git a/common/src/main/scala/net/psforever/packet/game/ObjectCreateMessage.scala b/common/src/main/scala/net/psforever/packet/game/ObjectCreateMessage.scala new file mode 100644 index 00000000..46896f29 --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/ObjectCreateMessage.scala @@ -0,0 +1,55 @@ +package net.psforever.packet.game + +import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} +import scodec.bits._ +import scodec.{Attempt, Codec, Err} +import scodec.codecs._ +import shapeless._ + +case class ObjectCreateMessageParent(guid : Int, slot : Int) + +case class ObjectCreateMessage(streamLength : Long, + objectClass : Int, + guid : Int, + parentInfo : Option[ObjectCreateMessageParent]) + extends PlanetSideGamePacket { + + def opcode = GamePacketOpcode.ObjectCreateMessage + def encode = ObjectCreateMessage.encode(this) +} + +object ObjectCreateMessage extends Marshallable[ObjectCreateMessage] { + + type Pattern = Int :: Int :: Option[ObjectCreateMessageParent] :: HNil + type ChoicePattern = Either[Pattern, Pattern] + + val noParent : Codec[Pattern] = (("object_class" | uintL(0xb)) :: + ("guid" | uint16L)).xmap[Pattern]( { + case cls :: guid :: HNil => cls :: guid :: None :: HNil + }, { + case cls :: guid :: None :: HNil => cls :: guid :: HNil + }) + val parent : Codec[Pattern] = (("parent_guid" | uint16L) :: + ("object_class" | uintL(0xb)) :: + ("guid" | uint16L) :: + ("parent_slot_index" | PacketHelpers.encodedStringSize)).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 + }) + + implicit val codec : Codec[ObjectCreateMessage] = ( + ("stream_length" | uint32L) :: either(bool, parent, noParent).exmap[Pattern]( { + case Left(a :: b :: Some(c) :: HNil) => Attempt.successful(a :: b :: Some(c) :: HNil) + case Right(a :: b :: None :: HNil) => Attempt.successful(a :: b :: None :: HNil) + // failure cases + case Left(a :: b :: None :: HNil) => Attempt.failure(Err("expected parent structure")) + case Right(a :: b :: Some(c) :: HNil) => Attempt.failure(Err("got unexpected parent structure")) + }, { + 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)) + }) + ).as[ObjectCreateMessage] +}