Simplified Outfit packet usage

Outfit cleanup
More tests
This commit is contained in:
Resaec 2025-08-24 16:43:24 +02:00
parent 8de797087f
commit cc16040ec3
14 changed files with 359 additions and 333 deletions

View file

@ -9,8 +9,7 @@ import scodec.codecs._
import shapeless.{::, HNil} import shapeless.{::, HNil}
final case class OutfitEvent( final case class OutfitEvent(
request_type: OutfitEvent.RequestType.Type, outfit_id: Long,
outfit_guid: Long,
action: OutfitEventAction action: OutfitEventAction
) extends PlanetSideGamePacket { ) extends PlanetSideGamePacket {
type Packet = OutfitEvent type Packet = OutfitEvent
@ -84,13 +83,13 @@ object OutfitEventAction {
private val OutfitRankNamesCodec: Codec[OutfitRankNames] = ( private val OutfitRankNamesCodec: Codec[OutfitRankNames] = (
PacketHelpers.encodedWideString :: PacketHelpers.encodedWideString ::
PacketHelpers.encodedWideString :: PacketHelpers.encodedWideString ::
PacketHelpers.encodedWideString :: PacketHelpers.encodedWideString ::
PacketHelpers.encodedWideString :: PacketHelpers.encodedWideString ::
PacketHelpers.encodedWideString :: PacketHelpers.encodedWideString ::
PacketHelpers.encodedWideString :: PacketHelpers.encodedWideString ::
PacketHelpers.encodedWideString :: PacketHelpers.encodedWideString ::
PacketHelpers.encodedWideString PacketHelpers.encodedWideString
).xmap[OutfitRankNames]( ).xmap[OutfitRankNames](
{ {
case u0 :: u1 :: u2 :: u3 :: u4 :: u5 :: u6 :: u7 :: HNil => case u0 :: u1 :: u2 :: u3 :: u4 :: u5 :: u6 :: u7 :: HNil =>
@ -103,20 +102,20 @@ object OutfitEventAction {
) )
private val InfoCodec: Codec[OutfitInfo] = ( private val InfoCodec: Codec[OutfitInfo] = (
("outfit_name" | PacketHelpers.encodedWideStringAligned(5)) :: ("outfit_name" | PacketHelpers.encodedWideStringAligned(5)) ::
("outfit_points1" | uint32L) :: ("outfit_points1" | uint32L) ::
("outfit_points2" | uint32L) :: ("outfit_points2" | uint32L) ::
("member_count" | uint32L) :: ("member_count" | uint32L) ::
("outfit_rank_names" | OutfitRankNamesCodec) :: ("outfit_rank_names" | OutfitRankNamesCodec) ::
("motd" | PacketHelpers.encodedWideString) :: ("motd" | PacketHelpers.encodedWideString) ::
("" | uint8L) :: ("" | uint8L) ::
("" | bool) :: ("" | bool) ::
("" | uint32L) :: ("" | uint32L) ::
("created_timestamp" | uint32L) :: ("created_timestamp" | uint32L) ::
("" | uint32L) :: ("" | uint32L) ::
("" | uint32L) :: ("" | uint32L) ::
("" | uint32L) ("" | uint32L)
).xmap[OutfitInfo]( ).xmap[OutfitInfo](
{ {
case outfit_name :: outfit_points1 :: outfit_points2 :: member_count :: outfit_rank_names :: motd :: u10 :: u11 :: u12 :: created_timestamp :: u23 :: u24 :: u25 :: HNil => case outfit_name :: outfit_points1 :: outfit_points2 :: member_count :: outfit_rank_names :: motd :: u10 :: u11 :: u12 :: created_timestamp :: u23 :: u24 :: u25 :: HNil =>
OutfitInfo(outfit_name, outfit_points1, outfit_points2, member_count, outfit_rank_names, motd, u10, u11, u12, created_timestamp, u23, u24, u25) OutfitInfo(outfit_name, outfit_points1, outfit_points2, member_count, outfit_rank_names, motd, u10, u11, u12, created_timestamp, u23, u24, u25)
@ -213,17 +212,17 @@ object OutfitEventAction {
object OutfitEvent extends Marshallable[OutfitEvent] { object OutfitEvent extends Marshallable[OutfitEvent] {
object RequestType extends Enumeration { object PacketType extends Enumeration {
type Type = Value type Type = Value
val Unk0: RequestType.Value = Value(0) // start listing of members val Unk0: PacketType.Value = Value(0) // start listing of members
val Unk1: RequestType.Value = Value(1) // end listing of members val Unk1: PacketType.Value = Value(1) // end listing of members
val Unk2: RequestType.Value = Value(2) // send after creating an outfit // normal info, same as Unk0 val Unk2: PacketType.Value = Value(2) // send after creating an outfit // normal info, same as Unk0
val Unk3: RequestType.Value = Value(3) // below val Unk3: PacketType.Value = Value(3) // below
val UpdateOutfitId: RequestType.Value = Value(4) val UpdateOutfitId: PacketType.Value = Value(4)
val Unk5: RequestType.Value = Value(5) val Unk5: PacketType.Value = Value(5)
val Unk6: RequestType.Value = Value(6) val Unk6: PacketType.Value = Value(6)
val Unk7: RequestType.Value = Value(7) val Unk7: PacketType.Value = Value(7)
implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uintL(3)) implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uintL(3))
} }
@ -247,18 +246,18 @@ object OutfitEvent extends Marshallable[OutfitEvent] {
} }
implicit val codec: Codec[OutfitEvent] = ( implicit val codec: Codec[OutfitEvent] = (
("request_type" | RequestType.codec) >>:~ { request_type => ("packet_type" | PacketType.codec) >>:~ { packet_type =>
("outfit_guid" | uint32L) :: ("outfit_guid" | uint32L) ::
("action" | selectFromType(request_type.id)) ("action" | selectFromType(packet_type.id))
} }
).xmap[OutfitEvent]( ).xmap[OutfitEvent](
{ {
case request_type :: outfit_guid :: action :: HNil => case _ :: outfit_guid :: action :: HNil =>
OutfitEvent(request_type, outfit_guid, action) OutfitEvent(outfit_guid, action)
}, },
{ {
case OutfitEvent(request_type, outfit_guid, action) => case OutfitEvent(outfit_guid, action) =>
request_type :: outfit_guid :: action :: HNil OutfitEvent.PacketType(action.code) :: outfit_guid :: action :: HNil
} }
) )
} }

View file

@ -9,7 +9,6 @@ import scodec.codecs._
import shapeless.{::, HNil} import shapeless.{::, HNil}
final case class OutfitListEvent( final case class OutfitListEvent(
request_type: OutfitListEvent.RequestType.Type,
action: OutfitListEventAction action: OutfitListEventAction
) extends PlanetSideGamePacket { ) extends PlanetSideGamePacket {
type Packet = OutfitListEvent type Packet = OutfitListEvent
@ -50,11 +49,11 @@ object OutfitListEventAction {
val ListElementOutfitCodec: Codec[ListElementOutfit] = ( val ListElementOutfitCodec: Codec[ListElementOutfit] = (
("unk1" | uint32L) :: ("unk1" | uint32L) ::
("points" | uint32L) :: ("points" | uint32L) ::
("members" | uint32L) :: ("members" | uint32L) ::
("outfit_name" | PacketHelpers.encodedWideStringAligned(5)) :: ("outfit_name" | PacketHelpers.encodedWideStringAligned(5)) ::
("outfit_leader" | PacketHelpers.encodedWideString) ("outfit_leader" | PacketHelpers.encodedWideString)
).xmap[ListElementOutfit]( ).xmap[ListElementOutfit](
{ {
case u1 :: points :: members :: outfit_name :: outfit_leader :: HNil => case u1 :: points :: members :: outfit_name :: outfit_leader :: HNil =>
ListElementOutfit(u1, points, members, outfit_name, outfit_leader) ListElementOutfit(u1, points, members, outfit_name, outfit_leader)
@ -67,7 +66,7 @@ object OutfitListEventAction {
val Unk3Codec: Codec[Unk3] = ( val Unk3Codec: Codec[Unk3] = (
("unk1" | uint32L) ("unk1" | uint32L)
).xmap[Unk3]( ).xmap[Unk3](
{ {
case u1 => case u1 =>
Unk3(u1) Unk3(u1)
@ -109,17 +108,17 @@ object OutfitListEventAction {
object OutfitListEvent extends Marshallable[OutfitListEvent] { object OutfitListEvent extends Marshallable[OutfitListEvent] {
import shapeless.{::, HNil} import shapeless.{::, HNil}
object RequestType extends Enumeration { object PacketType extends Enumeration {
type Type = Value type Type = Value
val Unk0: RequestType.Value = Value(0) val Unk0: PacketType.Value = Value(0)
val Unk1: RequestType.Value = Value(1) val Unk1: PacketType.Value = Value(1)
val ListElementOutfit: RequestType.Value = Value(2) val ListElementOutfit: PacketType.Value = Value(2)
val Unk3: RequestType.Value = Value(3) val Unk3: PacketType.Value = Value(3)
val Unk4: RequestType.Value = Value(4) val Unk4: PacketType.Value = Value(4)
val Unk5: RequestType.Value = Value(5) val Unk5: PacketType.Value = Value(5)
val unk6: RequestType.Value = Value(6) val unk6: PacketType.Value = Value(6)
val unk7: RequestType.Value = Value(7) val unk7: PacketType.Value = Value(7)
implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uintL(3)) implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uintL(3))
} }
@ -132,7 +131,7 @@ object OutfitListEvent extends Marshallable[OutfitListEvent] {
case 0 => unknownCodec(action = code) case 0 => unknownCodec(action = code)
case 1 => unknownCodec(action = code) case 1 => unknownCodec(action = code)
case 2 => ListElementOutfitCodec case 2 => ListElementOutfitCodec
case 3 => Unk3Codec // indicated in code case 3 => Unk3Codec
case 4 => unknownCodec(action = code) case 4 => unknownCodec(action = code)
case 5 => unknownCodec(action = code) case 5 => unknownCodec(action = code)
case 6 => unknownCodec(action = code) case 6 => unknownCodec(action = code)
@ -143,17 +142,17 @@ object OutfitListEvent extends Marshallable[OutfitListEvent] {
} }
implicit val codec: Codec[OutfitListEvent] = ( implicit val codec: Codec[OutfitListEvent] = (
("request_type" | RequestType.codec) >>:~ { request_type => ("packet_type" | PacketType.codec) >>:~ { packet_type =>
("action" | selectFromType(request_type.id)).hlist ("action" | selectFromType(packet_type.id)).hlist
} }
).xmap[OutfitListEvent]( ).xmap[OutfitListEvent](
{ {
case request_type :: action :: HNil => case _ :: action :: HNil =>
OutfitListEvent(request_type, action) OutfitListEvent(action)
}, },
{ {
case OutfitListEvent(request_type, action) => case OutfitListEvent(action) =>
request_type :: action :: HNil OutfitListEvent.PacketType(action.code) :: action :: HNil
} }
) )
} }

View file

@ -9,7 +9,6 @@ import scodec.codecs._
import shapeless.{::, HNil} import shapeless.{::, HNil}
final case class OutfitMemberEvent( final case class OutfitMemberEvent(
packet_type: OutfitMemberEvent.PacketType.Type,
outfit_id: Long, outfit_id: Long,
member_id: Long, member_id: Long,
action: OutfitMemberEventAction action: OutfitMemberEventAction
@ -36,13 +35,13 @@ object OutfitMemberEventAction {
/* /*
action is unimplemented! if action == 0 unk2 will contain one additional uint32L action is unimplemented! if action == 0 unk2 will contain one additional uint32L
unk0_padding contains one byte of padding. may contain 4byte of unknown data depending on action padding contains one uint4L of padding. may contain uint32L of unknown data depending on action
*/ */
final case class Unk0( final case class Unk0(
member_name: String, member_name: String,
rank: Int, rank: Int,
points: Long, // client divides this by 100 points: Long, // client divides this by 100
last_login: Long, // seconds ago from current time, 0 if online last_online: Long, // seconds ago from current time, 0 if online
action: PacketType.Type, // should always be 1, otherwise there will be actual data in padding. not implemented! action: PacketType.Type, // should always be 1, otherwise there will be actual data in padding. not implemented!
padding: Int // should always be 0, 4 bits of padding // only contains data if action is 0 padding: Int // should always be 0, 4 bits of padding // only contains data if action is 0
) extends OutfitMemberEventAction(code = 0) ) extends OutfitMemberEventAction(code = 0)
@ -60,13 +59,13 @@ object OutfitMemberEventAction {
private val everFailCondition = conditional(included = false, bool) private val everFailCondition = conditional(included = false, bool)
val Unk0Codec: Codec[Unk0] = ( val Unk0Codec: Codec[Unk0] = (
("member_name" | PacketHelpers.encodedWideStringAligned(6)) :: // from here is packet_type == 0 only ("member_name" | PacketHelpers.encodedWideStringAligned(6)) :: // from here is packet_type == 0 only
("rank" | uint(3)) :: ("rank" | uint(3)) ::
("points" | uint32L) :: ("points" | uint32L) ::
("last_login" | uint32L) :: ("last_login" | uint32L) ::
("action" | OutfitMemberEventAction.PacketType.codec) :: ("action" | OutfitMemberEventAction.PacketType.codec) ::
("padding" | uint4L) ("padding" | uint4L)
).xmap[Unk0]( ).xmap[Unk0](
{ {
case member_name :: rank :: points :: last_login :: action :: padding :: HNil => case member_name :: rank :: points :: last_login :: action :: padding :: HNil =>
Unk0(member_name, rank, points, last_login, action, padding) Unk0(member_name, rank, points, last_login, action, padding)
@ -112,6 +111,8 @@ object OutfitMemberEvent extends Marshallable[OutfitMemberEvent] {
val Unk0: PacketType.Value = Value(0) val Unk0: PacketType.Value = Value(0)
val Unk1: PacketType.Value = Value(1) // Info: Player has been invited / response to OutfitMembershipRequest Unk2 for that player val Unk1: PacketType.Value = Value(1) // Info: Player has been invited / response to OutfitMembershipRequest Unk2 for that player
val Unk2: PacketType.Value = Value(2)
val Unk3: PacketType.Value = Value(3)
implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uintL(2)) implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uintL(2))
} }
@ -123,6 +124,8 @@ object OutfitMemberEvent extends Marshallable[OutfitMemberEvent] {
((code: @switch) match { ((code: @switch) match {
case 0 => Unk0Codec case 0 => Unk0Codec
case 1 => Unk1Codec case 1 => Unk1Codec
case 2 => unknownCodec(code)
case 3 => unknownCodec(code)
case _ => failureCodec(code) case _ => failureCodec(code)
}).asInstanceOf[Codec[OutfitMemberEventAction]] }).asInstanceOf[Codec[OutfitMemberEventAction]]
@ -134,14 +137,14 @@ object OutfitMemberEvent extends Marshallable[OutfitMemberEvent] {
("member_id" | uint32L) :: ("member_id" | uint32L) ::
("action" | selectFromType(packet_type.id)).hlist ("action" | selectFromType(packet_type.id)).hlist
} }
).xmap[OutfitMemberEvent]( ).xmap[OutfitMemberEvent](
{ {
case packet_type :: outfit_id :: member_id:: action :: HNil => case _ :: outfit_id :: member_id:: action :: HNil =>
OutfitMemberEvent(packet_type, outfit_id, member_id, action) OutfitMemberEvent(outfit_id, member_id, action)
}, },
{ {
case OutfitMemberEvent(packet_type, outfit_id, member_id, action) => case OutfitMemberEvent(outfit_id, member_id, action) =>
packet_type :: outfit_id :: member_id :: action :: HNil OutfitMemberEvent.PacketType(action.code) :: outfit_id :: member_id :: action :: HNil
} }
) )
} }

View file

@ -7,10 +7,10 @@ import scodec.codecs._
import shapeless.{::, HNil} import shapeless.{::, HNil}
final case class OutfitMemberUpdate( final case class OutfitMemberUpdate(
outfit_guid: Long, outfit_id: Long,
char_id: Long, char_id: Long,
rank: Int, // 0-7 rank: Int, // 0-7
unk1: Int, flag: Boolean,
) extends PlanetSideGamePacket { ) extends PlanetSideGamePacket {
type Packet = OutfitMemberUpdate type Packet = OutfitMemberUpdate
def opcode = GamePacketOpcode.OutfitMemberUpdate def opcode = GamePacketOpcode.OutfitMemberUpdate
@ -19,18 +19,18 @@ final case class OutfitMemberUpdate(
object OutfitMemberUpdate extends Marshallable[OutfitMemberUpdate] { object OutfitMemberUpdate extends Marshallable[OutfitMemberUpdate] {
implicit val codec: Codec[OutfitMemberUpdate] = ( implicit val codec: Codec[OutfitMemberUpdate] = (
("outfit_guid" | uint32L) :: ("outfit_id" | uint32L) ::
("char_id" | uint32L) :: ("char_id" | uint32L) ::
("rank" | uint(3)) :: ("rank" | uint(3)) ::
("unk1" | uint(5)) ("flag" | bool)
).xmap[OutfitMemberUpdate]( ).xmap[OutfitMemberUpdate](
{ {
case outfit_guid :: char_id :: rank :: u1 :: HNil => case outfit_id :: char_id :: rank :: flag :: HNil =>
OutfitMemberUpdate(outfit_guid, char_id, rank, u1) OutfitMemberUpdate(outfit_id, char_id, rank, flag)
}, },
{ {
case OutfitMemberUpdate(outfit_guid, char_id, rank, u1) => case OutfitMemberUpdate(outfit_id, char_id, rank, flag) =>
outfit_guid :: char_id :: rank :: u1 :: HNil outfit_id :: char_id :: rank :: flag :: HNil
} }
) )
} }

View file

@ -9,7 +9,6 @@ import scodec.codecs._
import shapeless.{::, HNil} import shapeless.{::, HNil}
final case class OutfitMembershipRequest( final case class OutfitMembershipRequest(
request_type: OutfitMembershipRequest.RequestType.Type,
outfit_id: Long, outfit_id: Long,
action: OutfitMembershipRequestAction action: OutfitMembershipRequestAction
) extends PlanetSideGamePacket { ) extends PlanetSideGamePacket {
@ -61,8 +60,8 @@ object OutfitMembershipRequestAction {
) extends OutfitMembershipRequestAction(code = 6) ) extends OutfitMembershipRequestAction(code = 6)
final case class SetRank( final case class SetRank(
avatar_id: Long, // 32 avatar_id: Long,
rank: Int, // 3 rank: Int,
member_name: String, member_name: String,
) extends OutfitMembershipRequestAction(code = 7) ) extends OutfitMembershipRequestAction(code = 7)
@ -75,120 +74,116 @@ object OutfitMembershipRequestAction {
object Codecs { object Codecs {
private val everFailCondition = conditional(included = false, bool) private val everFailCondition = conditional(included = false, bool)
val CreateCodec: Codec[Create] = val CreateCodec: Codec[Create] = (
( PacketHelpers.encodedWideStringAligned(5) ::
PacketHelpers.encodedWideStringAligned(5) :: PacketHelpers.encodedWideString
PacketHelpers.encodedWideString ).xmap[Create](
).xmap[Create]( {
{ case u1 :: outfit_name :: HNil =>
case u1 :: outfit_name :: HNil => Create(u1, outfit_name)
Create(u1, outfit_name) },
}, {
{ case Create(u1, outfit_name) =>
case Create(u1, outfit_name) => u1 :: outfit_name :: HNil
u1 :: outfit_name :: HNil }
} )
)
val FormCodec: Codec[Form] = val FormCodec: Codec[Form] = (
( PacketHelpers.encodedWideStringAligned(5) ::
PacketHelpers.encodedWideStringAligned(5) :: PacketHelpers.encodedWideString
PacketHelpers.encodedWideString ).xmap[Form](
).xmap[Form]( {
{ case u1 :: outfit_name :: HNil =>
case u1 :: outfit_name :: HNil => Form(u1, outfit_name)
Form(u1, outfit_name) },
}, {
{ case Form(u1, outfit_name) =>
case Form(u1, outfit_name) => u1 :: outfit_name :: HNil
u1 :: outfit_name :: HNil }
} )
)
val InviteCodec: Codec[Invite] = val InviteCodec: Codec[Invite] = (
( uint32L ::
uint32L :: PacketHelpers.encodedWideStringAligned(5)
PacketHelpers.encodedWideStringAligned(5) ).xmap[Invite](
).xmap[Invite]( {
{ case avatar_id :: member_name :: HNil =>
case u1 :: member_name :: HNil => Invite(avatar_id, member_name)
Invite(u1, member_name) },
}, {
{ case Invite(avatar_id, member_name) =>
case Invite(u1, member_name) => avatar_id :: member_name :: HNil
u1 :: member_name :: HNil }
} )
)
val AcceptInviteCodec: Codec[AcceptInvite] = val AcceptInviteCodec: Codec[AcceptInvite] = (
PacketHelpers.encodedWideString.xmap[AcceptInvite]( PacketHelpers.encodedWideString
{ ).xmap[AcceptInvite](
case u1 => {
AcceptInvite(u1) case member_name =>
}, AcceptInvite(member_name)
{ },
case AcceptInvite(u1) => {
u1 case AcceptInvite(member_name) =>
} member_name
) }
)
val RejectInviteCodec: Codec[RejectInvite] = val RejectInviteCodec: Codec[RejectInvite] = (
PacketHelpers.encodedWideString.xmap[RejectInvite]( PacketHelpers.encodedWideString
{ ).xmap[RejectInvite](
case u1 => {
RejectInvite(u1) case member_name =>
}, RejectInvite(member_name)
{ },
case RejectInvite(u1) => {
u1 case RejectInvite(member_name) =>
} member_name
) }
)
val CancelInviteCodec: Codec[CancelInvite] = val CancelInviteCodec: Codec[CancelInvite] = (
( uint32L ::
uint32L :: PacketHelpers.encodedWideStringAligned(5)
PacketHelpers.encodedWideStringAligned(5) ).xmap[CancelInvite](
).xmap[CancelInvite]( {
{ case avatar_id :: outfit_name :: HNil =>
case u1 :: outfit_name :: HNil => CancelInvite(avatar_id, outfit_name)
CancelInvite(u1, outfit_name) },
}, {
{ case CancelInvite(avatar_id, outfit_name) =>
case CancelInvite(u1, outfit_name) => avatar_id :: outfit_name :: HNil
u1 :: outfit_name :: HNil }
} )
)
val KickCodec: Codec[Kick] = val KickCodec: Codec[Kick] = (
( uint32L ::
uint32L :: PacketHelpers.encodedWideStringAligned(5)
PacketHelpers.encodedWideStringAligned(5) ).xmap[Kick](
).xmap[Kick]( {
{ case avatar_id :: member_name :: HNil =>
case u1 :: member_name :: HNil => Kick(avatar_id, member_name)
Kick(u1, member_name) },
}, {
{ case Kick(avatar_id, member_name) =>
case Kick(u1, member_name) => avatar_id :: member_name :: HNil
u1 :: member_name :: HNil }
} )
)
val SetRankCodec: Codec[SetRank] = val SetRankCodec: Codec[SetRank] = (
( uint32L ::
uint32L :: uintL(3) ::
uintL(3) :: PacketHelpers.encodedWideStringAligned(2)
PacketHelpers.encodedWideStringAligned(2) ).xmap[SetRank](
).xmap[SetRank]( {
{ case avatar_id :: rank :: member_name :: HNil =>
case u1 :: rank :: member_name :: HNil => SetRank(avatar_id, rank, member_name)
SetRank(u1, rank, member_name) },
}, {
{ case SetRank(avatar_id, rank, member_name) =>
case SetRank(u1, rank, member_name) => avatar_id :: rank :: member_name :: HNil
u1 :: rank :: member_name :: HNil }
} )
)
/** /**
@ -221,17 +216,17 @@ object OutfitMembershipRequestAction {
object OutfitMembershipRequest extends Marshallable[OutfitMembershipRequest] { object OutfitMembershipRequest extends Marshallable[OutfitMembershipRequest] {
object RequestType extends Enumeration { object PacketType extends Enumeration {
type Type = Value type Type = Value
val Create: RequestType.Value = Value(0) val Create: PacketType.Value = Value(0)
val Form: RequestType.Value = Value(1) val Form: PacketType.Value = Value(1)
val Invite: RequestType.Value = Value(2) val Invite: PacketType.Value = Value(2)
val Accept: RequestType.Value = Value(3) val Accept: PacketType.Value = Value(3)
val Reject: RequestType.Value = Value(4) val Reject: PacketType.Value = Value(4)
val Cancel: RequestType.Value = Value(5) val Cancel: PacketType.Value = Value(5)
val Kick: RequestType.Value = Value(6) val Kick: PacketType.Value = Value(6)
val SetRank: RequestType.Value = Value(7) val SetRank: PacketType.Value = Value(7)
implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uintL(3)) implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uintL(3))
} }
@ -255,18 +250,18 @@ object OutfitMembershipRequest extends Marshallable[OutfitMembershipRequest] {
} }
implicit val codec: Codec[OutfitMembershipRequest] = ( implicit val codec: Codec[OutfitMembershipRequest] = (
("request_type" | RequestType.codec) >>:~ { request_type => ("packet_type" | PacketType.codec) >>:~ { packet_type =>
("outfit_id" | uint32L) :: ("outfit_id" | uint32L) ::
("action" | selectFromType(request_type.id)) ("action" | selectFromType(packet_type.id))
} }
).xmap[OutfitMembershipRequest]( ).xmap[OutfitMembershipRequest](
{ {
case request_type :: outfit_id :: action :: HNil => case _ :: outfit_id :: action :: HNil =>
OutfitMembershipRequest(request_type, outfit_id, action) OutfitMembershipRequest(outfit_id, action)
}, },
{ {
case OutfitMembershipRequest(request_type, outfit_id, action) => case OutfitMembershipRequest(outfit_id, action) =>
request_type :: outfit_id :: action :: HNil OutfitMembershipRequest.PacketType(action.code) :: outfit_id :: action :: HNil
} }
) )
} }

View file

@ -43,22 +43,22 @@ object OutfitMembershipResponse extends Marshallable[OutfitMembershipResponse] {
} }
implicit val codec: Codec[OutfitMembershipResponse] = ( implicit val codec: Codec[OutfitMembershipResponse] = (
("response_type" | PacketType.codec) :: ("packet_type" | PacketType.codec) ::
("unk0" | uintL(5)) :: ("unk0" | uintL(5)) ::
("unk1" | uintL(3)) :: ("unk1" | uintL(3)) ::
("outfit_id" | uint32L) :: ("outfit_id" | uint32L) ::
("target_id" | uint32L) :: ("target_id" | uint32L) ::
("str1" | PacketHelpers.encodedWideStringAligned(5)) :: ("str1" | PacketHelpers.encodedWideStringAligned(5)) ::
("str2" | PacketHelpers.encodedWideString) :: ("str2" | PacketHelpers.encodedWideString) ::
("flag" | bool) ("flag" | bool)
).xmap[OutfitMembershipResponse]( ).xmap[OutfitMembershipResponse](
{ {
case response_type :: u0 :: u1 :: outfit_id :: target_id :: str1 :: str2 :: flag :: HNil => case packet_type :: u0 :: u1 :: outfit_id :: target_id :: str1 :: str2 :: flag :: HNil =>
OutfitMembershipResponse(response_type, u0, u1, outfit_id, target_id, str1, str2, flag) OutfitMembershipResponse(packet_type, u0, u1, outfit_id, target_id, str1, str2, flag)
}, },
{ {
case OutfitMembershipResponse(response_type, u0, u1, outfit_id, target_id, str1, str2, flag) => case OutfitMembershipResponse(packet_type, u0, u1, outfit_id, target_id, str1, str2, flag) =>
response_type :: u0 :: u1 :: outfit_id :: target_id :: str1 :: str2 :: flag :: HNil packet_type :: u0 :: u1 :: outfit_id :: target_id :: str1 :: str2 :: flag :: HNil
} }
) )
} }

View file

@ -37,16 +37,19 @@ object OutfitRequestAction {
* @param unk na * @param unk na
*/ */
final case class Unk2(unk: Int) extends OutfitRequestAction(code = 2) final case class Unk2(unk: Int) extends OutfitRequestAction(code = 2)
/** /**
* na * na
* @param unk na * @param unk na
*/ */
final case class Unk3(menuOpen: Boolean) extends OutfitRequestAction(code = 3) final case class Unk3(menuOpen: Boolean) extends OutfitRequestAction(code = 3)
/** /**
* na * na
* @param unk na * @param unk na
*/ */
final case class Unk4(menuOpen: Boolean) extends OutfitRequestAction(code = 4) final case class Unk4(menuOpen: Boolean) extends OutfitRequestAction(code = 4)
/** /**
* na * na
* @param unk na * @param unk na
@ -142,14 +145,14 @@ object OutfitRequest extends Marshallable[OutfitRequest] {
_ => Attempt.Failure(Err(s"can not encode $action-type info - no such thing")) _ => Attempt.Failure(Err(s"can not encode $action-type info - no such thing"))
) )
object RequestType extends Enumeration { object PacketType extends Enumeration {
type Type = Value type Type = Value
val Motd: RequestType.Value = Value(0) val Motd: PacketType.Value = Value(0)
val Rank: RequestType.Value = Value(1) val Rank: PacketType.Value = Value(1)
val Unk2: RequestType.Value = Value(2) val Unk2: PacketType.Value = Value(2)
val Detail: RequestType.Value = Value(3) val Detail: PacketType.Value = Value(3)
val List: RequestType.Value = Value(4) // sent by client if menu is either open (true) or closed (false) val List: PacketType.Value = Value(4) // sent by client if menu is either open (true) or closed (false)
implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uintL(3)) implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uintL(3))
} }
@ -169,18 +172,18 @@ object OutfitRequest extends Marshallable[OutfitRequest] {
} }
implicit val codec: Codec[OutfitRequest] = ( implicit val codec: Codec[OutfitRequest] = (
uint(bits = 3) >>:~ { code => ("packet_type" | PacketType.codec) >>:~ { packet_type =>
("id" | uint32L) :: ("id" | uint32L) ::
("action" | selectFromType(code)) ("action" | selectFromType(packet_type.id)).hlist
} }
).xmap[OutfitRequest]( ).xmap[OutfitRequest](
{ {
case _ :: id:: action :: HNil => case _ :: id:: action :: HNil =>
OutfitRequest(id, action) OutfitRequest(id, action)
}, },
{ {
case OutfitRequest(id, action) => case OutfitRequest(id, action) =>
action.code :: id :: action :: HNil OutfitRequest.PacketType(action.code) :: id :: action :: HNil
} }
) )
} }

View file

@ -2,13 +2,13 @@
package game package game
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game.OutfitEvent.RequestType import net.psforever.packet.game.OutfitEvent
import net.psforever.packet.game.OutfitEventAction._ import net.psforever.packet.game.OutfitEventAction._
import net.psforever.packet.game._
import org.specs2.mutable._ import org.specs2.mutable._
import scodec.bits._ import scodec.bits._
class OutfitEventTest extends Specification { class OutfitEventTest extends Specification {
val unk0_ABC: ByteVector = ByteVector.fromValidHex( val unk0_ABC: ByteVector = ByteVector.fromValidHex(
"8f 1 a8c2 0001" + // packet head "8f 1 a8c2 0001" + // packet head
"2a 0 42006c00610063006b002000410072006d006f0072006500640020005200650061007000650072007300" + // Black Armored Reapers "2a 0 42006c00610063006b002000410072006d006f0072006500640020005200650061007000650072007300" + // Black Armored Reapers
@ -51,8 +51,7 @@ class OutfitEventTest extends Specification {
"decode Unk0 ABC" in { "decode Unk0 ABC" in {
PacketCoding.decodePacket(unk0_ABC).require match { PacketCoding.decodePacket(unk0_ABC).require match {
case OutfitEvent(request_type, outfit_guid, action) => case OutfitEvent(outfit_guid, action) =>
request_type mustEqual RequestType.Unk0
outfit_guid mustEqual 25044 outfit_guid mustEqual 25044
action mustEqual Unk0( action mustEqual Unk0(
OutfitInfo( OutfitInfo(
@ -78,7 +77,6 @@ class OutfitEventTest extends Specification {
"encode Unk0 ABC" in { "encode Unk0 ABC" in {
val msg = OutfitEvent( val msg = OutfitEvent(
RequestType.Unk0,
25044, 25044,
Unk0( Unk0(
OutfitInfo( OutfitInfo(
@ -105,8 +103,7 @@ class OutfitEventTest extends Specification {
"decode Unk1 ABC" in { "decode Unk1 ABC" in {
PacketCoding.decodePacket(unk1_ABC).require match { PacketCoding.decodePacket(unk1_ABC).require match {
case OutfitEvent(request_type, outfit_guid, action) => case OutfitEvent(outfit_guid, action) =>
request_type mustEqual RequestType.Unk1
outfit_guid mustEqual 529688L outfit_guid mustEqual 529688L
action mustEqual Unk1() action mustEqual Unk1()
case _ => case _ =>
@ -116,7 +113,6 @@ class OutfitEventTest extends Specification {
"encode Unk1 ABC" in { "encode Unk1 ABC" in {
val msg = OutfitEvent( val msg = OutfitEvent(
RequestType.Unk1,
529688L, 529688L,
Unk1() Unk1()
) )
@ -127,8 +123,7 @@ class OutfitEventTest extends Specification {
"decode Unk2 ABC" in { "decode Unk2 ABC" in {
PacketCoding.decodePacket(unk2_ABC).require match { PacketCoding.decodePacket(unk2_ABC).require match {
case OutfitEvent(request_type, outfit_guid, action) => case OutfitEvent(outfit_guid, action) =>
request_type mustEqual RequestType.Unk2
outfit_guid mustEqual 2147418113L outfit_guid mustEqual 2147418113L
action mustEqual Unk2(OutfitInfo( action mustEqual Unk2(OutfitInfo(
outfit_name = "PlanetSide_Forever_Vanu", outfit_name = "PlanetSide_Forever_Vanu",
@ -152,7 +147,6 @@ class OutfitEventTest extends Specification {
"encode Unk2 ABC" in { "encode Unk2 ABC" in {
val msg = OutfitEvent( val msg = OutfitEvent(
RequestType.Unk2,
2147418113L, 2147418113L,
Unk2( Unk2(
OutfitInfo( OutfitInfo(
@ -179,8 +173,7 @@ class OutfitEventTest extends Specification {
"decode Unk3 ABC" in { "decode Unk3 ABC" in {
PacketCoding.decodePacket(unk3_ABC).require match { PacketCoding.decodePacket(unk3_ABC).require match {
case OutfitEvent(request_type, outfit_guid, action) => case OutfitEvent(outfit_guid, action) =>
request_type mustEqual RequestType.Unk3
outfit_guid mustEqual 2147418113L outfit_guid mustEqual 2147418113L
action mustEqual Unk3() action mustEqual Unk3()
case _ => case _ =>
@ -190,7 +183,6 @@ class OutfitEventTest extends Specification {
"encode Unk3 ABC" in { "encode Unk3 ABC" in {
val msg = OutfitEvent( val msg = OutfitEvent(
RequestType.Unk3,
2147418113L, 2147418113L,
Unk3() Unk3()
) )
@ -201,8 +193,7 @@ class OutfitEventTest extends Specification {
"decode Unk4 ABC" in { "decode Unk4 ABC" in {
PacketCoding.decodePacket(unk4_ABC).require match { PacketCoding.decodePacket(unk4_ABC).require match {
case OutfitEvent(request_type, outfit_guid, action) => case OutfitEvent(outfit_guid, action) =>
request_type mustEqual RequestType.UpdateOutfitId
outfit_guid mustEqual 2147418113L outfit_guid mustEqual 2147418113L
action mustEqual UpdateOutfitId( action mustEqual UpdateOutfitId(
new_outfit_id = 529744L, new_outfit_id = 529744L,
@ -214,7 +205,6 @@ class OutfitEventTest extends Specification {
"encode Unk4 ABC" in { "encode Unk4 ABC" in {
val msg = OutfitEvent( val msg = OutfitEvent(
RequestType.UpdateOutfitId,
2147418113L, 2147418113L,
UpdateOutfitId( UpdateOutfitId(
new_outfit_id = 529744L, new_outfit_id = 529744L,
@ -227,8 +217,7 @@ class OutfitEventTest extends Specification {
"decode Unk5 ABC" in { "decode Unk5 ABC" in {
PacketCoding.decodePacket(unk5_ABC).require match { PacketCoding.decodePacket(unk5_ABC).require match {
case OutfitEvent(request_type, outfit_guid, action) => case OutfitEvent(outfit_guid, action) =>
request_type mustEqual RequestType.Unk5
outfit_guid mustEqual 2147418113L outfit_guid mustEqual 2147418113L
action mustEqual Unk5( action mustEqual Unk5(
unk1 = 2, unk1 = 2,
@ -240,7 +229,6 @@ class OutfitEventTest extends Specification {
"encode Unk5 ABC" in { "encode Unk5 ABC" in {
val msg = OutfitEvent( val msg = OutfitEvent(
RequestType.Unk5,
2147418113L, 2147418113L,
Unk5( Unk5(
unk1 = 2, unk1 = 2,

View file

@ -3,12 +3,12 @@ package game
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game.OutfitListEvent import net.psforever.packet.game.OutfitListEvent
import net.psforever.packet.game.OutfitListEvent.RequestType
import net.psforever.packet.game.OutfitListEventAction.ListElementOutfit import net.psforever.packet.game.OutfitListEventAction.ListElementOutfit
import org.specs2.mutable._ import org.specs2.mutable._
import scodec.bits.ByteVector import scodec.bits.ByteVector
class OutfitListEventTest extends Specification { class OutfitListEventTest extends Specification {
val unk2_0_ABC: ByteVector = ByteVector.fromValidHex("98 5 e83a0000 000e1800 0800000 11404e0069006700680074004c006f00720064007300 854e005900430061007400") val unk2_0_ABC: ByteVector = ByteVector.fromValidHex("98 5 e83a0000 000e1800 0800000 11404e0069006700680074004c006f00720064007300 854e005900430061007400")
val unk2_0_DEF: ByteVector = ByteVector.fromValidHex("98 4 ec281001 51a62800 3400000 11a0490052004f004e004600490053005400200043006c0061006e00 8654006f006c006a00") val unk2_0_DEF: ByteVector = ByteVector.fromValidHex("98 4 ec281001 51a62800 3400000 11a0490052004f004e004600490053005400200043006c0061006e00 8654006f006c006a00")
val unk2_1_ABC: ByteVector = ByteVector.fromValidHex("98 4 723c0000 2aa81e00 2200000 11006900470061006d00650073002d004500 906900670061006d006500730043005400460057006800610063006b002d004500") val unk2_1_ABC: ByteVector = ByteVector.fromValidHex("98 4 723c0000 2aa81e00 2200000 11006900470061006d00650073002d004500 906900670061006d006500730043005400460057006800610063006b002d004500")
@ -21,8 +21,7 @@ class OutfitListEventTest extends Specification {
"decode unk0_ABC" in { "decode unk0_ABC" in {
PacketCoding.decodePacket(unk2_0_ABC).require match { PacketCoding.decodePacket(unk2_0_ABC).require match {
case OutfitListEvent(code, ListElementOutfit(unk1, points, members, outfit_name, outfit_leader)) => case OutfitListEvent(ListElementOutfit(unk1, points, members, outfit_name, outfit_leader)) =>
code mustEqual OutfitListEvent.RequestType.ListElementOutfit
unk1 mustEqual 7668 unk1 mustEqual 7668
points mustEqual 788224 points mustEqual 788224
members mustEqual 4 members mustEqual 4
@ -34,7 +33,15 @@ class OutfitListEventTest extends Specification {
} }
"encode unk0_ABC" in { "encode unk0_ABC" in {
val msg = OutfitListEvent(RequestType.ListElementOutfit, ListElementOutfit(7668, 788224, 4, "NightLords", "NYCat")) val msg = OutfitListEvent(
ListElementOutfit(
7668,
788224,
4,
"NightLords",
"NYCat"
)
)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual unk2_0_ABC pkt mustEqual unk2_0_ABC

View file

@ -22,8 +22,7 @@ class OutfitMemberEventTest extends Specification {
"decode Lazer padding" in { "decode Lazer padding" in {
PacketCoding.decodePacket(Lazer).require match { PacketCoding.decodePacket(Lazer).require match {
case OutfitMemberEvent(packet_type, outfit_id, member_id, Unk0(member_name, rank, points, last_login, action, padding)) => case OutfitMemberEvent(outfit_id, member_id, Unk0(member_name, rank, points, last_login, action, padding)) =>
packet_type mustEqual OutfitMemberEvent.PacketType.Unk0
outfit_id mustEqual 6418 outfit_id mustEqual 6418
member_id mustEqual 705344 member_id mustEqual 705344
member_name mustEqual "Lazer1982" member_name mustEqual "Lazer1982"
@ -39,14 +38,13 @@ class OutfitMemberEventTest extends Specification {
"encode Lazer padding" in { "encode Lazer padding" in {
val msg = OutfitMemberEvent( val msg = OutfitMemberEvent(
packet_type = OutfitMemberEvent.PacketType.Unk0,
outfit_id = 6418, outfit_id = 6418,
member_id = 705344, member_id = 705344,
Unk0( Unk0(
member_name = "Lazer1982", member_name = "Lazer1982",
rank = 7, rank = 7,
points = 3134113, points = 3134113,
last_login = 156506, last_online = 156506,
action = OutfitMemberEventAction.PacketType.Padding, action = OutfitMemberEventAction.PacketType.Padding,
padding = 0 padding = 0
) )
@ -58,8 +56,7 @@ class OutfitMemberEventTest extends Specification {
"decode OpolE padding" in { "decode OpolE padding" in {
PacketCoding.decodePacket(OpolE).require match { PacketCoding.decodePacket(OpolE).require match {
case OutfitMemberEvent(packet_type, outfit_id, member_id, Unk0(member_name, rank, points, last_login, action, unk0_padding)) => case OutfitMemberEvent(outfit_id, member_id, Unk0(member_name, rank, points, last_login, action, unk0_padding)) =>
packet_type mustEqual OutfitMemberEvent.PacketType.Unk0
outfit_id mustEqual 6418 outfit_id mustEqual 6418
member_id mustEqual 42644970 member_id mustEqual 42644970
member_name mustEqual "OpolE" member_name mustEqual "OpolE"
@ -75,14 +72,13 @@ class OutfitMemberEventTest extends Specification {
"encode OpolE padding" in { "encode OpolE padding" in {
val msg = OutfitMemberEvent( val msg = OutfitMemberEvent(
packet_type = OutfitMemberEvent.PacketType.Unk0,
outfit_id = 6418, outfit_id = 6418,
member_id = 42644970, member_id = 42644970,
Unk0( Unk0(
member_name = "OpolE", member_name = "OpolE",
rank = 6, rank = 6,
points = 461901, points = 461901,
last_login = 137576, last_online = 137576,
action = OutfitMemberEventAction.PacketType.Padding, action = OutfitMemberEventAction.PacketType.Padding,
padding = 0 padding = 0
) )
@ -95,8 +91,7 @@ class OutfitMemberEventTest extends Specification {
"decode Unk1" in { "decode Unk1" in {
PacketCoding.decodePacket(unk1).require match { PacketCoding.decodePacket(unk1).require match {
case OutfitMemberEvent(packet_type,outfit_id, member_id, Unk1()) => case OutfitMemberEvent(outfit_id, member_id, Unk1()) =>
packet_type mustEqual OutfitMemberEvent.PacketType.Unk1
outfit_id mustEqual 529744 outfit_id mustEqual 529744
member_id mustEqual 41605263 member_id mustEqual 41605263
case _ => case _ =>
@ -106,7 +101,6 @@ class OutfitMemberEventTest extends Specification {
"encode Unk1" in { "encode Unk1" in {
val msg = OutfitMemberEvent( val msg = OutfitMemberEvent(
packet_type = OutfitMemberEvent.PacketType.Unk1,
outfit_id = 529744, outfit_id = 529744,
member_id = 41605263, member_id = 41605263,
Unk1() Unk1()

View file

@ -0,0 +1,51 @@
// Copyright (c) 2023-2025 PSForever
package game
import net.psforever.packet._
import net.psforever.packet.game.OutfitMemberUpdate
import org.specs2.mutable._
import scodec.bits._
class OutfitMemberUpdateTest extends Specification {
val updateRankToOwnerOfOutfitInFormation = hex"91 0100ff7f15aa7a02f0"
val normalRankChange = hex"91 1219000086d9130090"
"decode updateOwnerOfOutfitInFormation" in {
PacketCoding.decodePacket(updateRankToOwnerOfOutfitInFormation).require match {
case OutfitMemberUpdate(outfit_id, char_id, rank, flag) =>
outfit_id mustEqual 2147418113
char_id mustEqual 41593365
rank mustEqual 7
flag mustEqual true
case _ =>
ko
}
}
"encode updateOwnerOfOutfitInFormation" in {
val msg = OutfitMemberUpdate(outfit_id = 2147418113, char_id = 41593365, rank = 7, flag = true)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual updateRankToOwnerOfOutfitInFormation
}
"decode normalRankChange" in {
PacketCoding.decodePacket(normalRankChange).require match {
case OutfitMemberUpdate(outfit_id, char_id, rank, flag) =>
outfit_id mustEqual 6418
char_id mustEqual 1300870
rank mustEqual 4
flag mustEqual true
case _ =>
ko
}
}
"encode normalRankChange" in {
val msg = OutfitMemberUpdate(outfit_id = 6418, char_id = 1300870, rank = 4, flag = true)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual normalRankChange
}
}

View file

@ -2,13 +2,13 @@
package game package game
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game._ import net.psforever.packet.game.OutfitMembershipRequest
import net.psforever.packet.game.OutfitMembershipRequest.RequestType
import net.psforever.packet.game.OutfitMembershipRequestAction._ import net.psforever.packet.game.OutfitMembershipRequestAction._
import org.specs2.mutable._ import org.specs2.mutable._
import scodec.bits._ import scodec.bits._
class OutfitMembershipRequestTest extends Specification { class OutfitMembershipRequestTest extends Specification {
val create_ABC = hex"8c 0 0200 000 1000 83 410042004300" val create_ABC = hex"8c 0 0200 000 1000 83 410042004300"
val create_2222 = hex"8c 0 1000 000 1000 84 3200320032003200" val create_2222 = hex"8c 0 1000 000 1000 84 3200320032003200"
val form_abc = hex"8c 2 0200 000 1000 83 610062006300" val form_abc = hex"8c 2 0200 000 1000 83 610062006300"
@ -30,8 +30,8 @@ class OutfitMembershipRequestTest extends Specification {
"decode CreateOutfit ABC" in { "decode CreateOutfit ABC" in {
PacketCoding.decodePacket(create_ABC).require match { PacketCoding.decodePacket(create_ABC).require match {
case OutfitMembershipRequest(request_type, outfit_id, action) => case OutfitMembershipRequest(outfit_id, action) =>
request_type mustEqual RequestType.Create
outfit_id mustEqual 1 outfit_id mustEqual 1
action mustEqual Create("", "ABC") action mustEqual Create("", "ABC")
case _ => case _ =>
@ -40,7 +40,7 @@ class OutfitMembershipRequestTest extends Specification {
} }
"encode CreateOutfit ABC" in { "encode CreateOutfit ABC" in {
val msg = OutfitMembershipRequest(RequestType.Create, 1, Create("", "ABC")) val msg = OutfitMembershipRequest(1, Create("", "ABC"))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual create_ABC pkt mustEqual create_ABC
@ -48,8 +48,7 @@ class OutfitMembershipRequestTest extends Specification {
"decode CreateOutfit 2222" in { "decode CreateOutfit 2222" in {
PacketCoding.decodePacket(create_2222).require match { PacketCoding.decodePacket(create_2222).require match {
case OutfitMembershipRequest(request_type, outfit_id, action) => case OutfitMembershipRequest(outfit_id, action) =>
request_type mustEqual RequestType.Create
outfit_id mustEqual 8 outfit_id mustEqual 8
action mustEqual Create("", "2222") action mustEqual Create("", "2222")
case _ => case _ =>
@ -58,7 +57,7 @@ class OutfitMembershipRequestTest extends Specification {
} }
"encode CreateOutfit 2222" in { "encode CreateOutfit 2222" in {
val msg = OutfitMembershipRequest(RequestType.Create, 8, Create("", "2222")) val msg = OutfitMembershipRequest(8, Create("", "2222"))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual create_2222 pkt mustEqual create_2222
@ -66,8 +65,7 @@ class OutfitMembershipRequestTest extends Specification {
"decode FormOutfit abc" in { "decode FormOutfit abc" in {
PacketCoding.decodePacket(form_abc).require match { PacketCoding.decodePacket(form_abc).require match {
case OutfitMembershipRequest(request_type, outfit_id, action) => case OutfitMembershipRequest(outfit_id, action) =>
request_type mustEqual RequestType.Form
outfit_id mustEqual 1 outfit_id mustEqual 1
action mustEqual Form("", "abc") action mustEqual Form("", "abc")
case _ => case _ =>
@ -76,7 +74,7 @@ class OutfitMembershipRequestTest extends Specification {
} }
"encode FormOutfit abc" in { "encode FormOutfit abc" in {
val msg = OutfitMembershipRequest(RequestType.Form, 1, Form("", "abc")) val msg = OutfitMembershipRequest(1, Form("", "abc"))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual form_abc pkt mustEqual form_abc
@ -84,8 +82,7 @@ class OutfitMembershipRequestTest extends Specification {
"decode FormOutfit 1" in { "decode FormOutfit 1" in {
PacketCoding.decodePacket(form_1).require match { PacketCoding.decodePacket(form_1).require match {
case OutfitMembershipRequest(request_type, outfit_id, action) => case OutfitMembershipRequest(outfit_id, action) =>
request_type mustEqual RequestType.Form
outfit_id mustEqual 8 outfit_id mustEqual 8
action mustEqual Form("", "1") action mustEqual Form("", "1")
case _ => case _ =>
@ -94,7 +91,7 @@ class OutfitMembershipRequestTest extends Specification {
} }
"encode FormOutfit 1" in { "encode FormOutfit 1" in {
val msg = OutfitMembershipRequest(RequestType.Form, 8, Form("", "1")) val msg = OutfitMembershipRequest(8, Form("", "1"))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual form_1 pkt mustEqual form_1
@ -102,8 +99,7 @@ class OutfitMembershipRequestTest extends Specification {
"decode Invite" in { "decode Invite" in {
PacketCoding.decodePacket(invite_old).require match { PacketCoding.decodePacket(invite_old).require match {
case OutfitMembershipRequest(request_type, outfit_id, action) => case OutfitMembershipRequest(outfit_id, action) =>
request_type mustEqual RequestType.Invite
outfit_id mustEqual 30383325L outfit_id mustEqual 30383325L
action mustEqual Invite(0, "virusgiver") action mustEqual Invite(0, "virusgiver")
case _ => case _ =>
@ -112,7 +108,7 @@ class OutfitMembershipRequestTest extends Specification {
} }
"encode Invite" in { "encode Invite" in {
val msg = OutfitMembershipRequest(RequestType.Invite, 30383325L, Invite(0, "virusgiver")) val msg = OutfitMembershipRequest(30383325L, Invite(0, "virusgiver"))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual invite_old pkt mustEqual invite_old
@ -120,8 +116,7 @@ class OutfitMembershipRequestTest extends Specification {
"decode AcceptOutfitInvite 1" in { "decode AcceptOutfitInvite 1" in {
PacketCoding.decodePacket(accept_1).require match { PacketCoding.decodePacket(accept_1).require match {
case OutfitMembershipRequest(request_type, outfit_id, action) => case OutfitMembershipRequest(outfit_id, action) =>
request_type mustEqual RequestType.Accept
outfit_id mustEqual 1 outfit_id mustEqual 1
action mustEqual AcceptInvite("") action mustEqual AcceptInvite("")
case _ => case _ =>
@ -130,7 +125,7 @@ class OutfitMembershipRequestTest extends Specification {
} }
"encode AcceptOutfitInvite 1" in { "encode AcceptOutfitInvite 1" in {
val msg = OutfitMembershipRequest(RequestType.Accept, 1, AcceptInvite("")) val msg = OutfitMembershipRequest(1, AcceptInvite(""))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual accept_1 pkt mustEqual accept_1
@ -138,8 +133,7 @@ class OutfitMembershipRequestTest extends Specification {
"decode AcceptOutfitInvite 2" in { "decode AcceptOutfitInvite 2" in {
PacketCoding.decodePacket(accept_2).require match { PacketCoding.decodePacket(accept_2).require match {
case OutfitMembershipRequest(request_type, outfit_id, action) => case OutfitMembershipRequest(outfit_id, action) =>
request_type mustEqual RequestType.Accept
outfit_id mustEqual 2 outfit_id mustEqual 2
action mustEqual AcceptInvite("") action mustEqual AcceptInvite("")
case _ => case _ =>
@ -148,7 +142,7 @@ class OutfitMembershipRequestTest extends Specification {
} }
"encode AcceptOutfitInvite 2" in { "encode AcceptOutfitInvite 2" in {
val msg = OutfitMembershipRequest(RequestType.Accept, 2, AcceptInvite("")) val msg = OutfitMembershipRequest(2, AcceptInvite(""))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual accept_2 pkt mustEqual accept_2
@ -156,8 +150,7 @@ class OutfitMembershipRequestTest extends Specification {
"decode RejectOutfitInvite 1" in { "decode RejectOutfitInvite 1" in {
PacketCoding.decodePacket(reject_1).require match { PacketCoding.decodePacket(reject_1).require match {
case OutfitMembershipRequest(request_type, outfit_id, action) => case OutfitMembershipRequest(outfit_id, action) =>
request_type mustEqual RequestType.Reject
outfit_id mustEqual 1 outfit_id mustEqual 1
action mustEqual RejectInvite("") action mustEqual RejectInvite("")
case _ => case _ =>
@ -166,7 +159,7 @@ class OutfitMembershipRequestTest extends Specification {
} }
"encode RejectOutfitInvite 1" in { "encode RejectOutfitInvite 1" in {
val msg = OutfitMembershipRequest(RequestType.Reject, 1, RejectInvite("")) val msg = OutfitMembershipRequest(1, RejectInvite(""))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual reject_1 pkt mustEqual reject_1
@ -174,8 +167,7 @@ class OutfitMembershipRequestTest extends Specification {
"decode RejectOutfitInvite 2" in { "decode RejectOutfitInvite 2" in {
PacketCoding.decodePacket(reject_2).require match { PacketCoding.decodePacket(reject_2).require match {
case OutfitMembershipRequest(request_type, outfit_id, action) => case OutfitMembershipRequest(outfit_id, action) =>
request_type mustEqual RequestType.Reject
outfit_id mustEqual 2 outfit_id mustEqual 2
action mustEqual RejectInvite("") action mustEqual RejectInvite("")
case _ => case _ =>
@ -184,7 +176,7 @@ class OutfitMembershipRequestTest extends Specification {
} }
"encode RejectOutfitInvite 2" in { "encode RejectOutfitInvite 2" in {
val msg = OutfitMembershipRequest(RequestType.Reject, 2, RejectInvite("")) val msg = OutfitMembershipRequest(2, RejectInvite(""))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual reject_2 pkt mustEqual reject_2
@ -192,8 +184,7 @@ class OutfitMembershipRequestTest extends Specification {
"decode CancelOutfitInvite 3" in { "decode CancelOutfitInvite 3" in {
PacketCoding.decodePacket(cancel_3).require match { PacketCoding.decodePacket(cancel_3).require match {
case OutfitMembershipRequest(request_type, outfit_id, action) => case OutfitMembershipRequest(outfit_id, action) =>
request_type mustEqual RequestType.Cancel
outfit_id mustEqual 3 outfit_id mustEqual 3
action mustEqual CancelInvite(0, "") action mustEqual CancelInvite(0, "")
case _ => case _ =>
@ -202,7 +193,7 @@ class OutfitMembershipRequestTest extends Specification {
} }
"encode CancelOutfitInvite 3" in { "encode CancelOutfitInvite 3" in {
val msg = OutfitMembershipRequest(RequestType.Cancel, 3, CancelInvite(0, "")) val msg = OutfitMembershipRequest(3, CancelInvite(0, ""))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual cancel_3 pkt mustEqual cancel_3
@ -210,8 +201,7 @@ class OutfitMembershipRequestTest extends Specification {
"decode CancelOutfitInvite 1 abc" in { "decode CancelOutfitInvite 1 abc" in {
PacketCoding.decodePacket(cancel_1_abc).require match { PacketCoding.decodePacket(cancel_1_abc).require match {
case OutfitMembershipRequest(request_type, outfit_id, action) => case OutfitMembershipRequest(outfit_id, action) =>
request_type mustEqual RequestType.Cancel
outfit_id mustEqual 1 outfit_id mustEqual 1
action mustEqual CancelInvite(0, "abc") action mustEqual CancelInvite(0, "abc")
case _ => case _ =>
@ -220,7 +210,7 @@ class OutfitMembershipRequestTest extends Specification {
} }
"encode CancelOutfitInvite 1 abc" in { "encode CancelOutfitInvite 1 abc" in {
val msg = OutfitMembershipRequest(RequestType.Cancel, 1, CancelInvite(0, "abc")) val msg = OutfitMembershipRequest(1, CancelInvite(0, "abc"))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual cancel_1_abc pkt mustEqual cancel_1_abc
@ -228,8 +218,7 @@ class OutfitMembershipRequestTest extends Specification {
"decode CancelOutfitInvite 3 def" in { "decode CancelOutfitInvite 3 def" in {
PacketCoding.decodePacket(cancel_3_def).require match { PacketCoding.decodePacket(cancel_3_def).require match {
case OutfitMembershipRequest(request_type, outfit_id, action) => case OutfitMembershipRequest(outfit_id, action) =>
request_type mustEqual RequestType.Cancel
outfit_id mustEqual 3 outfit_id mustEqual 3
action mustEqual CancelInvite(0, "def") action mustEqual CancelInvite(0, "def")
case _ => case _ =>
@ -238,7 +227,7 @@ class OutfitMembershipRequestTest extends Specification {
} }
"encode CancelOutfitInvite 3 def" in { "encode CancelOutfitInvite 3 def" in {
val msg = OutfitMembershipRequest(RequestType.Cancel, 3, CancelInvite(0, "def")) val msg = OutfitMembershipRequest(3, CancelInvite(0, "def"))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual cancel_3_def pkt mustEqual cancel_3_def
@ -248,8 +237,7 @@ class OutfitMembershipRequestTest extends Specification {
"decode invite" in { "decode invite" in {
PacketCoding.decodePacket(invite).require match { PacketCoding.decodePacket(invite).require match {
case OutfitMembershipRequest(request_type, outfit_id, Invite(unk1, member_name)) => case OutfitMembershipRequest(outfit_id, Invite(unk1, member_name)) =>
request_type mustEqual RequestType.Invite
outfit_id mustEqual 1 outfit_id mustEqual 1
unk1 mustEqual 0 unk1 mustEqual 0
member_name mustEqual "inviteTest1" member_name mustEqual "inviteTest1"
@ -259,7 +247,7 @@ class OutfitMembershipRequestTest extends Specification {
} }
"encode invite" in { "encode invite" in {
val msg = OutfitMembershipRequest(RequestType.Invite, 1, Invite(0, "inviteTest1")) val msg = OutfitMembershipRequest(1, Invite(0, "inviteTest1"))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual invite pkt mustEqual invite
@ -268,8 +256,7 @@ class OutfitMembershipRequestTest extends Specification {
"decode kick" in { "decode kick" in {
PacketCoding.decodePacket(kick).require match { PacketCoding.decodePacket(kick).require match {
case OutfitMembershipRequest(request_type, outfit_id, Kick(avatar_id, member_name)) => case OutfitMembershipRequest(outfit_id, Kick(avatar_id, member_name)) =>
request_type mustEqual RequestType.Kick
outfit_id mustEqual 1 outfit_id mustEqual 1
avatar_id mustEqual 41575613 avatar_id mustEqual 41575613
member_name mustEqual "" member_name mustEqual ""
@ -279,7 +266,7 @@ class OutfitMembershipRequestTest extends Specification {
} }
"encode kick" in { "encode kick" in {
val msg = OutfitMembershipRequest(RequestType.Kick, 1, Kick(41575613, "")) val msg = OutfitMembershipRequest(1, Kick(41575613, ""))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual kick pkt mustEqual kick
@ -287,8 +274,7 @@ class OutfitMembershipRequestTest extends Specification {
"decode setrank" in { "decode setrank" in {
PacketCoding.decodePacket(setrank).require match { PacketCoding.decodePacket(setrank).require match {
case OutfitMembershipRequest(request_type, outfit_id, SetRank(avatar_id, rank, member_name)) => case OutfitMembershipRequest(outfit_id, SetRank(avatar_id, rank, member_name)) =>
request_type mustEqual RequestType.SetRank
outfit_id mustEqual 1 outfit_id mustEqual 1
avatar_id mustEqual 41575613 avatar_id mustEqual 41575613
rank mustEqual 1 rank mustEqual 1
@ -299,7 +285,7 @@ class OutfitMembershipRequestTest extends Specification {
} }
"encode setrank" in { "encode setrank" in {
val msg = OutfitMembershipRequest(RequestType.SetRank, 1, SetRank(41575613, 1, "")) val msg = OutfitMembershipRequest(1, SetRank(41575613, 1, ""))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual setrank pkt mustEqual setrank

View file

@ -2,8 +2,8 @@
package game package game
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game.OutfitMembershipResponse
import net.psforever.packet.game.OutfitMembershipResponse.PacketType import net.psforever.packet.game.OutfitMembershipResponse.PacketType
import net.psforever.packet.game._
import org.specs2.mutable._ import org.specs2.mutable._
import scodec.bits._ import scodec.bits._

View file

@ -3,17 +3,18 @@ package game
import org.specs2.mutable._ import org.specs2.mutable._
import net.psforever.packet._ import net.psforever.packet._
import net.psforever.packet.game._ import net.psforever.packet.game.{OutfitRequest, OutfitRequestAction}
import scodec.bits._ import scodec.bits._
class OutfitRequestTest extends Specification { class OutfitRequestTest extends Specification {
val setMotd = hex"8e 02b54f40401780560061006e00750020006f0075007400660069007400200066006f0072002000740068006500200070006c0061006e00650074007300690064006500200066006f00720065007600650072002000700072006f006a006500630074002100200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002d00660069006e00640020006f007500740020006d006f00720065002000610062006f0075007400200074006800650020005000530045004d0055002000700072006f006a0065006300740020006100740020005000530066006f00720065007600650072002e006e0065007400" val setMotd = hex"8e 02b54f40401780560061006e00750020006f0075007400660069007400200066006f0072002000740068006500200070006c0061006e00650074007300690064006500200066006f00720065007600650072002000700072006f006a006500630074002100200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002d00660069006e00640020006f007500740020006d006f00720065002000610062006f0075007400200074006800650020005000530045004d0055002000700072006f006a0065006300740020006100740020005000530066006f00720065007600650072002e006e0065007400"
val setRanks = hex"8e 22b54f405800c000c000c000c000c000c000c000" val setRanks = hex"8e 22b54f405800c000c000c000c000c000c000c000"
val string4 = hex"8e 42b54f404aa0" //faked by modifying the previous example val string4 = hex"8e 42b54f404aa0" //faked by modifying the previous example
val string6 = hex"8e 649e822010" val string6 = hex"8e 649e822010"
val string8 = hex"8e 81b2cf4050" val string8 = hex"8e 81b2cf4050"
"decode 0" in { "decode Motd" in {
PacketCoding.decodePacket(setMotd).require match { PacketCoding.decodePacket(setMotd).require match {
case OutfitRequest(id, OutfitRequestAction.Motd(str)) => case OutfitRequest(id, OutfitRequestAction.Motd(str)) =>
id mustEqual 41593365L id mustEqual 41593365L
@ -23,7 +24,7 @@ class OutfitRequestTest extends Specification {
} }
} }
"decode 1" in { "decode Ranks" in {
PacketCoding.decodePacket(setRanks).require match { PacketCoding.decodePacket(setRanks).require match {
case OutfitRequest(id, OutfitRequestAction.Ranks(list)) => case OutfitRequest(id, OutfitRequestAction.Ranks(list)) =>
id mustEqual 41593365L id mustEqual 41593365L
@ -33,7 +34,7 @@ class OutfitRequestTest extends Specification {
} }
} }
"decode 2 (fake)" in { "decode Unk2 (fake)" in {
PacketCoding.decodePacket(string4).require match { PacketCoding.decodePacket(string4).require match {
case OutfitRequest(id, OutfitRequestAction.Unk2(value)) => case OutfitRequest(id, OutfitRequestAction.Unk2(value)) =>
id mustEqual 41593365L id mustEqual 41593365L
@ -43,7 +44,7 @@ class OutfitRequestTest extends Specification {
} }
} }
"decode 3" in { "decode Unk3" in {
PacketCoding.decodePacket(string6).require match { PacketCoding.decodePacket(string6).require match {
case OutfitRequest(id, OutfitRequestAction.Unk3(value)) => case OutfitRequest(id, OutfitRequestAction.Unk3(value)) =>
id mustEqual 1176612L id mustEqual 1176612L
@ -53,7 +54,7 @@ class OutfitRequestTest extends Specification {
} }
} }
"decode 4" in { "decode Unk4" in {
PacketCoding.decodePacket(string8).require match { PacketCoding.decodePacket(string8).require match {
case OutfitRequest(id, OutfitRequestAction.Unk4(value)) => case OutfitRequest(id, OutfitRequestAction.Unk4(value)) =>
id mustEqual 41588237L id mustEqual 41588237L
@ -63,7 +64,7 @@ class OutfitRequestTest extends Specification {
} }
} }
"encode 0" in { "encode Motd" in {
val msg = OutfitRequest(41593365L, OutfitRequestAction.Motd( val msg = OutfitRequest(41593365L, OutfitRequestAction.Motd(
"Vanu outfit for the planetside forever project! -find out more about the PSEMU project at PSforever.net" "Vanu outfit for the planetside forever project! -find out more about the PSEMU project at PSforever.net"
)) ))
@ -72,28 +73,28 @@ class OutfitRequestTest extends Specification {
pkt mustEqual setMotd pkt mustEqual setMotd
} }
"encode 1" in { "encode Ranks" in {
val msg = OutfitRequest(41593365L, OutfitRequestAction.Ranks(List(Some(""), Some(""), Some(""), Some(""), Some(""), Some(""), Some(""), Some("")))) val msg = OutfitRequest(41593365L, OutfitRequestAction.Ranks(List(Some(""), Some(""), Some(""), Some(""), Some(""), Some(""), Some(""), Some(""))))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual setRanks pkt mustEqual setRanks
} }
"encode 2 (fake)" in { "encode Unk2 (fake)" in {
val msg = OutfitRequest(41593365L, OutfitRequestAction.Unk2(85)) val msg = OutfitRequest(41593365L, OutfitRequestAction.Unk2(85))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual string4 pkt mustEqual string4
} }
"encode 3" in { "encode Unk3" in {
val msg = OutfitRequest(1176612L, OutfitRequestAction.Unk3(true)) val msg = OutfitRequest(1176612L, OutfitRequestAction.Unk3(true))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual string6 pkt mustEqual string6
} }
"encode 4" in { "encode Unk4" in {
val msg = OutfitRequest(41588237L, OutfitRequestAction.Unk4(true)) val msg = OutfitRequest(41588237L, OutfitRequestAction.Unk4(true))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector