-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1160 from Resaec/outfit_membership_request_packet
OutfitMembershipRequest packet
- Loading branch information
Showing
4 changed files
with
436 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
184 changes: 184 additions & 0 deletions
184
src/main/scala/net/psforever/packet/game/OutfitMembershipRequest.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
// Copyright (c) 2023 PSForever | ||
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._ | ||
import shapeless.{::, HNil} | ||
|
||
final case class OutfitMembershipRequest( | ||
request_type: OutfitMembershipRequest.RequestType.Type, | ||
avatar_guid: PlanetSideGUID, | ||
unk1: Int, | ||
action: OutfitAction | ||
) extends PlanetSideGamePacket { | ||
type Packet = OutfitMembershipRequest | ||
|
||
def opcode: Type = GamePacketOpcode.OutfitMembershipRequest | ||
|
||
def encode: Attempt[BitVector] = OutfitMembershipRequest.encode(this) | ||
} | ||
|
||
abstract class OutfitAction(val code: Int) | ||
object OutfitAction { | ||
|
||
final case class CreateOutfit(unk2: String, unk3: Int, unk4: Boolean, outfit_name: String) extends OutfitAction(code = 0) | ||
|
||
final case class FormOutfit(unk2: String, unk3: Int, unk4: Boolean, outfit_name: String) extends OutfitAction(code = 1) | ||
|
||
final case class AcceptOutfitInvite(unk2: String) extends OutfitAction(code = 3) | ||
|
||
final case class RejectOutfitInvite(unk2: String) extends OutfitAction(code = 4) | ||
|
||
final case class CancelOutfitInvite(unk5: Int, unk6: Int, outfit_name: String) extends OutfitAction(code = 5) | ||
|
||
final case class Unknown(badCode: Int, data: BitVector) extends OutfitAction(badCode) | ||
|
||
/** | ||
* The `Codec`s used to transform the input stream into the context of a specific action | ||
* and extract the field data from that stream. | ||
*/ | ||
object Codecs { | ||
private val everFailCondition = conditional(included = false, bool) | ||
|
||
val CreateOutfitCodec: Codec[CreateOutfit] = | ||
(PacketHelpers.encodedWideString :: uint4L :: bool :: PacketHelpers.encodedWideString).xmap[CreateOutfit]( | ||
{ | ||
case unk2 :: unk3 :: unk4 :: outfit_name :: HNil => | ||
CreateOutfit(unk2, unk3, unk4, outfit_name) | ||
}, | ||
{ | ||
case CreateOutfit(unk2, unk3, unk4, outfit_name) => | ||
unk2 :: unk3 :: unk4 :: outfit_name :: HNil | ||
} | ||
) | ||
|
||
val FormOutfitCodec: Codec[FormOutfit] = | ||
(PacketHelpers.encodedWideString :: uint4L :: bool :: PacketHelpers.encodedWideString).xmap[FormOutfit]( | ||
{ | ||
case unk2 :: unk3 :: unk4 :: outfit_name :: HNil => | ||
FormOutfit(unk2, unk3, unk4, outfit_name) | ||
}, | ||
{ | ||
case FormOutfit(unk2, unk3, unk4, outfit_name) => | ||
unk2 :: unk3 :: unk4 :: outfit_name :: HNil | ||
} | ||
) | ||
|
||
val AcceptOutfitCodec: Codec[AcceptOutfitInvite] = | ||
PacketHelpers.encodedWideString.xmap[AcceptOutfitInvite]( | ||
{ | ||
case unk2 => | ||
AcceptOutfitInvite(unk2) | ||
}, | ||
{ | ||
case AcceptOutfitInvite(unk2) => | ||
unk2 | ||
} | ||
) | ||
|
||
val RejectOutfitCodec: Codec[RejectOutfitInvite] = | ||
PacketHelpers.encodedWideString.xmap[RejectOutfitInvite]( | ||
{ | ||
case unk2 => | ||
RejectOutfitInvite(unk2) | ||
}, | ||
{ | ||
case RejectOutfitInvite(unk2) => | ||
unk2 | ||
} | ||
) | ||
|
||
val CancelOutfitCodec: Codec[CancelOutfitInvite] = | ||
(uint16L :: uint16L :: PacketHelpers.encodedWideStringAligned(5)).xmap[CancelOutfitInvite]( | ||
{ | ||
case unk5 :: unk6 :: outfit_name :: HNil => | ||
CancelOutfitInvite(unk5, unk6, outfit_name) | ||
}, | ||
{ | ||
case CancelOutfitInvite(unk5, unk6, outfit_name) => | ||
unk5 :: unk6 :: outfit_name :: HNil | ||
} | ||
) | ||
|
||
/** | ||
* A common form for known action code indexes with an unknown purpose and transformation is an "Unknown" object. | ||
* @param action the action behavior code | ||
* @return a transformation between the action code and the unknown bit data | ||
*/ | ||
def unknownCodec(action: Int): Codec[Unknown] = | ||
bits.xmap[Unknown]( | ||
data => Unknown(action, data), | ||
{ | ||
case Unknown(_, data) => data | ||
} | ||
) | ||
|
||
/** | ||
* The action code was completely unanticipated! | ||
* @param action the action behavior code | ||
* @return nothing; always fail | ||
*/ | ||
def failureCodec(action: Int): Codec[OutfitAction] = | ||
everFailCondition.exmap[OutfitAction]( | ||
_ => Attempt.failure(Err(s"can not match a codec pattern for decoding $action")), | ||
_ => Attempt.failure(Err(s"can not match a codec pattern for encoding $action")) | ||
) | ||
} | ||
} | ||
|
||
object OutfitMembershipRequest extends Marshallable[OutfitMembershipRequest] { | ||
|
||
object RequestType extends Enumeration { | ||
type Type = Value | ||
|
||
val Create: RequestType.Value = Value(0) | ||
val Form: RequestType.Value = Value(1) | ||
val Unk2: 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) | ||
|
||
implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uintL(3)) | ||
} | ||
|
||
private def selectFromType(code: Int): Codec[OutfitAction] = { | ||
import OutfitAction.Codecs._ | ||
import scala.annotation.switch | ||
|
||
((code: @switch) match { | ||
case 0 => CreateOutfitCodec | ||
case 1 => FormOutfitCodec // so far same as Create | ||
case 2 => unknownCodec(action = code) | ||
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 _ => failureCodec(code) | ||
}).asInstanceOf[Codec[OutfitAction]] | ||
} | ||
|
||
implicit val codec: Codec[OutfitMembershipRequest] = ( | ||
("request_type" | RequestType.codec) >>:~ { request_type => | ||
("avatar_guid" | PlanetSideGUID.codec) :: | ||
("unk1" | uint16L) :: | ||
("action" | selectFromType(request_type.id)) | ||
} | ||
).xmap[OutfitMembershipRequest]( | ||
{ | ||
case request_type :: avatar_guid :: u1 :: action :: HNil => | ||
OutfitMembershipRequest(request_type, avatar_guid, u1, action) | ||
}, | ||
{ | ||
case OutfitMembershipRequest(request_type, avatar_guid, u1, action) => | ||
request_type :: avatar_guid :: u1 :: action :: HNil | ||
} | ||
) | ||
} |
Oops, something went wrong.