mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
Add OutfitMemberEvent packet
Fix and improve some other Outfit packets and tests
This commit is contained in:
parent
4bc272c2f0
commit
d75ab1166f
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -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]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
58
src/test/scala/game/OutfitMemberEventTest.scala
Normal file
58
src/test/scala/game/OutfitMemberEventTest.scala
Normal 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
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue