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, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.PlanetSideGUID
import scodec.{Attempt, Codec, Err}
import scodec.bits.BitVector
import scodec.codecs._

View file

@ -3,42 +3,52 @@ package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import scodec.Codec
import scodec.bits.ByteVector
import scodec.codecs._
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(
unk00: Int,
outfit_id: Long,
member_id: Long,
member_name: String,
rank: Int, // 0-7
points: Long, // client divides this by 100
last_login: Long, // seconds ago from current time, 0 if online
unk1: Int,
) extends PlanetSideGamePacket {
action: Int, // action is unimplemented
outfit_id: Long,
member_id: Long,
member_name: String,
rank: Int, // 0-7
points: Long, // client divides this by 100
last_login: Long, // seconds ago from current time, 0 if online
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 {
type Packet = OutfitMemberEvent
def opcode = GamePacketOpcode.OutfitMemberEvent
def encode = OutfitMemberEvent.encode(this)
}
object OutfitMemberEvent extends Marshallable[OutfitMemberEvent] {
implicit val codec: Codec[OutfitMemberEvent] = (
("unk00" | uintL(2)) ::
("outfit_id" | uint32L) ::
("member_id" | uint32L) ::
("member_name" | PacketHelpers.encodedWideStringAligned(6)) ::
("rank" | uint(3)) ::
("points" | uint32L) ::
("last_login" | uint32L) ::
("unk1" | uint(5))
("action" | uintL(2)) ::
("outfit_id" | uint32L) ::
("member_id" | uint32L) ::
("member_name" | PacketHelpers.encodedWideStringAligned(6)) ::
("rank" | uint(3)) ::
("points" | uint32L) ::
("last_login" | uint32L) ::
("action2" | uintL(1)) ::
("padding" | bytes)
).xmap[OutfitMemberEvent](
{
case unk00 :: outfit_id :: member_id :: member_name :: rank :: points :: last_login :: u1 :: HNil =>
OutfitMemberEvent(unk00, outfit_id, member_id, member_name, rank, points, last_login, u1)
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, padding)
},
{
case OutfitMemberEvent(unk00, outfit_id, member_id, member_name, rank, points, last_login, u1) =>
unk00 :: outfit_id :: member_id :: member_name :: rank :: points :: last_login :: u1 :: HNil
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 :: action2 :: padding :: HNil
}
)
}

View file

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

View file

@ -22,41 +22,50 @@ final case class OutfitMembershipRequest(
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 {
final case class CreateOutfit(
unk2: String,
unk3: Int,
unk4: Boolean,
final case class Create(
unk1: String,
outfit_name: String
) extends OutfitMembershipRequestAction(code = 0)
final case class FormOutfit(
unk2: String,
unk3: Int,
unk4: Boolean,
final case class Form(
unk1: String,
outfit_name: String
) extends OutfitMembershipRequestAction(code = 1)
final case class Unk2(
unk2: Int,
unk3: Int,
final case class Invite(
avatar_id: Long,
member_name: String,
) extends OutfitMembershipRequestAction(code = 2)
final case class AcceptOutfitInvite(
unk2: String
final case class AcceptInvite(
member_name: String
) extends OutfitMembershipRequestAction(code = 3)
final case class RejectOutfitInvite(
unk2: String
final case class RejectInvite(
member_name: String
) extends OutfitMembershipRequestAction(code = 4)
final case class CancelOutfitInvite(
unk5: Int,
unk6: Int,
outfit_name: String
final case class CancelInvite(
avatar_id: Long,
member_name: String,
) 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)
/**
@ -66,96 +75,122 @@ object OutfitMembershipRequestAction {
object Codecs {
private val everFailCondition = conditional(included = false, bool)
val CreateOutfitCodec: Codec[CreateOutfit] =
val CreateCodec: Codec[Create] =
(
PacketHelpers.encodedWideString ::
uint4L ::
bool ::
PacketHelpers.encodedWideStringAligned(5) ::
PacketHelpers.encodedWideString
).xmap[CreateOutfit](
).xmap[Create](
{
case unk2 :: unk3 :: unk4 :: outfit_name :: HNil =>
CreateOutfit(unk2, unk3, unk4, outfit_name)
case u1 :: outfit_name :: HNil =>
Create(u1, outfit_name)
},
{
case CreateOutfit(unk2, unk3, unk4, outfit_name) =>
unk2 :: unk3 :: unk4 :: outfit_name :: HNil
case Create(u1, outfit_name) =>
u1 :: outfit_name :: HNil
}
)
val FormOutfitCodec: Codec[FormOutfit] =
val FormCodec: Codec[Form] =
(
PacketHelpers.encodedWideString ::
uint4L ::
bool ::
PacketHelpers.encodedWideStringAligned(5) ::
PacketHelpers.encodedWideString
).xmap[FormOutfit](
).xmap[Form](
{
case unk2 :: unk3 :: unk4 :: outfit_name :: HNil =>
FormOutfit(unk2, unk3, unk4, outfit_name)
case u1 :: outfit_name :: HNil =>
Form(u1, outfit_name)
},
{
case FormOutfit(unk2, unk3, unk4, outfit_name) =>
unk2 :: unk3 :: unk4 :: outfit_name :: HNil
case Form(u1, outfit_name) =>
u1 :: outfit_name :: HNil
}
)
val Unk2Codec: Codec[Unk2] =
val InviteCodec: Codec[Invite] =
(
uint16L ::
uint16L ::
uint32L ::
PacketHelpers.encodedWideStringAligned(5)
).xmap[Unk2](
).xmap[Invite](
{
case unk2 :: unk3 :: member_name :: HNil =>
Unk2(unk2, unk3, member_name)
case u1 :: member_name :: HNil =>
Invite(u1, member_name)
},
{
case Unk2(unk2, unk3, member_name) =>
unk2 :: unk3 :: member_name :: HNil
case Invite(u1, member_name) =>
u1 :: member_name :: HNil
}
)
val AcceptOutfitCodec: Codec[AcceptOutfitInvite] =
PacketHelpers.encodedWideString.xmap[AcceptOutfitInvite](
val AcceptInviteCodec: Codec[AcceptInvite] =
PacketHelpers.encodedWideString.xmap[AcceptInvite](
{
case unk2 =>
AcceptOutfitInvite(unk2)
case u1 =>
AcceptInvite(u1)
},
{
case AcceptOutfitInvite(unk2) =>
unk2
case AcceptInvite(u1) =>
u1
}
)
val RejectOutfitCodec: Codec[RejectOutfitInvite] =
PacketHelpers.encodedWideString.xmap[RejectOutfitInvite](
val RejectInviteCodec: Codec[RejectInvite] =
PacketHelpers.encodedWideString.xmap[RejectInvite](
{
case unk2 =>
RejectOutfitInvite(unk2)
case u1 =>
RejectInvite(u1)
},
{
case RejectOutfitInvite(unk2) =>
unk2
case RejectInvite(u1) =>
u1
}
)
val CancelOutfitCodec: Codec[CancelOutfitInvite] =
val CancelInviteCodec: Codec[CancelInvite] =
(
uint16L ::
uint16L ::
uint32L ::
PacketHelpers.encodedWideStringAligned(5)
).xmap[CancelOutfitInvite](
).xmap[CancelInvite](
{
case unk5 :: unk6 :: outfit_name :: HNil =>
CancelOutfitInvite(unk5, unk6, outfit_name)
case u1 :: outfit_name :: HNil =>
CancelInvite(u1, outfit_name)
},
{
case CancelOutfitInvite(unk5, unk6, outfit_name) =>
unk5 :: unk6 :: outfit_name :: HNil
case CancelInvite(u1, outfit_name) =>
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.
*
@ -191,12 +226,12 @@ object OutfitMembershipRequest extends Marshallable[OutfitMembershipRequest] {
val Create: RequestType.Value = Value(0)
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 Reject: RequestType.Value = Value(4)
val Cancel: RequestType.Value = Value(5)
val Unk6: RequestType.Value = Value(6) // 6 and 7 seen as failed decodes, validity unknown
val Unk7: RequestType.Value = Value(7)
val Kick: RequestType.Value = Value(6)
val SetRank: RequestType.Value = Value(7)
implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uintL(3))
}
@ -206,15 +241,15 @@ object OutfitMembershipRequest extends Marshallable[OutfitMembershipRequest] {
import scala.annotation.switch
((code: @switch) match {
case 0 => CreateOutfitCodec
case 1 => FormOutfitCodec // so far same as Create
case 2 => Unk2Codec
case 3 => AcceptOutfitCodec
case 4 => RejectOutfitCodec // so far same as Accept
case 5 => CancelOutfitCodec
case 6 => unknownCodec(action = code)
case 7 => unknownCodec(action = code)
// 3 bit limit
case 0 => CreateCodec
case 1 => FormCodec // so far same as Create
case 2 => InviteCodec
case 3 => AcceptInviteCodec
case 4 => RejectInviteCodec
case 5 => CancelInviteCodec
case 6 => KickCodec
case 7 => SetRankCodec
case _ => failureCodec(code)
}).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, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.PlanetSideGUID
import scodec.{Attempt, Codec, Err}
import scodec.bits.BitVector
import scodec.codecs._
@ -12,10 +11,9 @@ import shapeless.{::, HNil}
final case class OutfitMembershipResponse(
response_type: OutfitMembershipResponse.ResponseType.Type,
unk0: Int,
unk1: Int,
outfit_id: Long,
target_guid: PlanetSideGUID,
unk3: Int,
//unk4: Boolean,
target_id: Long,
action: OutfitMembershipResponseAction
) extends PlanetSideGamePacket {
type Packet = OutfitMembershipResponse
@ -28,21 +26,39 @@ final case class OutfitMembershipResponse(
abstract class OutfitMembershipResponseAction(val code: Int)
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 Unk6OutfitResponse() extends OutfitMembershipResponseAction(code = 6)
final case class Unk7OutfitResponse() extends OutfitMembershipResponseAction(code = 7)
final case class Unk4OutfitResponse(
unk5: Int,
unk6: Int,
outfit_name: String
) extends OutfitMembershipResponseAction(code = 4)
final case class Unknown(badCode: Int, data: BitVector) extends OutfitMembershipResponseAction(badCode)
@ -53,17 +69,32 @@ object OutfitMembershipResponseAction {
object Codecs {
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.encodedWideString ::
PacketHelpers.encodedWideString
).xmap[CreateOutfitResponse](
).xmap[CreateResponse](
{
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
}
)
@ -99,7 +130,7 @@ object OutfitMembershipResponseAction {
)
val Unk3OutfitCodec: Codec[Unk3OutfitResponse] =
PacketHelpers.encodedWideString.xmap[Unk3OutfitResponse](
PacketHelpers.encodedWideStringAligned(5).xmap[Unk3OutfitResponse](
{
case unk2 =>
Unk3OutfitResponse(unk2)
@ -110,8 +141,11 @@ object OutfitMembershipResponseAction {
}
)
val Unk4OutfitCodec: Codec[Unk4OutfitResponse] =
(uint16L :: uint16L :: PacketHelpers.encodedWideStringAligned(5)).xmap[Unk4OutfitResponse](
val Unk4OutfitCodec: Codec[Unk4OutfitResponse] = (
uint16L ::
uint16L ::
PacketHelpers.encodedWideStringAligned(5)
).xmap[Unk4OutfitResponse](
{
case unk5 :: unk6 :: outfit_name :: HNil =>
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.
* @param action the action behavior code
@ -206,14 +204,24 @@ object OutfitMembershipResponse extends Marshallable[OutfitMembershipResponse] {
import scala.annotation.switch
((code: @switch) match {
case 0 => Unk0OutfitCodec // 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)
case 0 => UniversalResponseCodec
case 1 => UniversalResponseCodec
case 2 => UniversalResponseCodec
case 3 => UniversalResponseCodec
case 4 => UniversalResponseCodec
case 5 => UniversalResponseCodec
case 6 => UniversalResponseCodec
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
case _ => failureCodec(code)
}).asInstanceOf[Codec[OutfitMembershipResponseAction]]
@ -221,21 +229,20 @@ object OutfitMembershipResponse extends Marshallable[OutfitMembershipResponse] {
implicit val codec: Codec[OutfitMembershipResponse] = (
("response_type" | ResponseType.codec) >>:~ { response_type =>
("unk0" | uint8L) ::
("unk0" | uintL(5)) ::
("unk1" | uintL(3)) ::
("outfit_id" | uint32L) ::
("target_guid" | PlanetSideGUID.codec) ::
("unk3" | uint16L) ::
//("unk4" | bool) ::
("action" | selectFromType(response_type.id))
("target_id" | uint32L) ::
("action" | selectFromType(response_type.id))
}
).xmap[OutfitMembershipResponse](
{
case response_type :: u0 :: outfit_id :: target_guid :: u3 :: action :: HNil =>
OutfitMembershipResponse(response_type, u0, outfit_id, target_guid, u3, action)
case response_type :: u0 :: u1 :: outfit_id :: target_id :: action :: HNil =>
OutfitMembershipResponse(response_type, u0, u1, outfit_id, target_id, action)
},
{
case OutfitMembershipResponse(response_type, u0, outfit_id, u2, u3, action) =>
response_type :: u0 :: outfit_id :: u2 :: u3 :: action :: HNil
case OutfitMembershipResponse(response_type, u0, u1, outfit_id, target_id, action) =>
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.OutfitEventAction._
import net.psforever.packet.game._
import net.psforever.types.PlanetSideGUID
import org.specs2.mutable._
import scodec.bits._
@ -64,7 +63,9 @@ class OutfitEventTest extends Specification {
unk9 = 0,
OutfitRankNames("Dog Meat","Russian","","","Squad Leaders","Acting Commanders","Reapers",""),
"\\#0000ffMumble \\#0033ffInfo \\#0066ffis \\#0099ffthemoose.typefrag.com \\#00ccffport \\#00ffff9350 \\#00ccffjoin \\#0099ffit \\#0066ffor \\#0033ffbe \\#0000ffkicked.",
PlanetSideGUID(32783),
15,
128,
0,
0,
0,
0,
@ -93,7 +94,9 @@ class OutfitEventTest extends Specification {
unk9 = 0,
OutfitRankNames("Dog Meat","Russian","","","Squad Leaders","Acting Commanders","Reapers",""),
"\\#0000ffMumble \\#0033ffInfo \\#0066ffis \\#0099ffthemoose.typefrag.com \\#00ccffport \\#00ffff9350 \\#00ccffjoin \\#0099ffit \\#0066ffor \\#0033ffbe \\#0000ffkicked.",
PlanetSideGUID(32783),
15,
128,
0,
0,
0,
0,
@ -151,8 +154,10 @@ class OutfitEventTest extends Specification {
unk9 = 0,
OutfitRankNames("","","","","","","",""),
"",
PlanetSideGUID(28672),
33353,
0,
112,
73,
130,
0,
0,
0,
@ -179,15 +184,17 @@ class OutfitEventTest extends Specification {
unk9 = 0,
OutfitRankNames("","","","","","","",""),
"",
PlanetSideGUID(28672),
33353,
0,
0,
112,
73,
130,
0,
0,
0,
0,
0,
0,
0
)
)
)

View file

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

View file

@ -10,42 +10,29 @@ import scodec.bits._
class OutfitMemberEventTest extends Specification {
//val unk0_ABC: ByteVector = hex"90 3518 4000 1a4e 4100 2 180 450078007000650072007400 8483 07e0 119d bfe0 70" // 0x90048640001030c28022404c0061007a00650072003100390038003200f43a45e00b4c604010
val unk0_ABC_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 Billy = hex"90 0 4864 0003 a41a 280a 20 0 620069006c006c007900320035003600 935f 6000 186a b040 50"
val Lazer = hex"90 0 4864 0001 030c 2802 24 0 4c0061007a00650072003100390038003200 e6dc 25a0 153e 6040 10"
val Virus = hex"90 0 4864 0002 1b64 4c02 28 0 5600690072007500730047006900760065007200 2f89 0080 0000 0000 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 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 Lazer = hex"90 0 4864 0001 030c 2802 24 0 4c0061007a00650072003100390038003200 e6dc 25a0 153e 6040 10"
val Virus = hex"90 0 4864 0002 1b64 4c02 28 0 5600690072007500730047006900760065007200 2f89 0080 0000 0000 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"
/*
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))
*/
val Unk0 = hex"90 5 40542002 3f61e808 0"
"decode Unk0 ABC" in {
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) =>
unk00 mustEqual 0
outfit_guid mustEqual 6418L
unk3 mustEqual 49984
unk5 mustEqual 10
case OutfitMemberEvent(action, outfit_id, member_id, member_name, rank, points, last_login, action2, padding) =>
action mustEqual 0
outfit_id mustEqual 6418L
member_id mustEqual 705344
member_name mustEqual "Lazer1982"
unk8 mustEqual 244
unk9 mustEqual 58
unk10 mustEqual 69
unk11 mustEqual 224
unk12 mustEqual 11
unk13 mustEqual 76
unk14 mustEqual 96
unk15 mustEqual 64
unk16 mustEqual 16
rank mustEqual 7
points mustEqual 3134113
last_login mustEqual 156506
action2 mustEqual 1
padding mustEqual ByteVector(0x0)
case _ =>
ko
}
@ -53,20 +40,15 @@ val unk0_ABC_Lazer: ByteVector = hex"90 048640001030c28022404c0061007a00650
"encode Unk0 ABC" in {
val msg = OutfitMemberEvent(
unk00 = 0,
action = 0,
outfit_id = 6418L,
unk3 = 49984,
unk5 = 10,
member_id = 705344,
member_name = "Lazer1982",
unk8 = 244,
unk9 = 58,
unk10 = 69,
unk11 = 224,
unk12 = 11,
unk13 = 76,
unk14 = 96,
unk15 = 64,
unk16 = 16,
rank = 7,
points = 3134113,
last_login = 156506,
action2 = 1,
ByteVector.empty
)
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 form_abc = hex"8c 2 0200 000 1000 83 610062006300"
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 accept_1 = hex"8c 6 0200 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_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 {
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
avatar_id mustEqual 1
action mustEqual CreateOutfit("", 0, unk4 = false, "ABC")
outfit_id mustEqual 1
action mustEqual Create("", "ABC")
case _ =>
ko
}
}
"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
pkt mustEqual create_ABC
@ -43,17 +48,17 @@ class OutfitMembershipRequestTest extends Specification {
"decode CreateOutfit 2222" in {
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
avatar_id mustEqual 8
action mustEqual CreateOutfit("", 0, unk4 = false, "2222")
outfit_id mustEqual 8
action mustEqual Create("", "2222")
case _ =>
ko
}
}
"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
pkt mustEqual create_2222
@ -61,17 +66,17 @@ class OutfitMembershipRequestTest extends Specification {
"decode FormOutfit abc" in {
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
avatar_id mustEqual 1
action mustEqual FormOutfit("", 0, unk4 = false, "abc")
outfit_id mustEqual 1
action mustEqual Form("", "abc")
case _ =>
ko
}
}
"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
pkt mustEqual form_abc
@ -79,53 +84,53 @@ class OutfitMembershipRequestTest extends Specification {
"decode FormOutfit 1" in {
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
avatar_id mustEqual 8
action mustEqual FormOutfit("", 0, unk4 = false, "1")
outfit_id mustEqual 8
action mustEqual Form("", "1")
case _ =>
ko
}
}
"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
pkt mustEqual form_1
}
"decode Unk2" in {
PacketCoding.decodePacket(unk2).require match {
"decode Invite" in {
PacketCoding.decodePacket(invite_old).require match {
case OutfitMembershipRequest(request_type, outfit_id, action) =>
request_type mustEqual RequestType.Unk2
request_type mustEqual RequestType.Invite
outfit_id mustEqual 30383325L
action mustEqual Unk2(0, 0, "virusgiver")
action mustEqual Invite(0, "virusgiver")
case _ =>
ko
}
}
"encode Unk2" in {
val msg = OutfitMembershipRequest(RequestType.Unk2, 30383325L, Unk2(0, 0, "virusgiver"))
"encode Invite" in {
val msg = OutfitMembershipRequest(RequestType.Invite, 30383325L, Invite(0, "virusgiver"))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual unk2
pkt mustEqual invite_old
}
"decode AcceptOutfitInvite 1" in {
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
avatar_id mustEqual 1
action mustEqual AcceptOutfitInvite("")
outfit_id mustEqual 1
action mustEqual AcceptInvite("")
case _ =>
ko
}
}
"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
pkt mustEqual accept_1
@ -133,17 +138,17 @@ class OutfitMembershipRequestTest extends Specification {
"decode AcceptOutfitInvite 2" in {
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
avatar_id mustEqual 2
action mustEqual AcceptOutfitInvite("")
outfit_id mustEqual 2
action mustEqual AcceptInvite("")
case _ =>
ko
}
}
"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
pkt mustEqual accept_2
@ -151,17 +156,17 @@ class OutfitMembershipRequestTest extends Specification {
"decode RejectOutfitInvite 1" in {
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
avatar_id mustEqual 1
action mustEqual RejectOutfitInvite("")
outfit_id mustEqual 1
action mustEqual RejectInvite("")
case _ =>
ko
}
}
"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
pkt mustEqual reject_1
@ -169,17 +174,17 @@ class OutfitMembershipRequestTest extends Specification {
"decode RejectOutfitInvite 2" in {
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
avatar_id mustEqual 2
action mustEqual RejectOutfitInvite("")
outfit_id mustEqual 2
action mustEqual RejectInvite("")
case _ =>
ko
}
}
"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
pkt mustEqual reject_2
@ -187,17 +192,17 @@ class OutfitMembershipRequestTest extends Specification {
"decode CancelOutfitInvite 3" in {
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
avatar_id mustEqual 3
action mustEqual CancelOutfitInvite(0, 0, "")
outfit_id mustEqual 3
action mustEqual CancelInvite(0, "")
case _ =>
ko
}
}
"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
pkt mustEqual cancel_3
@ -205,17 +210,17 @@ class OutfitMembershipRequestTest extends Specification {
"decode CancelOutfitInvite 1 abc" in {
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
avatar_id mustEqual 1
action mustEqual CancelOutfitInvite(0, 0, "abc")
outfit_id mustEqual 1
action mustEqual CancelInvite(0, "abc")
case _ =>
ko
}
}
"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
pkt mustEqual cancel_1_abc
@ -223,19 +228,81 @@ class OutfitMembershipRequestTest extends Specification {
"decode CancelOutfitInvite 3 def" in {
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
avatar_id mustEqual 3
action mustEqual CancelOutfitInvite(0, 0, "def")
outfit_id mustEqual 3
action mustEqual CancelInvite(0, "def")
case _ =>
ko
}
}
"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
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
}
}