From 6551de6f53b1aa72e5df46b96121937b9d95b82c Mon Sep 17 00:00:00 2001 From: Resaec Date: Wed, 17 Dec 2025 03:53:50 +0100 Subject: [PATCH] Fix variable naming Added comments Added enums for ease of use Typed packet variables Added 3 more samples to test cases --- .../packet/game/EmpireBenefitsMessage.scala | 104 +++++++++--- .../game/EmpireBenefitsMessageTest.scala | 157 ++++++++++++++++-- 2 files changed, 228 insertions(+), 33 deletions(-) diff --git a/src/main/scala/net/psforever/packet/game/EmpireBenefitsMessage.scala b/src/main/scala/net/psforever/packet/game/EmpireBenefitsMessage.scala index 034c79454..24a62a6d5 100644 --- a/src/main/scala/net/psforever/packet/game/EmpireBenefitsMessage.scala +++ b/src/main/scala/net/psforever/packet/game/EmpireBenefitsMessage.scala @@ -1,14 +1,25 @@ // Copyright (c) 2025 PSForever package net.psforever.packet.game -import net.psforever.packet.game.EmpireBenefitsMessage.{ZoneLocks, ZoneBenefits} +import net.psforever.packet.game.EmpireBenefitsMessage.{ZoneBenefit, ZoneLock} import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} +import net.psforever.types.PlanetSideEmpire import scodec.Codec import scodec.codecs._ +import scala.language.implicitConversions + +/** + * EmpireBenefitsMessage + * + * zoneLocks gives the client information about which empire locks what continent. + * This produces a chat message. + * zoneBenefits tells the client what empire has which benefits enabled. + * This has to match zoneLocks to work properly. + */ final case class EmpireBenefitsMessage( - entriesA: Vector[ZoneLocks], - entriesB: Vector[ZoneBenefits] + zoneLocks: Vector[ZoneLock], + zoneBenefits: Vector[ZoneBenefit] ) extends PlanetSideGamePacket { type Packet = EmpireBenefitsMessage def opcode = GamePacketOpcode.EmpireBenefitsMessage @@ -17,28 +28,83 @@ final case class EmpireBenefitsMessage( object EmpireBenefitsMessage extends Marshallable[EmpireBenefitsMessage] { - final case class ZoneLocks( - empire: Int, - zone: String + /** + * ZoneLockZone + * + * Available Types of Zones + * + * These zones can be used to notify the client of a lock. + */ + object ZoneLockZone extends Enumeration { + type Type = String + + val i1: ZoneLockZone.Value = Value("lock-i1") // Extinction Continental Lock + val i2: ZoneLockZone.Value = Value("lock-i2") // Ascension Continental Lock + val i3: ZoneLockZone.Value = Value("lock-i3") // Desolation Continental Lock + val i4: ZoneLockZone.Value = Value("lock-i4") // Nexus Continental Lock + val i1_i2_i3_i4: ZoneLockZone.Value = Value("lock-i1-i2-i3-i4") // Oshur Cluster Lock + val z3: ZoneLockZone.Value = Value("lock-z3") // Cyssor Continental Lock + val z4: ZoneLockZone.Value = Value("lock-z4") // Ishundar Continental Lock + val z9: ZoneLockZone.Value = Value("lock-z9") // Searhus Continental Lock + val tr_homes: ZoneLockZone.Value = Value("lock-tr-homes") // TR Home Continent Lock + val nc_homes: ZoneLockZone.Value = Value("lock-nc-homes") // NC Home Continent Lock + val vs_homes: ZoneLockZone.Value = Value("lock-vs-homes") // VS Home Continent Lock + + implicit def valueToType(v: ZoneLockZone.Value): Type = v.toString + implicit val codec: Codec[Type] = PacketHelpers.encodedStringAligned(6) + } + + /** + * ZoneLockBenefit + * + * Available Types of Benefits + * + * Benefits 0, 2 and 5 are unknown. Benefits for i1 to i4 are unknown and mapped incorrectly here. + */ + object ZoneLockBenefit extends Enumeration { + type Type = Value + + val i1: ZoneLockBenefit.Value = Value(-1) // Extinction Continental Lock + val i2: ZoneLockBenefit.Value = Value(-2) // Ascension Continental Lock + val i3: ZoneLockBenefit.Value = Value(-3) // Desolation Continental Lock + val i4: ZoneLockBenefit.Value = Value(-4) // Nexus Continental Lock + + // val unk0: ZoneLockBenefit.Value = Value(0) + val z4: ZoneLockBenefit.Value = Value(1) // Ishundar Continental Lock + // val unk2: ZoneLockBenefit.Value = Value(2) + val z9: ZoneLockBenefit.Value = Value(3) // Searhus Continental Lock + val i1_i2_i3_i4: ZoneLockBenefit.Value = Value(4) // Oshur Cluster Lock + // val unk5: ZoneLockBenefit.Value = Value(5) + val z3: ZoneLockBenefit.Value = Value(6) // Cyssor Continental Lock + val tr_homes: ZoneLockBenefit.Value = Value(7) // TR Home Continent Lock + val nc_homes: ZoneLockBenefit.Value = Value(8) // NC Home Continent Lock + val vs_homes: ZoneLockBenefit.Value = Value(9) // VS Home Continent Lock + + implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uint16L) + } + + final case class ZoneLock( + empire: PlanetSideEmpire.Type, + zone: ZoneLockZone.Type, ) - final case class ZoneBenefits( - empire: Int, - value: Int + final case class ZoneBenefit( + empire: PlanetSideEmpire.Type, + value: ZoneLockBenefit.Type ) - private implicit val entryACodec: Codec[ZoneLocks] = ( - ("empire" | uint(2)) :: - ("zone" | PacketHelpers.encodedStringAligned(6)) - ).as[ZoneLocks] + private implicit val zoneLockCodec: Codec[ZoneLock] = ( + ("empire" | PlanetSideEmpire.codec) :: + ("zone" | ZoneLockZone.codec) + ).as[ZoneLock] - private implicit val entryBCodec: Codec[ZoneBenefits] = ( - ("empire" | uint(2)) :: - ("benefit" | uint16L) - ).as[ZoneBenefits] + private implicit val zoneBenefitCodec: Codec[ZoneBenefit] = ( + ("empire" | PlanetSideEmpire.codec) :: + ("benefit" | ZoneLockBenefit.codec) + ).as[ZoneBenefit] implicit val codec: Codec[EmpireBenefitsMessage] = ( - ("entriesA" | vectorOfN(uint32L.xmap(_.toInt, _.toLong), entryACodec)) :: - ("entriesB" | vectorOfN(uint32L.xmap(_.toInt, _.toLong), entryBCodec)) + ("zoneLocks" | vectorOfN(uint32L.xmap(_.toInt, _.toLong), zoneLockCodec)) :: + ("zoneBenefits" | vectorOfN(uint32L.xmap(_.toInt, _.toLong), zoneBenefitCodec)) ).as[EmpireBenefitsMessage] } diff --git a/src/test/scala/game/EmpireBenefitsMessageTest.scala b/src/test/scala/game/EmpireBenefitsMessageTest.scala index 3ea0431df..9f86281d6 100644 --- a/src/test/scala/game/EmpireBenefitsMessageTest.scala +++ b/src/test/scala/game/EmpireBenefitsMessageTest.scala @@ -2,8 +2,9 @@ package game import net.psforever.packet._ -import net.psforever.packet.game.EmpireBenefitsMessage.{ZoneLocks, ZoneBenefits} -import net.psforever.packet.game.{EmpireBenefitsMessage, OutfitEvent} +import net.psforever.packet.game.EmpireBenefitsMessage +import net.psforever.packet.game.EmpireBenefitsMessage.{ZoneBenefit, ZoneLock, ZoneLockBenefit, ZoneLockZone} +import net.psforever.types.PlanetSideEmpire import org.specs2.mutable._ import scodec.bits._ @@ -17,18 +18,86 @@ class EmpireBenefitsMessageTest extends Specification { "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 sample2: ByteVector = ByteVector.fromValidHex( + "d7" + + "05000000 23406c6f636b2d76732d686f6d657321c06c6f636b2d7a3321c06c6f636b2d7a3461c06c6f636b2d7a3964006c6f636b2d69312d69322d69332d6934" + + "05000000 004000600024010300410000" ) - val sample1_expectedBenefits = Vector( - ZoneBenefits(0, 1), - ZoneBenefits(0, 6), - ZoneBenefits(1, 4), - ZoneBenefits(2, 3) + val sample3: ByteVector = ByteVector.fromValidHex( + "d7" + + "05000000 21c06c6f636b2d7a3321c06c6f636b2d7a3423406c6f636b2d6e632d686f6d657361c06c6f636b2d7a39a4006c6f636b2d69312d69322d69332d6934" + + "05000000 004000600020010300810000" + ) + + val sample4: ByteVector = ByteVector.fromValidHex( + "d7" + + "06000000 a3406c6f636b2d6e632d686f6d6573a3406c6f636b2d74722d686f6d6573a1c06c6f636b2d7a33a1c06c6f636b2d7a34a1c06c6f636b2d7a39a4006c6f636b2d69312d69322d69332d6934" + + "06000000 80402030081002060081c0208000" + ) + + private val sample1_expectedLocks = Vector( + ZoneLock(PlanetSideEmpire.TR, ZoneLockZone.z3), + ZoneLock(PlanetSideEmpire.TR, ZoneLockZone.z4), + ZoneLock(PlanetSideEmpire.NC, ZoneLockZone.i1_i2_i3_i4), + ZoneLock(PlanetSideEmpire.VS, ZoneLockZone.z9) + ) + + private val sample1_expectedBenefits = Vector( + ZoneBenefit(PlanetSideEmpire.TR, ZoneLockBenefit.z4), + ZoneBenefit(PlanetSideEmpire.TR, ZoneLockBenefit.z3), + ZoneBenefit(PlanetSideEmpire.NC, ZoneLockBenefit.i1_i2_i3_i4), + ZoneBenefit(PlanetSideEmpire.VS, ZoneLockBenefit.z9) + ) + + private val sample2_expectedLocks = Vector( + ZoneLock(PlanetSideEmpire.TR, ZoneLockZone.vs_homes), + ZoneLock(PlanetSideEmpire.TR, ZoneLockZone.z3), + ZoneLock(PlanetSideEmpire.TR, ZoneLockZone.z4), + ZoneLock(PlanetSideEmpire.NC, ZoneLockZone.z9), + ZoneLock(PlanetSideEmpire.NC, ZoneLockZone.i1_i2_i3_i4) + ) + + private val sample2_expectedBenefits = Vector( + ZoneBenefit(PlanetSideEmpire.TR, ZoneLockBenefit.z4), + ZoneBenefit(PlanetSideEmpire.TR, ZoneLockBenefit.z3), + ZoneBenefit(PlanetSideEmpire.TR, ZoneLockBenefit.vs_homes), + ZoneBenefit(PlanetSideEmpire.NC, ZoneLockBenefit.z9), + ZoneBenefit(PlanetSideEmpire.NC, ZoneLockBenefit.i1_i2_i3_i4) + ) + + private val sample3_expectedLocks = Vector( + ZoneLock(PlanetSideEmpire.TR, ZoneLockZone.z3), + ZoneLock(PlanetSideEmpire.TR, ZoneLockZone.z4), + ZoneLock(PlanetSideEmpire.TR, ZoneLockZone.nc_homes), + ZoneLock(PlanetSideEmpire.NC, ZoneLockZone.z9), + ZoneLock(PlanetSideEmpire.VS, ZoneLockZone.i1_i2_i3_i4) + ) + + private val sample3_expectedBenefits = Vector( + ZoneBenefit(PlanetSideEmpire.TR, ZoneLockBenefit.z4), + ZoneBenefit(PlanetSideEmpire.TR, ZoneLockBenefit.z3), + ZoneBenefit(PlanetSideEmpire.TR, ZoneLockBenefit.nc_homes), + ZoneBenefit(PlanetSideEmpire.NC, ZoneLockBenefit.z9), + ZoneBenefit(PlanetSideEmpire.VS, ZoneLockBenefit.i1_i2_i3_i4) + ) + + private val sample4_expectedLocks = Vector( + ZoneLock(PlanetSideEmpire.VS, ZoneLockZone.nc_homes), + ZoneLock(PlanetSideEmpire.VS, ZoneLockZone.tr_homes), + ZoneLock(PlanetSideEmpire.VS, ZoneLockZone.z3), + ZoneLock(PlanetSideEmpire.VS, ZoneLockZone.z4), + ZoneLock(PlanetSideEmpire.VS, ZoneLockZone.z9), + ZoneLock(PlanetSideEmpire.VS, ZoneLockZone.i1_i2_i3_i4) + ) + + private val sample4_expectedBenefits = Vector( + ZoneBenefit(PlanetSideEmpire.VS, ZoneLockBenefit.z4), + ZoneBenefit(PlanetSideEmpire.VS, ZoneLockBenefit.z9), + ZoneBenefit(PlanetSideEmpire.VS, ZoneLockBenefit.i1_i2_i3_i4), + ZoneBenefit(PlanetSideEmpire.VS, ZoneLockBenefit.z3), + ZoneBenefit(PlanetSideEmpire.VS, ZoneLockBenefit.tr_homes), + ZoneBenefit(PlanetSideEmpire.VS, ZoneLockBenefit.nc_homes) ) "decode sample1" in { @@ -43,11 +112,71 @@ class EmpireBenefitsMessageTest extends Specification { "encode sample1" in { val msg = EmpireBenefitsMessage( - entriesA = sample1_expectedLocks, - entriesB = sample1_expectedBenefits + zoneLocks = sample1_expectedLocks, + zoneBenefits = sample1_expectedBenefits ) val pkt = PacketCoding.encodePacket(msg).require.toByteVector pkt mustEqual sample1 } + + "decode sample2" in { + PacketCoding.decodePacket(sample2).require match { + case EmpireBenefitsMessage(a, b) => + a mustEqual sample2_expectedLocks + b mustEqual sample2_expectedBenefits + case _ => + ko + } + } + + "encode sample2" in { + val msg = EmpireBenefitsMessage( + zoneLocks = sample2_expectedLocks, + zoneBenefits = sample2_expectedBenefits + ) + val pkt = PacketCoding.encodePacket(msg).require.toByteVector + + pkt mustEqual sample2 + } + + "decode sample3" in { + PacketCoding.decodePacket(sample3).require match { + case EmpireBenefitsMessage(a, b) => + a mustEqual sample3_expectedLocks + b mustEqual sample3_expectedBenefits + case _ => + ko + } + } + + "encode sample3" in { + val msg = EmpireBenefitsMessage( + zoneLocks = sample3_expectedLocks, + zoneBenefits = sample3_expectedBenefits + ) + val pkt = PacketCoding.encodePacket(msg).require.toByteVector + + pkt mustEqual sample3 + } + + "decode sample4" in { + PacketCoding.decodePacket(sample4).require match { + case EmpireBenefitsMessage(a, b) => + a mustEqual sample4_expectedLocks + b mustEqual sample4_expectedBenefits + case _ => + ko + } + } + + "encode sample4" in { + val msg = EmpireBenefitsMessage( + zoneLocks = sample4_expectedLocks, + zoneBenefits = sample4_expectedBenefits + ) + val pkt = PacketCoding.encodePacket(msg).require.toByteVector + + pkt mustEqual sample4 + } }