OutfitMembershipRequest all packets known!

OutfitMembershipResponse decoded, needs rework
OutfitMemberEvent decoded, needs rework
Tests reworked
This commit is contained in:
Resaec 2025-08-21 02:23:32 +02:00
parent b070834a8a
commit 17682c08d6
9 changed files with 393 additions and 287 deletions

View file

@ -3,7 +3,6 @@ package net.psforever.packet.game
import net.psforever.packet.GamePacketOpcode.Type import net.psforever.packet.GamePacketOpcode.Type
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.PlanetSideGUID
import scodec.{Attempt, Codec, Err} import scodec.{Attempt, Codec, Err}
import scodec.bits.BitVector import scodec.bits.BitVector
import scodec.codecs._ import scodec.codecs._

View file

@ -3,42 +3,52 @@ package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import scodec.Codec import scodec.Codec
import scodec.bits.ByteVector
import scodec.codecs._ import scodec.codecs._
import shapeless.{::, HNil} import shapeless.{::, HNil}
/*
action is unimplemented! if action == 0 only outfit_id and member_id are sent
action2 is unimplemented! if action2 == 0 unk2 will contain one additional uint32L
unk2 contains one byte of padding. may contain 4byte of unknown data depending on action2
*/
final case class OutfitMemberEvent( final case class OutfitMemberEvent(
unk00: Int, action: Int, // action is unimplemented
outfit_id: Long, outfit_id: Long,
member_id: Long, member_id: Long,
member_name: String, member_name: String,
rank: Int, // 0-7 rank: Int, // 0-7
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_login: Long, // seconds ago from current time, 0 if online
unk1: Int, action2: Int, // this should always be 1, otherwise there will be actual data in unk2!
padding: ByteVector, // only contains information if unk1 is 0, 1 byte of padding otherwise
) extends PlanetSideGamePacket { ) extends PlanetSideGamePacket {
type Packet = OutfitMemberEvent type Packet = OutfitMemberEvent
def opcode = GamePacketOpcode.OutfitMemberEvent def opcode = GamePacketOpcode.OutfitMemberEvent
def encode = OutfitMemberEvent.encode(this) def encode = OutfitMemberEvent.encode(this)
} }
object OutfitMemberEvent extends Marshallable[OutfitMemberEvent] { object OutfitMemberEvent extends Marshallable[OutfitMemberEvent] {
implicit val codec: Codec[OutfitMemberEvent] = ( implicit val codec: Codec[OutfitMemberEvent] = (
("unk00" | uintL(2)) :: ("action" | uintL(2)) ::
("outfit_id" | uint32L) :: ("outfit_id" | uint32L) ::
("member_id" | uint32L) :: ("member_id" | uint32L) ::
("member_name" | PacketHelpers.encodedWideStringAligned(6)) :: ("member_name" | PacketHelpers.encodedWideStringAligned(6)) ::
("rank" | uint(3)) :: ("rank" | uint(3)) ::
("points" | uint32L) :: ("points" | uint32L) ::
("last_login" | uint32L) :: ("last_login" | uint32L) ::
("unk1" | uint(5)) ("action2" | uintL(1)) ::
("padding" | bytes)
).xmap[OutfitMemberEvent]( ).xmap[OutfitMemberEvent](
{ {
case unk00 :: outfit_id :: member_id :: member_name :: rank :: points :: last_login :: u1 :: HNil => case unk00 :: outfit_id :: member_id :: member_name :: rank :: points :: last_login :: u1 :: padding :: HNil =>
OutfitMemberEvent(unk00, outfit_id, member_id, member_name, rank, points, last_login, u1) OutfitMemberEvent(unk00, outfit_id, member_id, member_name, rank, points, last_login, u1, padding)
}, },
{ {
case OutfitMemberEvent(unk00, outfit_id, member_id, member_name, rank, points, last_login, u1) => case OutfitMemberEvent(unk00, outfit_id, member_id, member_name, rank, points, last_login, action2, padding) =>
unk00 :: outfit_id :: member_id :: member_name :: rank :: points :: last_login :: u1 :: HNil unk00 :: outfit_id :: member_id :: member_name :: rank :: points :: last_login :: action2 :: padding :: HNil
} }
) )
} }

View file

@ -2,7 +2,6 @@
package net.psforever.packet.game package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
import net.psforever.types.PlanetSideGUID
import scodec.Codec import scodec.Codec
import scodec.codecs._ import scodec.codecs._
import shapeless.{::, HNil} import shapeless.{::, HNil}

View file

@ -22,41 +22,50 @@ final case class OutfitMembershipRequest(
abstract class OutfitMembershipRequestAction(val code: Int) abstract class OutfitMembershipRequestAction(val code: Int)
/*
Codecs 2,5,6,7 can either work off of the avatar_id (if GUI was used) or member_name (if chat command was used)
*/
object OutfitMembershipRequestAction { object OutfitMembershipRequestAction {
final case class CreateOutfit( final case class Create(
unk2: String, unk1: String,
unk3: Int,
unk4: Boolean,
outfit_name: String outfit_name: String
) extends OutfitMembershipRequestAction(code = 0) ) extends OutfitMembershipRequestAction(code = 0)
final case class FormOutfit( final case class Form(
unk2: String, unk1: String,
unk3: Int,
unk4: Boolean,
outfit_name: String outfit_name: String
) extends OutfitMembershipRequestAction(code = 1) ) extends OutfitMembershipRequestAction(code = 1)
final case class Unk2( final case class Invite(
unk2: Int, avatar_id: Long,
unk3: Int,
member_name: String, member_name: String,
) extends OutfitMembershipRequestAction(code = 2) ) extends OutfitMembershipRequestAction(code = 2)
final case class AcceptOutfitInvite(
unk2: String final case class AcceptInvite(
member_name: String
) extends OutfitMembershipRequestAction(code = 3) ) extends OutfitMembershipRequestAction(code = 3)
final case class RejectOutfitInvite( final case class RejectInvite(
unk2: String member_name: String
) extends OutfitMembershipRequestAction(code = 4) ) extends OutfitMembershipRequestAction(code = 4)
final case class CancelOutfitInvite( final case class CancelInvite(
unk5: Int, avatar_id: Long,
unk6: Int, member_name: String,
outfit_name: String
) extends OutfitMembershipRequestAction(code = 5) ) extends OutfitMembershipRequestAction(code = 5)
final case class Kick(
avatar_id: Long,
member_name: String,
) extends OutfitMembershipRequestAction(code = 6)
final case class SetRank(
avatar_id: Long, // 32
rank: Int, // 3
member_name: String,
) extends OutfitMembershipRequestAction(code = 7)
final case class Unknown(badCode: Int, data: BitVector) extends OutfitMembershipRequestAction(badCode) final case class Unknown(badCode: Int, data: BitVector) extends OutfitMembershipRequestAction(badCode)
/** /**
@ -66,96 +75,122 @@ object OutfitMembershipRequestAction {
object Codecs { object Codecs {
private val everFailCondition = conditional(included = false, bool) private val everFailCondition = conditional(included = false, bool)
val CreateOutfitCodec: Codec[CreateOutfit] = val CreateCodec: Codec[Create] =
( (
PacketHelpers.encodedWideString :: PacketHelpers.encodedWideStringAligned(5) ::
uint4L ::
bool ::
PacketHelpers.encodedWideString PacketHelpers.encodedWideString
).xmap[CreateOutfit]( ).xmap[Create](
{ {
case unk2 :: unk3 :: unk4 :: outfit_name :: HNil => case u1 :: outfit_name :: HNil =>
CreateOutfit(unk2, unk3, unk4, outfit_name) Create(u1, outfit_name)
}, },
{ {
case CreateOutfit(unk2, unk3, unk4, outfit_name) => case Create(u1, outfit_name) =>
unk2 :: unk3 :: unk4 :: outfit_name :: HNil u1 :: outfit_name :: HNil
} }
) )
val FormOutfitCodec: Codec[FormOutfit] = val FormCodec: Codec[Form] =
( (
PacketHelpers.encodedWideString :: PacketHelpers.encodedWideStringAligned(5) ::
uint4L ::
bool ::
PacketHelpers.encodedWideString PacketHelpers.encodedWideString
).xmap[FormOutfit]( ).xmap[Form](
{ {
case unk2 :: unk3 :: unk4 :: outfit_name :: HNil => case u1 :: outfit_name :: HNil =>
FormOutfit(unk2, unk3, unk4, outfit_name) Form(u1, outfit_name)
}, },
{ {
case FormOutfit(unk2, unk3, unk4, outfit_name) => case Form(u1, outfit_name) =>
unk2 :: unk3 :: unk4 :: outfit_name :: HNil u1 :: outfit_name :: HNil
} }
) )
val Unk2Codec: Codec[Unk2] = val InviteCodec: Codec[Invite] =
( (
uint16L :: uint32L ::
uint16L ::
PacketHelpers.encodedWideStringAligned(5) PacketHelpers.encodedWideStringAligned(5)
).xmap[Unk2]( ).xmap[Invite](
{ {
case unk2 :: unk3 :: member_name :: HNil => case u1 :: member_name :: HNil =>
Unk2(unk2, unk3, member_name) Invite(u1, member_name)
}, },
{ {
case Unk2(unk2, unk3, member_name) => case Invite(u1, member_name) =>
unk2 :: unk3 :: member_name :: HNil u1 :: member_name :: HNil
} }
) )
val AcceptOutfitCodec: Codec[AcceptOutfitInvite] = val AcceptInviteCodec: Codec[AcceptInvite] =
PacketHelpers.encodedWideString.xmap[AcceptOutfitInvite]( PacketHelpers.encodedWideString.xmap[AcceptInvite](
{ {
case unk2 => case u1 =>
AcceptOutfitInvite(unk2) AcceptInvite(u1)
}, },
{ {
case AcceptOutfitInvite(unk2) => case AcceptInvite(u1) =>
unk2 u1
} }
) )
val RejectOutfitCodec: Codec[RejectOutfitInvite] = val RejectInviteCodec: Codec[RejectInvite] =
PacketHelpers.encodedWideString.xmap[RejectOutfitInvite]( PacketHelpers.encodedWideString.xmap[RejectInvite](
{ {
case unk2 => case u1 =>
RejectOutfitInvite(unk2) RejectInvite(u1)
}, },
{ {
case RejectOutfitInvite(unk2) => case RejectInvite(u1) =>
unk2 u1
} }
) )
val CancelOutfitCodec: Codec[CancelOutfitInvite] = val CancelInviteCodec: Codec[CancelInvite] =
( (
uint16L :: uint32L ::
uint16L ::
PacketHelpers.encodedWideStringAligned(5) PacketHelpers.encodedWideStringAligned(5)
).xmap[CancelOutfitInvite]( ).xmap[CancelInvite](
{ {
case unk5 :: unk6 :: outfit_name :: HNil => case u1 :: outfit_name :: HNil =>
CancelOutfitInvite(unk5, unk6, outfit_name) CancelInvite(u1, outfit_name)
}, },
{ {
case CancelOutfitInvite(unk5, unk6, outfit_name) => case CancelInvite(u1, outfit_name) =>
unk5 :: unk6 :: outfit_name :: HNil u1 :: outfit_name :: HNil
} }
) )
val KickCodec: Codec[Kick] =
(
uint32L ::
PacketHelpers.encodedWideStringAligned(5)
).xmap[Kick](
{
case u1 :: member_name :: HNil =>
Kick(u1, member_name)
},
{
case Kick(u1, member_name) =>
u1 :: member_name :: HNil
}
)
val SetRankCodec: Codec[SetRank] =
(
uint32L ::
uintL(3) ::
PacketHelpers.encodedWideStringAligned(2)
).xmap[SetRank](
{
case u1 :: rank :: member_name :: HNil =>
SetRank(u1, rank, member_name)
},
{
case SetRank(u1, rank, member_name) =>
u1 :: rank :: member_name :: HNil
}
)
/** /**
* A common form for known action code indexes with an unknown purpose and transformation is an "Unknown" object. * A common form for known action code indexes with an unknown purpose and transformation is an "Unknown" object.
* *
@ -191,12 +226,12 @@ object OutfitMembershipRequest extends Marshallable[OutfitMembershipRequest] {
val Create: RequestType.Value = Value(0) val Create: RequestType.Value = Value(0)
val Form: RequestType.Value = Value(1) val Form: RequestType.Value = Value(1)
val Unk2: RequestType.Value = Value(2) val Invite: RequestType.Value = Value(2)
val Accept: RequestType.Value = Value(3) val Accept: RequestType.Value = Value(3)
val Reject: RequestType.Value = Value(4) val Reject: RequestType.Value = Value(4)
val Cancel: RequestType.Value = Value(5) val Cancel: RequestType.Value = Value(5)
val Unk6: RequestType.Value = Value(6) // 6 and 7 seen as failed decodes, validity unknown val Kick: RequestType.Value = Value(6)
val Unk7: RequestType.Value = Value(7) val SetRank: RequestType.Value = Value(7)
implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uintL(3)) implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uintL(3))
} }
@ -206,15 +241,15 @@ object OutfitMembershipRequest extends Marshallable[OutfitMembershipRequest] {
import scala.annotation.switch import scala.annotation.switch
((code: @switch) match { ((code: @switch) match {
case 0 => CreateOutfitCodec case 0 => CreateCodec
case 1 => FormOutfitCodec // so far same as Create case 1 => FormCodec // so far same as Create
case 2 => Unk2Codec case 2 => InviteCodec
case 3 => AcceptOutfitCodec case 3 => AcceptInviteCodec
case 4 => RejectOutfitCodec // so far same as Accept case 4 => RejectInviteCodec
case 5 => CancelOutfitCodec case 5 => CancelInviteCodec
case 6 => unknownCodec(action = code) case 6 => KickCodec
case 7 => unknownCodec(action = code) case 7 => SetRankCodec
// 3 bit limit
case _ => failureCodec(code) case _ => failureCodec(code)
}).asInstanceOf[Codec[OutfitMembershipRequestAction]] }).asInstanceOf[Codec[OutfitMembershipRequestAction]]
} }

View file

@ -3,7 +3,6 @@ package net.psforever.packet.game
import net.psforever.packet.GamePacketOpcode.Type import net.psforever.packet.GamePacketOpcode.Type
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.PlanetSideGUID
import scodec.{Attempt, Codec, Err} import scodec.{Attempt, Codec, Err}
import scodec.bits.BitVector import scodec.bits.BitVector
import scodec.codecs._ import scodec.codecs._
@ -12,10 +11,9 @@ import shapeless.{::, HNil}
final case class OutfitMembershipResponse( final case class OutfitMembershipResponse(
response_type: OutfitMembershipResponse.ResponseType.Type, response_type: OutfitMembershipResponse.ResponseType.Type,
unk0: Int, unk0: Int,
unk1: Int,
outfit_id: Long, outfit_id: Long,
target_guid: PlanetSideGUID, target_id: Long,
unk3: Int,
//unk4: Boolean,
action: OutfitMembershipResponseAction action: OutfitMembershipResponseAction
) extends PlanetSideGamePacket { ) extends PlanetSideGamePacket {
type Packet = OutfitMembershipResponse type Packet = OutfitMembershipResponse
@ -28,21 +26,39 @@ final case class OutfitMembershipResponse(
abstract class OutfitMembershipResponseAction(val code: Int) abstract class OutfitMembershipResponseAction(val code: Int)
object OutfitMembershipResponseAction { object OutfitMembershipResponseAction {
final case class CreateOutfitResponse(str1: String, str2: String, str3: String) extends OutfitMembershipResponseAction(code = 0) final case class Universal(
str1: String,
str2: String,
flag: Boolean
) extends OutfitMembershipResponseAction(-1)
final case class Unk1OutfitResponse(player_name: String, outfit_name: String, unk7: Int) extends OutfitMembershipResponseAction(code = 1) final case class CreateResponse(
str1: String,
str2: String,
str3: String
) extends OutfitMembershipResponseAction(code = 0)
final case class Unk2OutfitResponse(player_name: String, outfit_name: String, unk7: Int) extends OutfitMembershipResponseAction(code = 2) // unk7 = rank? final case class Unk1OutfitResponse(
player_name: String,
outfit_name: String,
unk7: Int
) extends OutfitMembershipResponseAction(code = 1)
final case class Unk3OutfitResponse(unk2: String) extends OutfitMembershipResponseAction(code = 3) final case class Unk2OutfitResponse(
player_name: String,
outfit_name: String,
unk7: Int
) extends OutfitMembershipResponseAction(code = 2) // unk7 = rank?
final case class Unk4OutfitResponse(unk5: Int, unk6: Int, outfit_name: String) extends OutfitMembershipResponseAction(code = 4) final case class Unk3OutfitResponse(
unk2: String
) extends OutfitMembershipResponseAction(code = 3)
final case class Unk5OutfitResponse() extends OutfitMembershipResponseAction(code = 5) final case class Unk4OutfitResponse(
unk5: Int,
final case class Unk6OutfitResponse() extends OutfitMembershipResponseAction(code = 6) unk6: Int,
outfit_name: String
final case class Unk7OutfitResponse() extends OutfitMembershipResponseAction(code = 7) ) extends OutfitMembershipResponseAction(code = 4)
final case class Unknown(badCode: Int, data: BitVector) extends OutfitMembershipResponseAction(badCode) final case class Unknown(badCode: Int, data: BitVector) extends OutfitMembershipResponseAction(badCode)
@ -53,17 +69,32 @@ object OutfitMembershipResponseAction {
object Codecs { object Codecs {
private val everFailCondition = conditional(included = false, bool) private val everFailCondition = conditional(included = false, bool)
val Unk0OutfitCodec: Codec[CreateOutfitResponse] = ( val UniversalResponseCodec: Codec[OutfitMembershipResponseAction] = (
PacketHelpers.encodedWideStringAligned(5) ::
PacketHelpers.encodedWideString ::
("flag" | bool)
).xmap[OutfitMembershipResponseAction](
{
case str1 :: str2 :: flag :: HNil =>
Universal(str1, str2, flag)
},
{
case Universal(str1, str2, flag) =>
str1 :: str2 :: flag :: HNil
}
)
val CreateOutfitCodec: Codec[CreateResponse] = (
PacketHelpers.encodedWideStringAligned(5) :: PacketHelpers.encodedWideStringAligned(5) ::
PacketHelpers.encodedWideString :: PacketHelpers.encodedWideString ::
PacketHelpers.encodedWideString PacketHelpers.encodedWideString
).xmap[CreateOutfitResponse]( ).xmap[CreateResponse](
{ {
case str1 :: str2 :: str3 :: HNil => case str1 :: str2 :: str3 :: HNil =>
CreateOutfitResponse(str1, str2, str3) CreateResponse(str1, str2, str3)
}, },
{ {
case CreateOutfitResponse(str1, str2, str3) => case CreateResponse(str1, str2, str3) =>
str1 :: str2 :: str3 :: HNil str1 :: str2 :: str3 :: HNil
} }
) )
@ -99,7 +130,7 @@ object OutfitMembershipResponseAction {
) )
val Unk3OutfitCodec: Codec[Unk3OutfitResponse] = val Unk3OutfitCodec: Codec[Unk3OutfitResponse] =
PacketHelpers.encodedWideString.xmap[Unk3OutfitResponse]( PacketHelpers.encodedWideStringAligned(5).xmap[Unk3OutfitResponse](
{ {
case unk2 => case unk2 =>
Unk3OutfitResponse(unk2) Unk3OutfitResponse(unk2)
@ -110,8 +141,11 @@ object OutfitMembershipResponseAction {
} }
) )
val Unk4OutfitCodec: Codec[Unk4OutfitResponse] = val Unk4OutfitCodec: Codec[Unk4OutfitResponse] = (
(uint16L :: uint16L :: PacketHelpers.encodedWideStringAligned(5)).xmap[Unk4OutfitResponse]( uint16L ::
uint16L ::
PacketHelpers.encodedWideStringAligned(5)
).xmap[Unk4OutfitResponse](
{ {
case unk5 :: unk6 :: outfit_name :: HNil => case unk5 :: unk6 :: outfit_name :: HNil =>
Unk4OutfitResponse(unk5, unk6, outfit_name) Unk4OutfitResponse(unk5, unk6, outfit_name)
@ -122,42 +156,6 @@ object OutfitMembershipResponseAction {
} }
) )
// val Unk5OutfitCodec: Codec[Unk5OutfitResponse] =
// (uint16L :: uint16L :: PacketHelpers.encodedWideStringAligned(5)).xmap[Unk5OutfitResponse](
// {
// case unk5 :: unk6 :: outfit_name :: HNil =>
// Unk5OutfitResponse(unk5, unk6, outfit_name)
// },
// {
// case Unk5OutfitResponse(unk5, unk6, outfit_name) =>
// unk5 :: unk6 :: outfit_name :: HNil
// }
// )
//
// val Unk6OutfitCodec: Codec[Unk6OutfitResponse] =
// (uint16L :: uint16L :: PacketHelpers.encodedWideStringAligned(5)).xmap[Unk6OutfitResponse](
// {
// case _ =>
// Unk6OutfitResponse()
// },
// {
// case Unk6OutfitResponse() =>
// _
// }
// )
//
// val Unk7OutfitCodec: Codec[Unk7OutfitResponse] =
// (uint16L :: uint16L :: PacketHelpers.encodedWideStringAligned(5)).xmap[Unk7OutfitResponse](
// {
// case _ =>
// Unk7OutfitResponse()
// },
// {
// case Unk7OutfitResponse() =>
// _
// }
// )
/** /**
* A common form for known action code indexes with an unknown purpose and transformation is an "Unknown" object. * A common form for known action code indexes with an unknown purpose and transformation is an "Unknown" object.
* @param action the action behavior code * @param action the action behavior code
@ -206,14 +204,24 @@ object OutfitMembershipResponse extends Marshallable[OutfitMembershipResponse] {
import scala.annotation.switch import scala.annotation.switch
((code: @switch) match { ((code: @switch) match {
case 0 => Unk0OutfitCodec // seem as OMReq Create response case 0 => UniversalResponseCodec
case 1 => Unk1OutfitCodec case 1 => UniversalResponseCodec
case 2 => Unk2OutfitCodec case 2 => UniversalResponseCodec
case 3 => Unk3OutfitCodec case 3 => UniversalResponseCodec
case 4 => Unk4OutfitCodec case 4 => UniversalResponseCodec
case 5 => unknownCodec(action = code) case 5 => UniversalResponseCodec
case 6 => unknownCodec(action = code) case 6 => UniversalResponseCodec
case 7 => unknownCodec(action = code) case 7 => UniversalResponseCodec
// case 0 => CreateOutfitCodec // seem as OMReq Create response
// case 1 => Unk1OutfitCodec
// case 2 => Unk2OutfitCodec
// case 3 => Unk3OutfitCodec
// case 4 => Unk4OutfitCodec
// case 5 => unknownCodec(action = code)
// case 6 => unknownCodec(action = code)
// case 7 => unknownCodec(action = code)
// 3 bit limit // 3 bit limit
case _ => failureCodec(code) case _ => failureCodec(code)
}).asInstanceOf[Codec[OutfitMembershipResponseAction]] }).asInstanceOf[Codec[OutfitMembershipResponseAction]]
@ -221,21 +229,20 @@ object OutfitMembershipResponse extends Marshallable[OutfitMembershipResponse] {
implicit val codec: Codec[OutfitMembershipResponse] = ( implicit val codec: Codec[OutfitMembershipResponse] = (
("response_type" | ResponseType.codec) >>:~ { response_type => ("response_type" | ResponseType.codec) >>:~ { response_type =>
("unk0" | uint8L) :: ("unk0" | uintL(5)) ::
("unk1" | uintL(3)) ::
("outfit_id" | uint32L) :: ("outfit_id" | uint32L) ::
("target_guid" | PlanetSideGUID.codec) :: ("target_id" | uint32L) ::
("unk3" | uint16L) ::
//("unk4" | bool) ::
("action" | selectFromType(response_type.id)) ("action" | selectFromType(response_type.id))
} }
).xmap[OutfitMembershipResponse]( ).xmap[OutfitMembershipResponse](
{ {
case response_type :: u0 :: outfit_id :: target_guid :: u3 :: action :: HNil => case response_type :: u0 :: u1 :: outfit_id :: target_id :: action :: HNil =>
OutfitMembershipResponse(response_type, u0, outfit_id, target_guid, u3, action) OutfitMembershipResponse(response_type, u0, u1, outfit_id, target_id, action)
}, },
{ {
case OutfitMembershipResponse(response_type, u0, outfit_id, u2, u3, action) => case OutfitMembershipResponse(response_type, u0, u1, outfit_id, target_id, action) =>
response_type :: u0 :: outfit_id :: u2 :: u3 :: action :: HNil response_type :: u0 :: u1 :: outfit_id :: target_id :: action :: HNil
} }
) )
} }

View file

@ -5,7 +5,6 @@ import net.psforever.packet._
import net.psforever.packet.game.OutfitEvent.RequestType import net.psforever.packet.game.OutfitEvent.RequestType
import net.psforever.packet.game.OutfitEventAction._ import net.psforever.packet.game.OutfitEventAction._
import net.psforever.packet.game._ import net.psforever.packet.game._
import net.psforever.types.PlanetSideGUID
import org.specs2.mutable._ import org.specs2.mutable._
import scodec.bits._ import scodec.bits._
@ -64,7 +63,9 @@ class OutfitEventTest extends Specification {
unk9 = 0, unk9 = 0,
OutfitRankNames("Dog Meat","Russian","","","Squad Leaders","Acting Commanders","Reapers",""), OutfitRankNames("Dog Meat","Russian","","","Squad Leaders","Acting Commanders","Reapers",""),
"\\#0000ffMumble \\#0033ffInfo \\#0066ffis \\#0099ffthemoose.typefrag.com \\#00ccffport \\#00ffff9350 \\#00ccffjoin \\#0099ffit \\#0066ffor \\#0033ffbe \\#0000ffkicked.", "\\#0000ffMumble \\#0033ffInfo \\#0066ffis \\#0099ffthemoose.typefrag.com \\#00ccffport \\#00ffff9350 \\#00ccffjoin \\#0099ffit \\#0066ffor \\#0033ffbe \\#0000ffkicked.",
PlanetSideGUID(32783), 15,
128,
0,
0, 0,
0, 0,
0, 0,
@ -93,7 +94,9 @@ class OutfitEventTest extends Specification {
unk9 = 0, unk9 = 0,
OutfitRankNames("Dog Meat","Russian","","","Squad Leaders","Acting Commanders","Reapers",""), OutfitRankNames("Dog Meat","Russian","","","Squad Leaders","Acting Commanders","Reapers",""),
"\\#0000ffMumble \\#0033ffInfo \\#0066ffis \\#0099ffthemoose.typefrag.com \\#00ccffport \\#00ffff9350 \\#00ccffjoin \\#0099ffit \\#0066ffor \\#0033ffbe \\#0000ffkicked.", "\\#0000ffMumble \\#0033ffInfo \\#0066ffis \\#0099ffthemoose.typefrag.com \\#00ccffport \\#00ffff9350 \\#00ccffjoin \\#0099ffit \\#0066ffor \\#0033ffbe \\#0000ffkicked.",
PlanetSideGUID(32783), 15,
128,
0,
0, 0,
0, 0,
0, 0,
@ -151,8 +154,10 @@ class OutfitEventTest extends Specification {
unk9 = 0, unk9 = 0,
OutfitRankNames("","","","","","","",""), OutfitRankNames("","","","","","","",""),
"", "",
PlanetSideGUID(28672), 0,
33353, 112,
73,
130,
0, 0,
0, 0,
0, 0,
@ -179,15 +184,17 @@ class OutfitEventTest extends Specification {
unk9 = 0, unk9 = 0,
OutfitRankNames("","","","","","","",""), OutfitRankNames("","","","","","","",""),
"", "",
PlanetSideGUID(28672), 0,
33353, 112,
0, 73,
130,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0
) )
) )
) )

View file

@ -9,18 +9,18 @@ import org.specs2.mutable._
import scodec.bits.ByteVector import scodec.bits.ByteVector
class OutfitListEventTest extends Specification { class OutfitListEventTest extends Specification {
val unk0_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 unk0_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 unk1_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")
val unk2_ABC: ByteVector = ByteVector.fromValidHex("98 4 9a3c0001 16da4e00 0400000 11a042006c006f006f00640020006f0066002000560061006e007500 864b00610072006e002d004500") val unk2_2_ABC: ByteVector = ByteVector.fromValidHex("98 4 9a3c0001 16da4e00 0400000 11a042006c006f006f00640020006f0066002000560061006e007500 864b00610072006e002d004500")
val unk3_ABC: ByteVector = ByteVector.fromValidHex("98 4 9c3c0000 df587c00 1400000 11a054006800650020004e00650076006500720068006f006f006400 8e6f00460058006f00530074006f006e0065004d0061006e002d004700") val unk2_3_ABC: ByteVector = ByteVector.fromValidHex("98 4 9c3c0000 df587c00 1400000 11a054006800650020004e00650076006500720068006f006f006400 8e6f00460058006f00530074006f006e0065004d0061006e002d004700")
val unk4_ABC: ByteVector = ByteVector.fromValidHex("98 4 c03c0000 24060400 0600000 1220540068006500200042006c00610063006b0020004b006e0069006700680074007300 874400720061007a00760065006e00") val unk2_4_ABC: ByteVector = ByteVector.fromValidHex("98 4 c03c0000 24060400 0600000 1220540068006500200042006c00610063006b0020004b006e0069006700680074007300 874400720061007a00760065006e00")
val unk5_ABC: ByteVector = ByteVector.fromValidHex("98 5 383c0001 4b709a00 0c00000 10a03e005400760053003c00 89430061007000650062006f00610074007300") val unk2_5_ABC: ByteVector = ByteVector.fromValidHex("98 5 383c0001 4b709a00 0c00000 10a03e005400760053003c00 89430061007000650062006f00610074007300")
val unk6_ABC: ByteVector = ByteVector.fromValidHex("98 5 b03c0000 35d67000 0400000 11404c006f0073007400200043006100750073006500 895a00650072006f004b00650077006c006c00") val unk2_6_ABC: ByteVector = ByteVector.fromValidHex("98 5 b03c0000 35d67000 0400000 11404c006f0073007400200043006100750073006500 895a00650072006f004b00650077006c006c00")
val unk7_ABC: ByteVector = ByteVector.fromValidHex("98 4 043e0001 9fb82616 1400000 11e0540068006500200042006c00610063006b00200054006f00770065007200 874b00720075007000680065007800") val unk2_7_ABC: ByteVector = ByteVector.fromValidHex("98 4 043e0001 9fb82616 1400000 11e0540068006500200042006c00610063006b00200054006f00770065007200 874b00720075007000680065007800")
"decode unk0_ABC" in { "decode unk0_ABC" in {
PacketCoding.decodePacket(unk0_ABC).require match { PacketCoding.decodePacket(unk2_0_ABC).require match {
case OutfitListEvent(code, ListElementOutfit(unk1, points, members, outfit_name, outfit_leader)) => case OutfitListEvent(code, ListElementOutfit(unk1, points, members, outfit_name, outfit_leader)) =>
code mustEqual OutfitListEvent.RequestType.ListElementOutfit code mustEqual OutfitListEvent.RequestType.ListElementOutfit
unk1 mustEqual 7668 unk1 mustEqual 7668
@ -37,7 +37,7 @@ class OutfitListEventTest extends Specification {
val msg = OutfitListEvent(RequestType.ListElementOutfit, ListElementOutfit(7668, 788224, 4, "NightLords", "NYCat")) val msg = OutfitListEvent(RequestType.ListElementOutfit, ListElementOutfit(7668, 788224, 4, "NightLords", "NYCat"))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual unk0_ABC pkt mustEqual unk2_0_ABC
} }
} }

View file

@ -10,7 +10,7 @@ import scodec.bits._
class OutfitMemberEventTest extends Specification { 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 3518 4000 1a4e 4100 2 180 450078007000650072007400 8483 07e0 119d bfe0 70" // 0x90048640001030c28022404c0061007a00650072003100390038003200f43a45e00b4c604010
val unk0_ABC_Lazer: ByteVector = hex"90 048640001030c28022404c0061007a00650072003100390038003200f43a45e00b4c604010" val unk0_ABC_Lazer = hex"90 0 4864 0001 030c 2802 24 0 4c0061007a00650072003100390038003200 f43a 45e0 0b4c 6040 10"
val OpolE = hex"90 0 4864 0003 aad6 280a 14 0 4f0070006f006c004500 c9a1 80e0 0d03 2040 10" val OpolE = hex"90 0 4864 0003 aad6 280a 14 0 4f0070006f006c004500 c9a1 80e0 0d03 2040 10"
val Billy = hex"90 0 4864 0003 a41a 280a 20 0 620069006c006c007900320035003600 935f 6000 186a b040 50" val Billy = hex"90 0 4864 0003 a41a 280a 20 0 620069006c006c007900320035003600 935f 6000 186a b040 50"
@ -19,33 +19,20 @@ val unk0_ABC_Lazer: ByteVector = hex"90 048640001030c28022404c0061007a00650
val PvtPa = hex"90 0 4864 0000 1e69 e80a 2c 0 500076007400500061006e00630061006b0065007300 705e a080 0a85 e060 10" val PvtPa = hex"90 0 4864 0000 1e69 e80a 2c 0 500076007400500061006e00630061006b0065007300 705e a080 0a85 e060 10"
val Night = hex"90 0 4864 0002 4cf0 3802 28 0 4e006900670068007400770069006e0067003100 b8fb 9a40 0da6 ec80 50" val Night = hex"90 0 4864 0002 4cf0 3802 28 0 4e006900670068007400770069006e0067003100 b8fb 9a40 0da6 ec80 50"
/* val Unk0 = hex"90 5 40542002 3f61e808 0"
OutfitMemberEvent(0, ValidPlanetSideGUID(6418), 0, 0, 64, 195, 10, 0, Lazer1982, 230, 220, 37, 160, 21, 62, 96, 64, 16, BitVector(empty))
OutfitMemberEvent(0, ValidPlanetSideGUID(6418), 0, 0, 7, 154, 122, 2, PvtPancakes, 112, 94, 160, 128, 10, 133, 224, 96, 16, BitVector(empty))
OutfitMemberEvent(0, ValidPlanetSideGUID(6418), 0, 0, 134, 217, 19, 0, VirusGiver, 47, 137, 0, 128, 0, 0, 0, 0, 16, BitVector(empty))
OutfitMemberEvent(0, ValidPlanetSideGUID(6418), 0, 0, 234, 181, 138, 2, OpolE, 201, 161, 128, 224, 13, 3, 32, 64, 16, BitVector(empty))
OutfitMemberEvent(0, ValidPlanetSideGUID(6418), 0, 0, 233, 6, 138, 2, billy256, 147, 95, 96, 0, 24, 106, 176, 64, 80, BitVector(empty))
*/
"decode Unk0 ABC" in { "decode Unk0 ABC" in {
PacketCoding.decodePacket(unk0_ABC_Lazer).require match { PacketCoding.decodePacket(unk0_ABC_Lazer).require match {
case OutfitMemberEvent(unk00, outfit_guid, unk3, unk5, member_name, unk8, unk9, unk10, unk11, unk12, unk13,unk14,unk15,unk16) => case OutfitMemberEvent(action, outfit_id, member_id, member_name, rank, points, last_login, action2, padding) =>
unk00 mustEqual 0 action mustEqual 0
outfit_guid mustEqual 6418L outfit_id mustEqual 6418L
unk3 mustEqual 49984 member_id mustEqual 705344
unk5 mustEqual 10
member_name mustEqual "Lazer1982" member_name mustEqual "Lazer1982"
unk8 mustEqual 244 rank mustEqual 7
unk9 mustEqual 58 points mustEqual 3134113
unk10 mustEqual 69 last_login mustEqual 156506
unk11 mustEqual 224 action2 mustEqual 1
unk12 mustEqual 11 padding mustEqual ByteVector(0x0)
unk13 mustEqual 76
unk14 mustEqual 96
unk15 mustEqual 64
unk16 mustEqual 16
case _ => case _ =>
ko ko
} }
@ -53,20 +40,15 @@ val unk0_ABC_Lazer: ByteVector = hex"90 048640001030c28022404c0061007a00650
"encode Unk0 ABC" in { "encode Unk0 ABC" in {
val msg = OutfitMemberEvent( val msg = OutfitMemberEvent(
unk00 = 0, action = 0,
outfit_id = 6418L, outfit_id = 6418L,
unk3 = 49984, member_id = 705344,
unk5 = 10,
member_name = "Lazer1982", member_name = "Lazer1982",
unk8 = 244, rank = 7,
unk9 = 58, points = 3134113,
unk10 = 69, last_login = 156506,
unk11 = 224, action2 = 1,
unk12 = 11, ByteVector.empty
unk13 = 76,
unk14 = 96,
unk15 = 64,
unk16 = 16,
) )
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector

View file

@ -13,7 +13,7 @@ class OutfitMembershipRequestTest extends Specification {
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"
val form_1 = hex"8c 2 1000 000 1000 81 3100" val form_1 = hex"8c 2 1000 000 1000 81 3100"
val unk2 = hex"8c 5 bb399e0 2000 0000 1140 7600690072007500730067006900760065007200" // -- virusgiver val invite_old = hex"8c 5 bb399e0 2000 0000 1140 7600690072007500730067006900760065007200" // -- virusgiver
val unk3 = hex"8c 5 bb399e0 2000 0000 1080 750072006f006200" // -- "urob" -- could be false positive -- seems to gets an OMSResp -> 0x8d271bb399e025af8f405080550072006f0062008080 val unk3 = hex"8c 5 bb399e0 2000 0000 1080 750072006f006200" // -- "urob" -- could be false positive -- seems to gets an OMSResp -> 0x8d271bb399e025af8f405080550072006f0062008080
val accept_1 = hex"8c 6 0200 000 1000" val accept_1 = hex"8c 6 0200 000 1000"
val accept_2 = hex"8c 6 0400 000 1000" val accept_2 = hex"8c 6 0400 000 1000"
@ -23,19 +23,24 @@ class OutfitMembershipRequestTest extends Specification {
val cancel_1_abc = hex"8c a 0200 000 0000 0000 1060 610062006300" val cancel_1_abc = hex"8c a 0200 000 0000 0000 1060 610062006300"
val cancel_3_def = hex"8c a 0600 000 0000 0000 1060 640065006600" // /outfitcancel 123 def -- first parameter is skipped val cancel_3_def = hex"8c a 0600 000 0000 0000 1060 640065006600" // /outfitcancel 123 def -- first parameter is skipped
// dumped from half implemented outfit
val invite = hex"8c4020000000000000116069006e00760069007400650054006500730074003100"
val kick = hex"8cc020000017ac8f405000"
val setrank = hex"8ce020000017ac8f404600" // setting rank from 0 to 1
"decode CreateOutfit ABC" in { "decode CreateOutfit ABC" in {
PacketCoding.decodePacket(create_ABC).require match { PacketCoding.decodePacket(create_ABC).require match {
case OutfitMembershipRequest(request_type, avatar_id, action) => case OutfitMembershipRequest(request_type, outfit_id, action) =>
request_type mustEqual RequestType.Create request_type mustEqual RequestType.Create
avatar_id mustEqual 1 outfit_id mustEqual 1
action mustEqual CreateOutfit("", 0, unk4 = false, "ABC") action mustEqual Create("", "ABC")
case _ => case _ =>
ko ko
} }
} }
"encode CreateOutfit ABC" in { "encode CreateOutfit ABC" in {
val msg = OutfitMembershipRequest(RequestType.Create, 1, CreateOutfit("", 0, unk4 = false, "ABC")) val msg = OutfitMembershipRequest(RequestType.Create, 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
@ -43,17 +48,17 @@ 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, avatar_id, action) => case OutfitMembershipRequest(request_type, outfit_id, action) =>
request_type mustEqual RequestType.Create request_type mustEqual RequestType.Create
avatar_id mustEqual 8 outfit_id mustEqual 8
action mustEqual CreateOutfit("", 0, unk4 = false, "2222") action mustEqual Create("", "2222")
case _ => case _ =>
ko ko
} }
} }
"encode CreateOutfit 2222" in { "encode CreateOutfit 2222" in {
val msg = OutfitMembershipRequest(RequestType.Create, 8, CreateOutfit("", 0, unk4 = false, "2222")) val msg = OutfitMembershipRequest(RequestType.Create, 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
@ -61,17 +66,17 @@ 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, avatar_id, action) => case OutfitMembershipRequest(request_type, outfit_id, action) =>
request_type mustEqual RequestType.Form request_type mustEqual RequestType.Form
avatar_id mustEqual 1 outfit_id mustEqual 1
action mustEqual FormOutfit("", 0, unk4 = false, "abc") action mustEqual Form("", "abc")
case _ => case _ =>
ko ko
} }
} }
"encode FormOutfit abc" in { "encode FormOutfit abc" in {
val msg = OutfitMembershipRequest(RequestType.Form, 1, FormOutfit("", 0, unk4 = false, "abc")) val msg = OutfitMembershipRequest(RequestType.Form, 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
@ -79,53 +84,53 @@ 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, avatar_id, action) => case OutfitMembershipRequest(request_type, outfit_id, action) =>
request_type mustEqual RequestType.Form request_type mustEqual RequestType.Form
avatar_id mustEqual 8 outfit_id mustEqual 8
action mustEqual FormOutfit("", 0, unk4 = false, "1") action mustEqual Form("", "1")
case _ => case _ =>
ko ko
} }
} }
"encode FormOutfit 1" in { "encode FormOutfit 1" in {
val msg = OutfitMembershipRequest(RequestType.Form, 8, FormOutfit("", 0, unk4 = false, "1")) val msg = OutfitMembershipRequest(RequestType.Form, 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
} }
"decode Unk2" in { "decode Invite" in {
PacketCoding.decodePacket(unk2).require match { PacketCoding.decodePacket(invite_old).require match {
case OutfitMembershipRequest(request_type, outfit_id, action) => case OutfitMembershipRequest(request_type, outfit_id, action) =>
request_type mustEqual RequestType.Unk2 request_type mustEqual RequestType.Invite
outfit_id mustEqual 30383325L outfit_id mustEqual 30383325L
action mustEqual Unk2(0, 0, "virusgiver") action mustEqual Invite(0, "virusgiver")
case _ => case _ =>
ko ko
} }
} }
"encode Unk2" in { "encode Invite" in {
val msg = OutfitMembershipRequest(RequestType.Unk2, 30383325L, Unk2(0, 0, "virusgiver")) val msg = OutfitMembershipRequest(RequestType.Invite, 30383325L, Invite(0, "virusgiver"))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual unk2 pkt mustEqual invite_old
} }
"decode AcceptOutfitInvite 1" in { "decode AcceptOutfitInvite 1" in {
PacketCoding.decodePacket(accept_1).require match { PacketCoding.decodePacket(accept_1).require match {
case OutfitMembershipRequest(request_type, avatar_id, action) => case OutfitMembershipRequest(request_type, outfit_id, action) =>
request_type mustEqual RequestType.Accept request_type mustEqual RequestType.Accept
avatar_id mustEqual 1 outfit_id mustEqual 1
action mustEqual AcceptOutfitInvite("") action mustEqual AcceptInvite("")
case _ => case _ =>
ko ko
} }
} }
"encode AcceptOutfitInvite 1" in { "encode AcceptOutfitInvite 1" in {
val msg = OutfitMembershipRequest(RequestType.Accept, 1, AcceptOutfitInvite("")) val msg = OutfitMembershipRequest(RequestType.Accept, 1, AcceptInvite(""))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual accept_1 pkt mustEqual accept_1
@ -133,17 +138,17 @@ 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, avatar_id, action) => case OutfitMembershipRequest(request_type, outfit_id, action) =>
request_type mustEqual RequestType.Accept request_type mustEqual RequestType.Accept
avatar_id mustEqual 2 outfit_id mustEqual 2
action mustEqual AcceptOutfitInvite("") action mustEqual AcceptInvite("")
case _ => case _ =>
ko ko
} }
} }
"encode AcceptOutfitInvite 2" in { "encode AcceptOutfitInvite 2" in {
val msg = OutfitMembershipRequest(RequestType.Accept, 2, AcceptOutfitInvite("")) val msg = OutfitMembershipRequest(RequestType.Accept, 2, AcceptInvite(""))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual accept_2 pkt mustEqual accept_2
@ -151,17 +156,17 @@ 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, avatar_id, action) => case OutfitMembershipRequest(request_type, outfit_id, action) =>
request_type mustEqual RequestType.Reject request_type mustEqual RequestType.Reject
avatar_id mustEqual 1 outfit_id mustEqual 1
action mustEqual RejectOutfitInvite("") action mustEqual RejectInvite("")
case _ => case _ =>
ko ko
} }
} }
"encode RejectOutfitInvite 1" in { "encode RejectOutfitInvite 1" in {
val msg = OutfitMembershipRequest(RequestType.Reject, 1, RejectOutfitInvite("")) val msg = OutfitMembershipRequest(RequestType.Reject, 1, RejectInvite(""))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual reject_1 pkt mustEqual reject_1
@ -169,17 +174,17 @@ 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, avatar_id, action) => case OutfitMembershipRequest(request_type, outfit_id, action) =>
request_type mustEqual RequestType.Reject request_type mustEqual RequestType.Reject
avatar_id mustEqual 2 outfit_id mustEqual 2
action mustEqual RejectOutfitInvite("") action mustEqual RejectInvite("")
case _ => case _ =>
ko ko
} }
} }
"encode RejectOutfitInvite 2" in { "encode RejectOutfitInvite 2" in {
val msg = OutfitMembershipRequest(RequestType.Reject, 2, RejectOutfitInvite("")) val msg = OutfitMembershipRequest(RequestType.Reject, 2, RejectInvite(""))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual reject_2 pkt mustEqual reject_2
@ -187,17 +192,17 @@ 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, avatar_id, action) => case OutfitMembershipRequest(request_type, outfit_id, action) =>
request_type mustEqual RequestType.Cancel request_type mustEqual RequestType.Cancel
avatar_id mustEqual 3 outfit_id mustEqual 3
action mustEqual CancelOutfitInvite(0, 0, "") action mustEqual CancelInvite(0, "")
case _ => case _ =>
ko ko
} }
} }
"encode CancelOutfitInvite 3" in { "encode CancelOutfitInvite 3" in {
val msg = OutfitMembershipRequest(RequestType.Cancel, 3, CancelOutfitInvite(0, 0, "")) val msg = OutfitMembershipRequest(RequestType.Cancel, 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
@ -205,17 +210,17 @@ 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, avatar_id, action) => case OutfitMembershipRequest(request_type, outfit_id, action) =>
request_type mustEqual RequestType.Cancel request_type mustEqual RequestType.Cancel
avatar_id mustEqual 1 outfit_id mustEqual 1
action mustEqual CancelOutfitInvite(0, 0, "abc") action mustEqual CancelInvite(0, "abc")
case _ => case _ =>
ko ko
} }
} }
"encode CancelOutfitInvite 1 abc" in { "encode CancelOutfitInvite 1 abc" in {
val msg = OutfitMembershipRequest(RequestType.Cancel, 1, CancelOutfitInvite(0, 0, "abc")) val msg = OutfitMembershipRequest(RequestType.Cancel, 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
@ -223,19 +228,81 @@ 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, avatar_id, action) => case OutfitMembershipRequest(request_type, outfit_id, action) =>
request_type mustEqual RequestType.Cancel request_type mustEqual RequestType.Cancel
avatar_id mustEqual 3 outfit_id mustEqual 3
action mustEqual CancelOutfitInvite(0, 0, "def") action mustEqual CancelInvite(0, "def")
case _ => case _ =>
ko ko
} }
} }
"encode CancelOutfitInvite 3 def" in { "encode CancelOutfitInvite 3 def" in {
val msg = OutfitMembershipRequest(RequestType.Cancel, 3, CancelOutfitInvite(0, 0, "def")) val msg = OutfitMembershipRequest(RequestType.Cancel, 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
} }
//
"decode invite" in {
PacketCoding.decodePacket(invite).require match {
case OutfitMembershipRequest(request_type, outfit_id, Invite(unk1, member_name)) =>
request_type mustEqual RequestType.Invite
outfit_id mustEqual 1
unk1 mustEqual 0
member_name mustEqual "inviteTest1"
case _ =>
ko
}
}
"encode invite" in {
val msg = OutfitMembershipRequest(RequestType.Invite, 1, Invite(0, "inviteTest1"))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual invite
}
"decode kick" in {
PacketCoding.decodePacket(kick).require match {
case OutfitMembershipRequest(request_type, outfit_id, Kick(avatar_id, member_name)) =>
request_type mustEqual RequestType.Kick
outfit_id mustEqual 1
avatar_id mustEqual 41575613
member_name mustEqual ""
case _ =>
ko
}
}
"encode kick" in {
val msg = OutfitMembershipRequest(RequestType.Kick, 1, Kick(41575613, ""))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual kick
}
"decode setrank" in {
PacketCoding.decodePacket(setrank).require match {
case OutfitMembershipRequest(request_type, outfit_id, SetRank(avatar_id, rank, member_name)) =>
request_type mustEqual RequestType.SetRank
outfit_id mustEqual 1
avatar_id mustEqual 41575613
rank mustEqual 1
member_name mustEqual ""
case _ =>
ko
}
}
"encode setrank" in {
val msg = OutfitMembershipRequest(RequestType.SetRank, 1, SetRank(41575613, 1, ""))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual setrank
}
} }