From 958427dc8ff2c0f3962ec0b4eef56951c8b24aad Mon Sep 17 00:00:00 2001 From: Resaec Date: Tue, 16 Dec 2025 01:03:32 +0100 Subject: [PATCH] EmpireBenefitsMessage packet --- .../psforever/packet/GamePacketOpcode.scala | 2 +- .../packet/game/EmpireBenefitsMessage.scala | 44 +++++++++++++++ .../game/EmpireBenefitsMessageTest.scala | 53 +++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/main/scala/net/psforever/packet/game/EmpireBenefitsMessage.scala create mode 100644 src/test/scala/game/EmpireBenefitsMessageTest.scala diff --git a/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index 0253d2b80..54c4e3352 100644 --- a/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -557,7 +557,7 @@ object GamePacketOpcode extends Enumeration { case 0xd4 => game.GenericObjectActionAtPositionMessage.decode case 0xd5 => game.PropertyOverrideMessage.decode case 0xd6 => game.WarpgateLinkOverrideMessage.decode - case 0xd7 => noDecoder(EmpireBenefitsMessage) + case 0xd7 => game.EmpireBenefitsMessage.decode // 0xd8 case 0xd8 => noDecoder(ForceEmpireMessage) case 0xd9 => game.BroadcastWarpgateUpdateMessage.decode diff --git a/src/main/scala/net/psforever/packet/game/EmpireBenefitsMessage.scala b/src/main/scala/net/psforever/packet/game/EmpireBenefitsMessage.scala new file mode 100644 index 000000000..034c79454 --- /dev/null +++ b/src/main/scala/net/psforever/packet/game/EmpireBenefitsMessage.scala @@ -0,0 +1,44 @@ +// Copyright (c) 2025 PSForever +package net.psforever.packet.game + +import net.psforever.packet.game.EmpireBenefitsMessage.{ZoneLocks, ZoneBenefits} +import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} +import scodec.Codec +import scodec.codecs._ + +final case class EmpireBenefitsMessage( + entriesA: Vector[ZoneLocks], + entriesB: Vector[ZoneBenefits] + ) extends PlanetSideGamePacket { + type Packet = EmpireBenefitsMessage + def opcode = GamePacketOpcode.EmpireBenefitsMessage + def encode = EmpireBenefitsMessage.encode(this) +} + +object EmpireBenefitsMessage extends Marshallable[EmpireBenefitsMessage] { + + final case class ZoneLocks( + empire: Int, + zone: String + ) + + final case class ZoneBenefits( + empire: Int, + value: Int + ) + + private implicit val entryACodec: Codec[ZoneLocks] = ( + ("empire" | uint(2)) :: + ("zone" | PacketHelpers.encodedStringAligned(6)) + ).as[ZoneLocks] + + private implicit val entryBCodec: Codec[ZoneBenefits] = ( + ("empire" | uint(2)) :: + ("benefit" | uint16L) + ).as[ZoneBenefits] + + implicit val codec: Codec[EmpireBenefitsMessage] = ( + ("entriesA" | vectorOfN(uint32L.xmap(_.toInt, _.toLong), entryACodec)) :: + ("entriesB" | vectorOfN(uint32L.xmap(_.toInt, _.toLong), entryBCodec)) + ).as[EmpireBenefitsMessage] +} diff --git a/src/test/scala/game/EmpireBenefitsMessageTest.scala b/src/test/scala/game/EmpireBenefitsMessageTest.scala new file mode 100644 index 000000000..3ea0431df --- /dev/null +++ b/src/test/scala/game/EmpireBenefitsMessageTest.scala @@ -0,0 +1,53 @@ +// Copyright (c) 2025 PSForever +package game + +import net.psforever.packet._ +import net.psforever.packet.game.EmpireBenefitsMessage.{ZoneLocks, ZoneBenefits} +import net.psforever.packet.game.{EmpireBenefitsMessage, OutfitEvent} +import org.specs2.mutable._ +import scodec.bits._ + +class EmpireBenefitsMessageTest extends Specification { + + val sample1: ByteVector = ByteVector.fromValidHex( + "d7" + // header + "04000000" + // count uint32L + "21c06c6f636b2d7a3321c06c6f636b2d7a3464006c6f636b2d69312d69322d69332d6934a1c06c6f636b2d7a39" + + "04000000" + // count uint32L + "004000600410020300" + ) + + val sample1_expectedLocks = Vector( + ZoneLocks(0, "lock-z3"), + ZoneLocks(0, "lock-z4"), + ZoneLocks(1, "lock-i1-i2-i3-i4"), + ZoneLocks(2, "lock-z9") + ) + + val sample1_expectedBenefits = Vector( + ZoneBenefits(0, 1), + ZoneBenefits(0, 6), + ZoneBenefits(1, 4), + ZoneBenefits(2, 3) + ) + + "decode sample1" in { + PacketCoding.decodePacket(sample1).require match { + case EmpireBenefitsMessage(a, b) => + a mustEqual sample1_expectedLocks + b mustEqual sample1_expectedBenefits + case _ => + ko + } + } + + "encode sample1" in { + val msg = EmpireBenefitsMessage( + entriesA = sample1_expectedLocks, + entriesB = sample1_expectedBenefits + ) + val pkt = PacketCoding.encodePacket(msg).require.toByteVector + + pkt mustEqual sample1 + } +}