Merge pull request #1095 from Fate-JH/outfit-request

Packet: OutfitRequest
This commit is contained in:
Fate-JH 2023-05-30 13:02:56 -04:00 committed by GitHub
commit 006dd201ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 283 additions and 1 deletions

View file

@ -591,6 +591,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case packet: HitHint =>
sessionFuncs.handleHitHint(packet)
case _: OutfitRequest => ()
case pkt =>
log.warning(s"Unhandled GamePacket $pkt")
}

View file

@ -470,7 +470,7 @@ object GamePacketOpcode extends Enumeration {
case 0x8b => noDecoder(UnknownMessage139)
case 0x8c => noDecoder(OutfitMembershipRequest)
case 0x8d => noDecoder(OutfitMembershipResponse)
case 0x8e => noDecoder(OutfitRequest)
case 0x8e => game.OutfitRequest.decode
case 0x8f => noDecoder(OutfitEvent)
// OPCODES 0x90-9f

View file

@ -0,0 +1,177 @@
// Copyright (c) 2023 PSForever
package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import scodec.{Attempt, Codec, Err}
import scodec.bits.ByteVector
import scodec.codecs._
import shapeless.{::, HNil}
/**
* na
*/
abstract class OutfitRequestForm(val code: Int)
object OutfitRequestForm {
/**
* na
* @param str na
*/
final case class Unk0(str: String) extends OutfitRequestForm(code = 0)
/**
* na
* @param list na
*/
final case class Unk1(list: List[Option[String]]) extends OutfitRequestForm(code = 1)
/**
* na
* @param unk na
*/
final case class Unk2(unk: Int) extends OutfitRequestForm(code = 2)
/**
* na
* @param unk na
*/
final case class Unk3(unk: Boolean) extends OutfitRequestForm(code = 3)
/**
* na
* @param unk na
*/
final case class Unk4(unk: Boolean) extends OutfitRequestForm(code = 4)
/**
* na
* @param unk na
*/
final case class Fail(unk: ByteVector) extends OutfitRequestForm(code = -1)
}
/**
* na
* @param id na
* @param info na
*/
final case class OutfitRequest(id: Long, info: OutfitRequestForm)
extends PlanetSideGamePacket {
type Packet = OrbitalStrikeWaypointMessage
def opcode = GamePacketOpcode.OutfitRequest
def encode = OutfitRequest.encode(this)
}
object OutfitRequest extends Marshallable[OutfitRequest] {
/**
* na
*/
private val unk0Codec: Codec[OutfitRequestForm] = PacketHelpers.encodedWideStringAligned(adjustment = 5).hlist
.xmap[OutfitRequestForm] (
{
case value :: HNil => OutfitRequestForm.Unk0(value)
},
{
case OutfitRequestForm.Unk0(value) => value :: HNil
}
)
/**
* na
*/
private val unk1Codec: Codec[OutfitRequestForm] = unk1PaddedEntryCodec(len = 8, pad = 5).xmap[OutfitRequestForm] (
list => OutfitRequestForm.Unk1(list),
{
case OutfitRequestForm.Unk1(list) => list
}
)
/**
* na
*/
private def unk1PaddedEntryCodec(len: Int, pad: Int): Codec[List[Option[String]]] =
{
val nextPad = if (pad == 0) 7 else pad - 1
optional(bool, PacketHelpers.encodedWideStringAligned(nextPad)) >>:~ { strOpt =>
(strOpt match {
case None if len > 1 => unk1PaddedEntryCodec(len - 1, nextPad)
case Some(_) if len > 1 => unk1PaddedEntryCodec(len - 1, pad = 8)
case _ => PacketHelpers.listOfNSized(size = 0L, optional(bool, PacketHelpers.encodedWideString))
}).hlist
}
}.xmap[List[Option[String]]](
{
case head :: tail :: HNil => head +: tail
},
list => list.head :: list.tail :: HNil
)
/**
* na
*/
private val unk2Codec: Codec[OutfitRequestForm] = uint8.hlist.xmap[OutfitRequestForm] (
{
case value :: HNil => OutfitRequestForm.Unk2(value)
},
{
case OutfitRequestForm.Unk2(value) => value :: HNil
}
)
/**
* na
*/
private val unk3Codec: Codec[OutfitRequestForm] = bool.hlist.xmap[OutfitRequestForm] (
{
case value :: HNil => OutfitRequestForm.Unk3(value)
},
{
case OutfitRequestForm.Unk3(value) => value :: HNil
}
)
/**
* na
*/
private val unk4Codec: Codec[OutfitRequestForm] = bool.hlist.xmap[OutfitRequestForm] (
{
case value :: HNil => OutfitRequestForm.Unk4(value)
},
{
case OutfitRequestForm.Unk4(value) => value :: HNil
}
)
/**
* na
*/
private def failCodec(code: Int): Codec[OutfitRequestForm] = conditional(included = false, bool).exmap[OutfitRequestForm](
_ => Attempt.Failure(Err(s"can not decode $code-type info - what is this thing?")),
_ => Attempt.Failure(Err(s"can not encode $code-type info - no such thing"))
)
/**
* na
*/
private def infoCodec(code: Int): Codec[OutfitRequestForm] = {
code match {
case 0 => unk0Codec
case 1 => unk1Codec
case 2 => unk2Codec
case 3 => unk3Codec
case 4 => unk4Codec
case _ => failCodec(code)
}
}
implicit val codec: Codec[OutfitRequest] = (
uint(bits = 3) >>:~ { code =>
("id" | uint32L) ::
("info" | infoCodec(code))
}
).xmap[OutfitRequest](
{
case _:: id:: info :: HNil =>
OutfitRequest(id, info)
},
{
case OutfitRequest(id, info) =>
info.code :: id :: info :: HNil
}
)
}

View file

@ -0,0 +1,103 @@
// Copyright (c) 2023 PSForever
package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
import net.psforever.types.PlanetSideGUID
import scodec.bits._
class OutfitRequestTest extends Specification {
val string0 = hex"8e02b54f40401780560061006e00750020006f0075007400660069007400200066006f0072002000740068006500200070006c0061006e00650074007300690064006500200066006f00720065007600650072002000700072006f006a006500630074002100200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002000200020002d00660069006e00640020006f007500740020006d006f00720065002000610062006f0075007400200074006800650020005000530045004d0055002000700072006f006a0065006300740020006100740020005000530066006f00720065007600650072002e006e0065007400"
val string2 = hex"8e22b54f405800c000c000c000c000c000c000c000"
val string4 = hex"8e42b54f404aa0" //faked by modifying the previous example
val string6 = hex"8e649e822010"
val string8 = hex"8e81b2cf4050"
"decode 0" in {
PacketCoding.decodePacket(string0).require match {
case OutfitRequest(id, OutfitRequestForm.Unk0(str)) =>
id mustEqual 41593365L
str mustEqual "Vanu outfit for the planetside forever project! -find out more about the PSEMU project at PSforever.net"
case _ =>
ko
}
}
"decode 1" in {
PacketCoding.decodePacket(string2).require match {
case OutfitRequest(id, OutfitRequestForm.Unk1(list)) =>
id mustEqual 41593365L
list mustEqual List(Some(""), Some(""), Some(""), Some(""), Some(""), Some(""), Some(""), Some(""))
case _ =>
ko
}
}
"decode 2 (fake)" in {
PacketCoding.decodePacket(string4).require match {
case OutfitRequest(id, OutfitRequestForm.Unk2(value)) =>
id mustEqual 41593365L
value mustEqual 85
case _ =>
ko
}
}
"decode 3" in {
PacketCoding.decodePacket(string6).require match {
case OutfitRequest(id, OutfitRequestForm.Unk3(value)) =>
id mustEqual 1176612L
value mustEqual true
case _ =>
ko
}
}
"decode 4" in {
PacketCoding.decodePacket(string8).require match {
case OutfitRequest(id, OutfitRequestForm.Unk4(value)) =>
id mustEqual 41588237L
value mustEqual true
case _ =>
ko
}
}
"encode 0" in {
val msg = OutfitRequest(41593365L, OutfitRequestForm.Unk0(
"Vanu outfit for the planetside forever project! -find out more about the PSEMU project at PSforever.net"
))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual string0
}
"encode 1" in {
val msg = OutfitRequest(41593365L, OutfitRequestForm.Unk1(List(Some(""), Some(""), Some(""), Some(""), Some(""), Some(""), Some(""), Some(""))))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual string2
}
"encode 2 (fake)" in {
val msg = OutfitRequest(41593365L, OutfitRequestForm.Unk2(85))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual string4
}
"encode 3" in {
val msg = OutfitRequest(1176612L, OutfitRequestForm.Unk3(true))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual string6
}
"encode 4" in {
val msg = OutfitRequest(41588237L, OutfitRequestForm.Unk4(true))
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual string8
}
}