Add OutfitMemberEvent packet

Fix and improve some other Outfit packets and tests
This commit is contained in:
Resaec 2024-04-26 02:16:40 +02:00
parent 4bc272c2f0
commit d75ab1166f
6 changed files with 638 additions and 81 deletions

View file

@ -474,7 +474,7 @@ object GamePacketOpcode extends Enumeration {
case 0x8f => game.OutfitEvent.decode
// OPCODES 0x90-9f
case 0x90 => noDecoder(OutfitMemberEvent)
case 0x90 => game.OutfitMemberEvent.decode
case 0x91 => game.OutfitMemberUpdate.decode
case 0x92 => game.PlanetsideStringAttributeMessage.decode
case 0x93 => game.DataChallengeMessage.decode

View file

@ -3,6 +3,7 @@ package net.psforever.packet.game
import net.psforever.packet.GamePacketOpcode.Type
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.PlanetSideGUID
import scodec.{Attempt, Codec, Err}
import scodec.bits.BitVector
import scodec.codecs._
@ -10,7 +11,7 @@ import shapeless.{::, HNil}
final case class OutfitEvent(
request_type: OutfitEvent.RequestType.Type,
unk1: Int,
outfit_guid: PlanetSideGUID,
action: OutfitEventAction
) extends PlanetSideGamePacket {
type Packet = OutfitEvent
@ -24,33 +25,79 @@ abstract class OutfitEventAction(val code: Int)
object OutfitEventAction {
final case class CreatedOutfit(
final case class OutfitRankNames(
rank1: String,
rank2: String,
rank3: String,
rank4: String,
rank5: String,
rank6: String,
rank7: String,
rank8: String,
)
final case class OutfitInfo(
unk1: Int,
unk2: Int,
unk3: Int,
unk4: Int,
outfit_name: String,
unk6: Long,
unk7: Long,
members: Int,
member_count: Int,
unk9: Int,
unk10: String,
unk11: String,
unk12: String,
unk13: String,
unk14: String,
unk15: String,
unk16: String,
unk17: String,
unk18: String,
unk19: Int,
outfit_rank_names: OutfitRankNames,
motd: String,
owner_guid: PlanetSideGUID, // ?
unk20: Int,
unk21: Long,
unk22: Long,
unk21: Int,
unk21_2: Int,
created_timestamp: Long,
unk23: Long,
unk24: Long,
unk25: Int,
unk25: Long,
u123: Int,
)
final case class Unk0(
outfitInfo: OutfitInfo
) extends OutfitEventAction(code = 0)
final case class Unk1(
unk0: Int,
unk1: Int,
unk2: Int,
unk3: Boolean,
) extends OutfitEventAction(code = 1)
final case class Unk2(
outfitInfo: OutfitInfo,
) extends OutfitEventAction(code = 2)
final case class Unk3(
unk0: Int,
unk1: Int,
unk2: Int,
unk3: Boolean,
data: BitVector,
) extends OutfitEventAction(code = 3)
final case class Unk4(
unk0: Int,
unk1: Int,
unk2: Int,
unk3: Int,
unk4: Boolean,
data: BitVector,
) extends OutfitEventAction(code = 4)
final case class Unk5(
unk0: Int,
unk1: Int,
unk2: Int,
unk3: Int,
unk4: Boolean,
data: BitVector,
) extends OutfitEventAction(code = 5)
final case class Unknown(badCode: Int, data: BitVector) extends OutfitEventAction(badCode)
/**
@ -60,42 +107,151 @@ object OutfitEventAction {
object Codecs {
private val everFailCondition = conditional(included = false, bool)
val CreatedOutfitCodec: Codec[CreatedOutfit] =
(uint8L ::
uint4L ::
uintL(3) ::
private val OutfitRankNamesCodec: Codec[OutfitRankNames] = (
PacketHelpers.encodedWideString ::
PacketHelpers.encodedWideString ::
PacketHelpers.encodedWideString ::
PacketHelpers.encodedWideString ::
PacketHelpers.encodedWideString ::
PacketHelpers.encodedWideString ::
PacketHelpers.encodedWideString ::
PacketHelpers.encodedWideString
).xmap[OutfitRankNames](
{
case u0 :: u1 :: u2 :: u3 :: u4 :: u5 :: u6 :: u7 :: HNil =>
OutfitRankNames(u0, u1, u2, u3, u4, u5, u6, u7)
},
{
case OutfitRankNames(u0, u1, u2, u3, u4, u5, u6, u7) =>
u0 :: u1 :: u2 :: u3 :: u4 :: u5 :: u6 :: u7 :: HNil
}
)
private val InfoCodec: Codec[OutfitInfo] = (
uint8L ::
uint8L ::
PacketHelpers.encodedWideStringAligned(5) ::
uint32L ::
uint32L ::
uint16L ::
uint16L ::
PacketHelpers.encodedString ::
PacketHelpers.encodedString ::
PacketHelpers.encodedString ::
PacketHelpers.encodedString ::
PacketHelpers.encodedString ::
PacketHelpers.encodedString ::
PacketHelpers.encodedString ::
PacketHelpers.encodedString ::
PacketHelpers.encodedString ::
uint16L ::
uint16L ::
OutfitRankNamesCodec ::
PacketHelpers.encodedWideString ::
PlanetSideGUID.codec ::
uint16L :: //
uint8L :: // bool somewhere here
uintL(1) :: //
("created_timestamp" | uint32L) ::
uint32L ::
uint32L ::
uint32L ::
uint32L ::
uint16L
).xmap[CreatedOutfit](
uintL(7)
).xmap[OutfitInfo](
{
case u2 :: u3 :: u4 :: outfit_name :: u6 :: u7 :: members :: u9 :: u10 :: u11 :: u12 :: u13 :: u14 :: u15 :: u16 :: u17 :: u18 :: u19 :: u20 :: u21 :: u22 :: u23 :: u24 :: u25 :: HNil =>
CreatedOutfit(u2, u3, u4, outfit_name, u6, u7, members, u9, u10, u11, u12, u13, u14, u15, u16, u17, u18, u19, u20, u21, u22, u23, u24, u25)
case u1 :: u2 :: outfit_name :: u6 :: u7 :: member_count :: u9 :: outfit_rank_names :: motd :: u19 :: u20 :: u21 :: u21_2 :: created_timestamp :: u23 :: u24 :: u25 :: u123 :: HNil =>
OutfitInfo(u1, u2, outfit_name, u6, u7, member_count, u9, outfit_rank_names, motd, u19, u20, u21, u21_2, created_timestamp, u23, u24, u25, u123)
},
{
case CreatedOutfit(u2, u3, u4, outfit_name, u6, u7, members, u9, u10, u11, u12, u13, u14, u15, u16, u17, u18, u19, u20, u21, u22, u23, u24, u25) =>
u2 :: u3 :: u4 :: outfit_name :: u6 :: u7 :: members :: u9 :: u10 :: u11 :: u12 :: u13 :: u14 :: u15 :: u16 :: u17 :: u18 :: u19 :: u20 :: u21 :: u22 :: u23 :: u24 :: u25 :: HNil
case OutfitInfo(u1, u2, outfit_name, u6, u7, member_count, u9, outfit_rank_names, motd, u19, u20, u21, u21_2, created_timestamp, u23, u24, u25, u123) =>
u1 :: u2 :: outfit_name :: u6 :: u7 :: member_count :: u9 :: outfit_rank_names :: motd :: u19 :: u20 :: u21 :: u21_2 :: created_timestamp :: u23 :: u24 :: u25 :: u123 :: HNil
}
)
val Unk0Codec: Codec[Unk0] = (
InfoCodec
).xmap[Unk0](
{
case info =>
Unk0(info)
},
{
case Unk0(info) =>
info
}
)
val Unk1Codec: Codec[Unk1] = (
uint8L ::
uint8L ::
uint4L ::
bool
).xmap[Unk1](
{
case u0 :: u1 :: u2 :: u3 :: HNil =>
Unk1(u0, u1, u2, u3)
},
{
case Unk1(u0, u1, u2, u3) =>
u0 :: u1 :: u2 :: u3 :: HNil
}
)
val Unk2Codec: Codec[Unk2] = (
InfoCodec
).xmap[Unk2](
{
case info =>
Unk2(info)
},
{
case Unk2(info) =>
info
}
)
val Unk3Codec: Codec[Unk3] = (
uint8L ::
uint8L ::
uint4L ::
bool ::
bits
).xmap[Unk3](
{
case u0 :: u1 :: u2 :: u3 :: data :: HNil =>
Unk3(u0, u1, u2, u3, data)
},
{
case Unk3(u0, u1, u2, u3, data) =>
u0 :: u1 :: u2 :: u3 :: data :: HNil
}
)
val Unk4Codec: Codec[Unk4] = (
uint16L ::
uint16L ::
uint16L ::
uint4L ::
bool ::
bits
).xmap[Unk4](
{
case u0 :: u1 :: u2 :: u3 :: u4 :: data :: HNil =>
Unk4(u0, u1, u2, u3, u4, data)
},
{
case Unk4(u0, u1, u2, u3, u4, data) =>
u0 :: u1 :: u2 ::u3 :: u4 :: data :: HNil
}
)
val Unk5Codec: Codec[Unk5] = (
uint16L ::
uint16L ::
uint16L ::
uint4L ::
bool ::
bits
).xmap[Unk5](
{
case u0 :: u1 :: u2 :: u3 :: u4 :: data :: HNil =>
Unk5(u0, u1, u2, u3, u4, data)
},
{
case Unk5(u0, u1, u2, u3, u4, data) =>
u0 :: u1 :: u2 :: u3 :: u4 :: data :: HNil
}
)
/**
* A common form for known action code indexes with an unknown purpose and transformation is an "Unknown" object.
*
@ -129,16 +285,36 @@ object OutfitEvent extends Marshallable[OutfitEvent] {
object RequestType extends Enumeration {
type Type = Value
val unk0: RequestType.Value = Value(0)
val unk1: RequestType.Value = Value(1)
val unk2: RequestType.Value = Value(2)
val unk3: RequestType.Value = Value(3)
val CreatedOutfit: RequestType.Value = Value(4)
val unk5: RequestType.Value = Value(5)
val Unk6: RequestType.Value = Value(6)
val Unk7: RequestType.Value = Value(7)
val Unk0: RequestType.Value = Value(0)
val Unk1: RequestType.Value = Value(1) // send in SMP after a f load of OutfitMemberEvent
val Unk2: RequestType.Value = Value(2) // send after creating an outfit
val Unk3: RequestType.Value = Value(3) // below
val Unk4: RequestType.Value = Value(4)
val Unk5: RequestType.Value = Value(5)
val unk6: RequestType.Value = Value(6)
val unk7: RequestType.Value = Value(7)
implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uint4L)
/*
unk3 -- #66162 PSCap-2016-02-28_02-58-10-PM.txt
MP(
SMP(
MPEx(
OutfitMembershipResponse,
OutfitEvent,
SquadMemberEvent
)
),
SMP(
MPEx(
PlanetsideAttributeMessage x 3 + PlanetsideStringAttributeMessage
)
)
)
)
*/
implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uintL(3))
}
private def selectFromType(code: Int): Codec[OutfitEventAction] = {
@ -146,12 +322,12 @@ object OutfitEvent extends Marshallable[OutfitEvent] {
import scala.annotation.switch
((code: @switch) match {
case 0 => unknownCodec(action = code)
case 1 => unknownCodec(action = code)
case 2 => unknownCodec(action = code)
case 3 => unknownCodec(action = code)
case 4 => CreatedOutfitCodec // sent after /outfitcreate
case 5 => unknownCodec(action = code)
case 0 => Unk0Codec
case 1 => Unk1Codec
case 2 => Unk2Codec // sent after /outfitcreate ?
case 3 => Unk3Codec
case 4 => Unk4Codec
case 5 => Unk5Codec
case 6 => unknownCodec(action = code)
case 7 => unknownCodec(action = code)
@ -161,17 +337,17 @@ object OutfitEvent extends Marshallable[OutfitEvent] {
implicit val codec: Codec[OutfitEvent] = (
("request_type" | RequestType.codec) >>:~ { request_type =>
("avatar_guid" | uint16L) ::
("outfit_guid" | PlanetSideGUID.codec) ::
("action" | selectFromType(request_type.id))
}
).xmap[OutfitEvent](
{
case request_type :: avatar_guid :: action :: HNil =>
OutfitEvent(request_type, avatar_guid, action)
case request_type :: outfit_guid :: action :: HNil =>
OutfitEvent(request_type, outfit_guid, action)
},
{
case OutfitEvent(request_type, avatar_guid, action) =>
request_type :: avatar_guid :: action :: HNil
case OutfitEvent(request_type, outfit_guid, action) =>
request_type :: outfit_guid :: action :: HNil
}
)
}

View file

@ -0,0 +1,57 @@
// Copyright (c) 2024 PSForever
package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.PlanetSideGUID
import scodec.Codec
import scodec.codecs._
import shapeless.{::, HNil}
final case class OutfitMemberEvent(
unk00: Int,
outfit_id: PlanetSideGUID,
unk1: Int,
unk2: Int,
unk3: Int,
member_name: String,
unk7: Int,
unk8: Int,
unk9: Int,
unk10: Int,
unk12: Int,
unk13: Int,
unk14: Int,
unk15: Int,
) extends PlanetSideGamePacket {
type Packet = OutfitMemberEvent
def opcode = GamePacketOpcode.OutfitMemberEvent
def encode = OutfitMemberEvent.encode(this)
}
object OutfitMemberEvent extends Marshallable[OutfitMemberEvent] {
implicit val codec: Codec[OutfitMemberEvent] = (
("unk00" | uintL(2)) ::
("outfit_id" | PlanetSideGUID.codec) ::
("unk1" | uint16L) ::
("unk2" | uint16L) ::
("unk3" | uint16L) ::
("member_name" | PacketHelpers.encodedWideStringAligned(6)) ::
("unk7" | uint8L) ::
("unk8" | uint8L) ::
("unk9" | uint16L) ::
("unk10" | uint8L) ::
("unk12" | uint8L) ::
("unk13" | uint8L) ::
("unk14" | uint8L) ::
("unk15" | uint8L)
).xmap[OutfitMemberEvent](
{
case unk00 :: outfit_id :: u1 :: u2 :: u3 :: member_name :: u7 :: u8 :: u9 :: u10 :: u12 :: u13 :: u14 :: u15 :: HNil =>
OutfitMemberEvent(unk00, outfit_id, u1, u2, u3, member_name, u7, u8, u9, u10, u12, u13, u14, u15)
},
{
case OutfitMemberEvent(unk00, outfit_id, u1, u2, u3, member_name, u7, u8, u9, u10, u12, u13, u14, u15) =>
unk00 :: outfit_id :: u1 :: u2 :: u3 :: member_name :: u7 :: u8 :: u9 :: u10 :: u12 :: u13 :: u14 :: u15 :: HNil
}
)
}

View file

@ -1,13 +1,18 @@
// Copyright (c) 2024 PSForever
package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
import net.psforever.types.PlanetSideGUID
import scodec.Codec
import scodec.codecs._
import shapeless.{::, HNil}
final case class OutfitMemberUpdate(outfit_id: PlanetSideGUID, unk1: Int, unk2: Int, unk3: Int) extends PlanetSideGamePacket {
final case class OutfitMemberUpdate(
outfit_guid: PlanetSideGUID,
unk1: Int,
unk2: Int,
unk3: Int,
) extends PlanetSideGamePacket {
type Packet = OutfitMemberUpdate
def opcode = GamePacketOpcode.OutfitMemberUpdate
def encode = OutfitMemberUpdate.encode(this)
@ -15,19 +20,18 @@ final case class OutfitMemberUpdate(outfit_id: PlanetSideGUID, unk1: Int, unk2:
object OutfitMemberUpdate extends Marshallable[OutfitMemberUpdate] {
implicit val codec: Codec[OutfitMemberUpdate] = (
("outfit_id" | PlanetSideGUID.codec) ::
("outfit_guid" | PlanetSideGUID.codec) ::
("unk1" | uint16L) ::
("unk2" | uint16L) ::
("unk3" | uint8L)
).xmap[OutfitMemberUpdate](
{
case u0 :: u1 :: u2 :: u3 :: HNil =>
OutfitMemberUpdate(u0, u1, u2, u3)
case outfit_guid :: u1 :: u2 :: u3 :: HNil =>
OutfitMemberUpdate(outfit_guid, u1, u2, u3)
},
{
case OutfitMemberUpdate(u0, u1, u2, u3) =>
u0 :: u1 :: u2 :: u3 :: HNil
case OutfitMemberUpdate(outfit_guid, u1, u2, u3) =>
outfit_guid :: u1 :: u2 :: u3 :: HNil
}
)
// ).as[OutfitMemberUpdate]
}

View file

@ -1,32 +1,294 @@
// Copyright (c) 2023 PSForever
// Copyright (c) 2024 PSForever
package game
import net.psforever.packet._
import net.psforever.packet.game.OutfitEvent.RequestType
import net.psforever.packet.game.OutfitEventAction._
import net.psforever.packet.game._
import net.psforever.types.PlanetSideGUID
import org.specs2.mutable._
import scodec.bits._
class OutfitEventTest extends Specification {
val created_ABC = hex"8f 4 0201 feff 2e 0 50006c0061006e006500740053006900640065005f0046006f00720065007600650072005f00560061006e007500 00000000 00000000 0100 0000 80 80 80 80 80 80 80 80 80 0070 4982 00000000 00000000 00000000 00000000 0000"
val unk0_ABC: ByteVector = ByteVector.fromValidHex(
"8f 1 a8c2 0001" + // packet head
"2a 0 42006c00610063006b002000410072006d006f0072006500640020005200650061007000650072007300" + // Black Armored Reapers
"1d9c4d0d" +
"1d9c4d0d" +
"ab00 0000" +
"88 44006f00670020004d00650061007400" + // Dog Meat
"87 5200750073007300690061006e00" + // Russian
"80" + //
"80" + //
"8d 5300710075006100640020004c00650061006400650072007300" + // Squad Leaders
"91 41006300740069006e006700200043006f006d006d0061006e006400650072007300" + // Acting Commanders
"87 5200650061007000650072007300" + // Reapers
"80" + //
"00" +
"9c 5c0023003000300030003000660066004d0075006d0062006c00650020005c00230030003000330033006600660049006e0066006f0020005c0023003000300036003600660066006900730020005c0023003000300039003900660066007400680065006d006f006f00730065002e00740079007000650066007200610067002e0063006f006d0020005c00230030003000630063006600660070006f007200740020005c002300300030006600660066006600390033003500300020005c0023003000300063006300660066006a006f0069006e0020005c0023003000300039003900660066006900740020005c0023003000300036003600660066006f00720020005c0023003000300033003300660066006200650020005c0023003000300030003000660066006b00690063006b00650064002e00" +
"0f80" +
"0000 00737296 24000000 00000000 00000000 0000")
val unk1_ABC: ByteVector = hex"8f 2 302a 10 00 0"
val unk2_ABC: ByteVector = ByteVector.fromValidHex(
"8f 4 0201 feff" +
"2e 0 50006c0061006e006500740053006900640065005f0046006f00720065007600650072005f00560061006e007500" + // PlanetSide_Forever_Vanu
"00000000" +
"00000000" +
"0100 0000" +
"80" +
"80" +
"80" +
"80" +
"80" +
"80" +
"80" +
"80" +
"80" +
"0070" +
"4982 00000000 00000000 00000000 00000000 0000")
val unk3_ABC: ByteVector = hex"8f 6 0201 fe fe 0"
val unk4_ABC: ByteVector = hex"8f 8 0201 fefe a02a 1000 0"
val unk5_ABC: ByteVector = hex"8f a 0201 fefe 0400 0000 0"
"decode CreatedOutfit ABC" in {
PacketCoding.decodePacket(created_ABC).require match {
case OutfitEvent(request_type, unk1, action) =>
request_type mustEqual RequestType.CreatedOutfit
unk1 mustEqual 258
action mustEqual CreatedOutfit(unk2 = 254, unk3 = 15, unk4 = 7, outfit_name = "PlanetSide_Forever_Vanu", unk6 = 0, unk7 = 0, members = 1, unk9 = 0, "", "", "", "", "", "", "", "", "", 28672, 33353, 0, 0, 0, 0, 0)
"decode Unk0 ABC" in {
PacketCoding.decodePacket(unk0_ABC).require match {
case OutfitEvent(request_type, outfit_guid, action) =>
request_type mustEqual RequestType.Unk0
outfit_guid mustEqual PlanetSideGUID(25044)
action mustEqual Unk0(
OutfitInfo(
unk1 = 0,
unk2 = 0,
outfit_name = "Black Armored Reapers",
unk6 = 223190045,
unk7 = 223190045,
member_count = 171,
unk9 = 0,
OutfitRankNames("Dog Meat","Russian","","","Squad Leaders","Acting Commanders","Reapers",""),
"\\#0000ffMumble \\#0033ffInfo \\#0066ffis \\#0099ffthemoose.typefrag.com \\#00ccffport \\#00ffff9350 \\#00ccffjoin \\#0099ffit \\#0066ffor \\#0033ffbe \\#0000ffkicked.",
PlanetSideGUID(32783),
0,
0,
0,
1210901990,
0,
0,
0,
0,
)
)
case _ =>
ko
}
}
"encode CreatedOutfit ABC" in {
val msg = OutfitEvent(RequestType.CreatedOutfit, 258, CreatedOutfit(unk2 = 254, unk3 = 15, unk4 = 7, outfit_name = "PlanetSide_Forever_Vanu", unk6 = 0, unk7 = 0, members = 1, unk9 = 0, "", "", "", "", "", "", "", "", "", 28672, 33353, 0, 0, 0, 0, 0))
"encode Unk0 ABC" in {
val msg = OutfitEvent(
RequestType.Unk0,
PlanetSideGUID(25044),
Unk0(
OutfitInfo(
unk1 = 0,
unk2 = 0,
outfit_name = "Black Armored Reapers",
unk6 = 223190045,
unk7 = 223190045,
member_count = 171,
unk9 = 0,
OutfitRankNames("Dog Meat","Russian","","","Squad Leaders","Acting Commanders","Reapers",""),
"\\#0000ffMumble \\#0033ffInfo \\#0066ffis \\#0099ffthemoose.typefrag.com \\#00ccffport \\#00ffff9350 \\#00ccffjoin \\#0099ffit \\#0066ffor \\#0033ffbe \\#0000ffkicked.",
PlanetSideGUID(32783),
0,
0,
0,
1210901990,
0,
0,
0,
0,
)
)
)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual created_ABC
pkt mustEqual unk0_ABC
}
"decode Unk1 ABC" in {
PacketCoding.decodePacket(unk1_ABC).require match {
case OutfitEvent(request_type, outfit_guid, action) =>
request_type mustEqual RequestType.Unk1
outfit_guid mustEqual PlanetSideGUID(5400)
action mustEqual Unk1(unk0 = 8, unk1 = 0, unk2 = 0, unk3 = false)
case _ =>
ko
}
}
"encode Unk1 ABC" in {
val msg = OutfitEvent(
RequestType.Unk1,
PlanetSideGUID(5400),
Unk1(
unk0 = 8,
unk1 = 0,
unk2 = 0,
unk3 = false,
)
)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual unk1_ABC
}
"decode Unk2 ABC" in {
PacketCoding.decodePacket(unk2_ABC).require match {
case OutfitEvent(request_type, outfit_guid, action) =>
request_type mustEqual RequestType.Unk2
outfit_guid mustEqual PlanetSideGUID(1)
action mustEqual Unk2(OutfitInfo(unk1 = 255, unk2 = 127, outfit_name = "PlanetSide_Forever_Vanu",
unk6 = 0, unk7 = 0, member_count = 1, unk9 = 0, OutfitRankNames("","","","","","","",""),
"", PlanetSideGUID(28672), 33353, 0, 0, 0, 0, 0, 0, 0))
case _ =>
ko
}
}
"encode Unk2 ABC" in {
val msg = OutfitEvent(
RequestType.Unk2,
PlanetSideGUID(1),
Unk2(
OutfitInfo(
unk1 = 255,
unk2 = 127,
outfit_name = "PlanetSide_Forever_Vanu",
unk6 = 0,
unk7 = 0,
member_count = 1,
unk9 = 0,
OutfitRankNames("","","","","","","",""),
"",
PlanetSideGUID(28672),
33353,
0,
0,
0,
0,
0,
0,
0,
)
)
)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual unk2_ABC
}
"decode Unk3 ABC" in {
PacketCoding.decodePacket(unk3_ABC).require match {
case OutfitEvent(request_type, outfit_guid, action) =>
request_type mustEqual RequestType.Unk3
outfit_guid mustEqual PlanetSideGUID(1)
action mustEqual Unk3(
unk0 = 255,
unk1 = 127,
unk2 = 0,
unk3 = false,
BitVector.fromValidHex("")
)
case _ =>
ko
}
}
"encode Unk3 ABC" in {
val msg = OutfitEvent(
RequestType.Unk3,
PlanetSideGUID(1),
Unk3(
unk0 = 255,
unk1 = 127,
unk2 = 0,
unk3 = false,
BitVector.fromValidHex("")
)
)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual unk3_ABC
}
"decode Unk4 ABC" in {
PacketCoding.decodePacket(unk4_ABC).require match {
case OutfitEvent(request_type, outfit_guid, action) =>
request_type mustEqual RequestType.Unk4
outfit_guid mustEqual PlanetSideGUID(1)
action mustEqual Unk4(
unk0 = 32767,
unk1 = 5456,
unk2 = 8,
0,
unk4 = false,
BitVector.fromValidHex("")
)
case _ =>
ko
}
}
"encode Unk4 ABC" in {
val msg = OutfitEvent(
RequestType.Unk4,
PlanetSideGUID(1),
Unk4(
unk0 = 32767,
unk1 = 5456,
unk2 = 8,
unk3 = 0,
unk4 = false,
BitVector.fromValidHex("")
)
)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual unk4_ABC
}
"decode Unk5 ABC" in {
PacketCoding.decodePacket(unk5_ABC).require match {
case OutfitEvent(request_type, outfit_guid, action) =>
request_type mustEqual RequestType.Unk5
outfit_guid mustEqual PlanetSideGUID(1)
action mustEqual Unk5(
unk0 = 32767,
unk1 = 2,
unk2 = 0,
unk3 = 0,
unk4 = false,
BitVector.fromValidHex("") // OR f88c2a0417c1a06101001f20f4b8c00000404090ac9c6745dea88cadf0f810e03e0200f92 with bool at the back
)
case _ =>
ko
}
}
"encode Unk5 ABC" in {
val msg = OutfitEvent(
RequestType.Unk5,
PlanetSideGUID(1),
Unk5(
unk0 = 32767,
unk1 = 2,
unk2 = 0,
unk3 = 0,
unk4 = false,
BitVector.fromValidHex("")
)
)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual unk5_ABC
}
}

View file

@ -0,0 +1,58 @@
// Copyright (c) 2024 PSForever
package game
import net.psforever.packet._
import net.psforever.packet.game._
import net.psforever.types.PlanetSideGUID
import org.specs2.mutable._
import scodec.bits._
class OutfitMemberEventTest extends Specification {
//val unk0_ABC: ByteVector = hex"90 3518 4000 1a4e 4100 2 180 450078007000650072007400 8483 07e0 119d bfe0 70" // 0x90048640001030c28022404c0061007a00650072003100390038003200f43a45e00b4c604010
val unk0_ABC: ByteVector = hex"90 048640001030c28022404c0061007a00650072003100390038003200f43a45e00b4c604010"
"decode Unk0 ABC" in {
PacketCoding.decodePacket(unk0_ABC).require match {
case OutfitMemberEvent(unk00, outfit_guid, unk1, unk2, unk3, member_name, unk7, unk8, unk9, unk10, unk12, unk13, unk14, unk15) =>
unk00 mustEqual 0
outfit_guid mustEqual PlanetSideGUID(6418)
unk1 mustEqual 0
unk2 mustEqual 49984
unk3 mustEqual 10
member_name mustEqual "Lazer1982"
unk7 mustEqual 244
unk8 mustEqual 58
unk9 mustEqual 57413
unk10 mustEqual 11
unk12 mustEqual 76
unk13 mustEqual 96
unk14 mustEqual 64
unk15 mustEqual 16
case _ =>
ko
}
}
"encode Unk0 ABC" in {
val msg = OutfitMemberEvent(
unk00 = 0,
outfit_id = PlanetSideGUID(6418),
unk1 = 0,
unk2 = 49984,
unk3 = 10,
member_name = "Lazer1982",
unk7 = 244,
unk8 = 58,
unk9 = 57413,
unk10 = 11,
unk12 = 76,
unk13 = 96,
unk14 = 64,
unk15 = 16,
)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual unk0_ABC
}
}