Fix variable naming

Added comments
Added enums for ease of use
Typed packet variables
Added 3 more samples to test cases
This commit is contained in:
Resaec 2025-12-17 03:53:50 +01:00
parent 3c074bbd70
commit 6551de6f53
2 changed files with 228 additions and 33 deletions

View file

@ -1,14 +1,25 @@
// Copyright (c) 2025 PSForever // Copyright (c) 2025 PSForever
package net.psforever.packet.game 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.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.PlanetSideEmpire
import scodec.Codec import scodec.Codec
import scodec.codecs._ 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( final case class EmpireBenefitsMessage(
entriesA: Vector[ZoneLocks], zoneLocks: Vector[ZoneLock],
entriesB: Vector[ZoneBenefits] zoneBenefits: Vector[ZoneBenefit]
) extends PlanetSideGamePacket { ) extends PlanetSideGamePacket {
type Packet = EmpireBenefitsMessage type Packet = EmpireBenefitsMessage
def opcode = GamePacketOpcode.EmpireBenefitsMessage def opcode = GamePacketOpcode.EmpireBenefitsMessage
@ -17,28 +28,83 @@ final case class EmpireBenefitsMessage(
object EmpireBenefitsMessage extends Marshallable[EmpireBenefitsMessage] { object EmpireBenefitsMessage extends Marshallable[EmpireBenefitsMessage] {
final case class ZoneLocks( /**
empire: Int, * ZoneLockZone
zone: String *
* 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( final case class ZoneBenefit(
empire: Int, empire: PlanetSideEmpire.Type,
value: Int value: ZoneLockBenefit.Type
) )
private implicit val entryACodec: Codec[ZoneLocks] = ( private implicit val zoneLockCodec: Codec[ZoneLock] = (
("empire" | uint(2)) :: ("empire" | PlanetSideEmpire.codec) ::
("zone" | PacketHelpers.encodedStringAligned(6)) ("zone" | ZoneLockZone.codec)
).as[ZoneLocks] ).as[ZoneLock]
private implicit val entryBCodec: Codec[ZoneBenefits] = ( private implicit val zoneBenefitCodec: Codec[ZoneBenefit] = (
("empire" | uint(2)) :: ("empire" | PlanetSideEmpire.codec) ::
("benefit" | uint16L) ("benefit" | ZoneLockBenefit.codec)
).as[ZoneBenefits] ).as[ZoneBenefit]
implicit val codec: Codec[EmpireBenefitsMessage] = ( implicit val codec: Codec[EmpireBenefitsMessage] = (
("entriesA" | vectorOfN(uint32L.xmap(_.toInt, _.toLong), entryACodec)) :: ("zoneLocks" | vectorOfN(uint32L.xmap(_.toInt, _.toLong), zoneLockCodec)) ::
("entriesB" | vectorOfN(uint32L.xmap(_.toInt, _.toLong), entryBCodec)) ("zoneBenefits" | vectorOfN(uint32L.xmap(_.toInt, _.toLong), zoneBenefitCodec))
).as[EmpireBenefitsMessage] ).as[EmpireBenefitsMessage]
} }

View file

@ -2,8 +2,9 @@
package game package game
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game.EmpireBenefitsMessage.{ZoneLocks, ZoneBenefits} import net.psforever.packet.game.EmpireBenefitsMessage
import net.psforever.packet.game.{EmpireBenefitsMessage, OutfitEvent} import net.psforever.packet.game.EmpireBenefitsMessage.{ZoneBenefit, ZoneLock, ZoneLockBenefit, ZoneLockZone}
import net.psforever.types.PlanetSideEmpire
import org.specs2.mutable._ import org.specs2.mutable._
import scodec.bits._ import scodec.bits._
@ -17,18 +18,86 @@ class EmpireBenefitsMessageTest extends Specification {
"004000600410020300" "004000600410020300"
) )
val sample1_expectedLocks = Vector( val sample2: ByteVector = ByteVector.fromValidHex(
ZoneLocks(0, "lock-z3"), "d7" +
ZoneLocks(0, "lock-z4"), "05000000 23406c6f636b2d76732d686f6d657321c06c6f636b2d7a3321c06c6f636b2d7a3461c06c6f636b2d7a3964006c6f636b2d69312d69322d69332d6934" +
ZoneLocks(1, "lock-i1-i2-i3-i4"), "05000000 004000600024010300410000"
ZoneLocks(2, "lock-z9")
) )
val sample1_expectedBenefits = Vector( val sample3: ByteVector = ByteVector.fromValidHex(
ZoneBenefits(0, 1), "d7" +
ZoneBenefits(0, 6), "05000000 21c06c6f636b2d7a3321c06c6f636b2d7a3423406c6f636b2d6e632d686f6d657361c06c6f636b2d7a39a4006c6f636b2d69312d69322d69332d6934" +
ZoneBenefits(1, 4), "05000000 004000600020010300810000"
ZoneBenefits(2, 3) )
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 { "decode sample1" in {
@ -43,11 +112,71 @@ class EmpireBenefitsMessageTest extends Specification {
"encode sample1" in { "encode sample1" in {
val msg = EmpireBenefitsMessage( val msg = EmpireBenefitsMessage(
entriesA = sample1_expectedLocks, zoneLocks = sample1_expectedLocks,
entriesB = sample1_expectedBenefits zoneBenefits = sample1_expectedBenefits
) )
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual sample1 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
}
} }