a lot of squad-relatd setup for little payoff; initial packet and test for SquadMemberEvent, originally by Aphedox; initial packet and tests for SquadState; new SquadDefinitionAction actions; a common initial SquadDetailDefinitionUpdate form

This commit is contained in:
FateJH 2019-06-10 16:26:31 -04:00
parent 9cf1cd92cc
commit 14bdcb7a7e
8 changed files with 499 additions and 39 deletions

View file

@ -450,14 +450,14 @@ object GamePacketOpcode extends Enumeration {
case 0x6f => game.SquadMembershipResponse.decode
// OPCODES 0x70-7f
case 0x70 => noDecoder(SquadMemberEvent)
case 0x70 => game.SquadMemberEvent.decode
case 0x71 => noDecoder(PlatoonEvent)
case 0x72 => game.FriendsRequest.decode
case 0x73 => game.FriendsResponse.decode
case 0x74 => game.TriggerEnvironmentalDamageMessage.decode
case 0x75 => game.TrainingZoneMessage.decode
case 0x76 => game.DeployableObjectsInfoMessage.decode
case 0x77 => noDecoder(SquadState)
case 0x77 => game.SquadState.decode
// 0x78
case 0x78 => game.OxygenStateMessage.decode
case 0x79 => noDecoder(TradeMessage)

View file

@ -18,6 +18,8 @@ abstract class SquadAction(val code : Int)
object SquadAction{
final case class DisplaySquad() extends SquadAction(0)
final case class AnswerSquadJoinRequest() extends SquadAction(1)
final case class SaveSquadDefinition() extends SquadAction(3)
final case class LoadSquadDefinition() extends SquadAction(4)
@ -74,6 +76,13 @@ object SquadAction{
}
)
val answerSquadJoinRequestCodec = everFailCondition.xmap[AnswerSquadJoinRequest] (
_ => AnswerSquadJoinRequest(),
{
case AnswerSquadJoinRequest() => None
}
)
val saveSquadDefinitionCodec = everFailCondition.xmap[SaveSquadDefinition] (
_ => SaveSquadDefinition(),
{
@ -259,7 +268,7 @@ object SquadAction{
* The following formats are translated; their purposes are listed:<br>
* &nbsp;&nbsp;`(None)`<br>
* &nbsp;&nbsp;&nbsp;&nbsp;`0 ` - Display Squad<br>
* &nbsp;&nbsp;&nbsp;&nbsp;`1 ` - UNKNOWN<br>
* &nbsp;&nbsp;&nbsp;&nbsp;`1 ` - Answer Squad Join Request<br>
* &nbsp;&nbsp;&nbsp;&nbsp;`2 ` - UNKNOWN<br>
* &nbsp;&nbsp;&nbsp;&nbsp;`3 ` - Save Squad Definition<br>
* &nbsp;&nbsp;&nbsp;&nbsp;`4 ` - Load Squad Definition<br>
@ -333,6 +342,7 @@ object SquadDefinitionActionMessage extends Marshallable[SquadDefinitionActionMe
import scala.annotation.switch
((code : @switch) match {
case 0 => displaySquadCodec
case 1 => answerSquadJoinRequestCodec
case 3 => saveSquadDefinitionCodec
case 4 => loadSquadDefinitionCodec
case 7 => listSquadDefinitionCodec
@ -353,7 +363,7 @@ object SquadDefinitionActionMessage extends Marshallable[SquadDefinitionActionMe
case 35 => cancelSquadSearchCodec
case 40 => findLfsSoldiersForRoleCodec
case 41 => cancelFindCodec
case 1 | 2 | 6 | 9 | 11 |
case 2 | 6 | 9 | 11 |
12 | 13 | 14 | 16 | 17 |
18 | 29 | 30 | 32 | 33 |
36 | 37 | 38 | 42 | 43 => unknownCodec(code)

View file

@ -62,6 +62,25 @@ object SquadDetailDefinitionUpdateMessage extends Marshallable[SquadDetailDefini
CertificationType.AgileExoSuit
)
final val Init = SquadDetailDefinitionUpdateMessage(
PlanetSideGUID(0),
"",
"",
PlanetSideZoneID(0),
List(
SquadPositionDetail(),
SquadPositionDetail(),
SquadPositionDetail(),
SquadPositionDetail(),
SquadPositionDetail(),
SquadPositionDetail(),
SquadPositionDetail(),
SquadPositionDetail(),
SquadPositionDetail(),
SquadPositionDetail()
)
)
def apply(guid : PlanetSideGUID, leader_name : String, task : String, zone_id : PlanetSideZoneID, member_info : List[SquadPositionDetail]) : SquadDetailDefinitionUpdateMessage = {
import scodec.bits._
SquadDetailDefinitionUpdateMessage(guid, hex"080000000000000000000".toBitVector, leader_name, task, zone_id, member_info)

View file

@ -0,0 +1,61 @@
// Copyright (c) 2019 PSForever
package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import scodec.{Attempt, Codec, Err}
import scodec.codecs._
import shapeless.{::, HNil}
final case class SquadMemberEvent(unk1 : Int,
unk2 : Int,
unk3 : Long,
unk4 : Int,
unk5 : Option[String],
unk6 : Option[Int],
unk7 : Option[Long])
extends PlanetSideGamePacket {
type Packet = SquadMemberEvent
def opcode = GamePacketOpcode.SquadMemberEvent
def encode = SquadMemberEvent.encode(this)
}
object SquadMemberEvent extends Marshallable[SquadMemberEvent] {
def apply(unk1 : Int, unk2 : Int, unk3 : Long, unk4 : Int) : SquadMemberEvent =
SquadMemberEvent(unk1, unk2, unk3, unk4, None, None, None)
def apply(unk2 : Int, unk3 : Long, unk4 : Int, unk5 : String, unk6 : Int, unk7 : Long) : SquadMemberEvent =
SquadMemberEvent(0, unk2, unk3, unk4, Some(unk5), Some(unk6), Some(unk7))
def apply(unk2 : Int, unk3 : Long, unk4 : Int, unk6 : Int) : SquadMemberEvent =
SquadMemberEvent(3, unk2, unk3, unk4, None, Some(unk6), None)
def apply(unk2 : Int, unk3 : Long, unk4 : Int, unk7 : Long) : SquadMemberEvent =
SquadMemberEvent(4, unk2, unk3, unk4, None, None, Some(unk7))
implicit val codec : Codec[SquadMemberEvent] = (
("unk1" | uintL(3)) >>:~ { unk1 =>
("unk2" | uint16L) ::
("unk3" | uint32L) ::
("unk4" | uintL(4)) ::
conditional(unk1 == 0, "unk5" | PacketHelpers.encodedWideStringAligned(1)) ::
conditional(unk1 == 0 || unk1 == 3, "unk6" | uint16L) ::
conditional(unk1 == 0 || unk1 == 4, "unk7" | uint32L)
}).exmap[SquadMemberEvent] (
{
case unk1 :: unk2 :: unk3 :: unk4 :: unk5 :: unk6 :: unk7 :: HNil =>
Attempt.Successful(SquadMemberEvent(unk1, unk2, unk3, unk4, unk5, unk6, unk7))
},
{
case data @ SquadMemberEvent(0, unk2, unk3, unk4, Some(unk5), Some(unk6), Some(unk7)) =>
Attempt.Successful(0 :: unk2 :: unk3 :: unk4 :: Some(unk5) :: Some(unk6) :: Some(unk7) :: HNil)
case data @ SquadMemberEvent(3, unk2, unk3, unk4, None, Some(unk6), None) =>
Attempt.Successful(3 :: unk2 :: unk3 :: unk4 :: None :: Some(unk6) :: None :: HNil)
case data @ SquadMemberEvent(4, unk2, unk3, unk4, None, None, Some(unk7)) =>
Attempt.Successful(4 :: unk2 :: unk3 :: unk4 :: None :: None :: Some(unk7) :: HNil)
case data @ SquadMemberEvent(unk1, unk2, unk3, unk4, None, None, None) =>
Attempt.Successful(unk1 :: unk2 :: unk3 :: unk4 :: None :: None :: None :: HNil)
case data =>
Attempt.Failure(Err(s"SquadMemberEvent can not encode with this pattern - $data"))
}
)
}

View file

@ -0,0 +1,76 @@
// Copyright (c) 2019 PSForever
package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
import net.psforever.types.Vector3
import scodec.{Attempt, Codec, Err}
import scodec.codecs._
import shapeless.{::, HNil}
final case class SquadStateInfo(char_id : Long,
unk2 : Int,
unk3 : Int,
pos : Vector3,
unk4 : Int,
unk5 : Int,
unk6 : Boolean,
unk7 : Int,
unk8 : Option[Int],
unk9 : Option[Boolean])
//7704001646c7a02810050a1a2bc97842280000
// SquadState(PlanetSideGUID(4),List(SquadStateInfo(1684830722,64,64,Vector3(3152.1562,3045.0781,35.515625),2,2,false,0,None,None)))
//770700342a28c028100420d60e9df8c2800000eab58a028100ce514b655ddc341400286d9130000021eb951539f4c4050800
// SquadState(PlanetSideGUID(7),List(SquadStateInfo(1117948930,64,64,Vector3(2822.125,3919.0234,43.546875),0,0,false,0,None,None), SquadStateInfo(3937765890,64,64,Vector3(2856.3984,3882.3516,53.859375),0,0,false,416,None,None), SquadStateInfo(2262373120,0,0,Vector3(2909.0547,3740.539,67.296875),0,1,false,132,None,None)))
//7704005dd9ccf01810132fdf9f9ef5c4a8000084de7a022c00e5898c8d5e4c429b004ed01d50181016395a4c364e08280001b901a070bd0140805308d59f90641f40000c001db11e280a00088d19e3a190f0ca6c0100
// SquadState(PlanetSideGUID(4),List(SquadStateInfo(3718041345,64,64,Vector3(3966.5938,6095.8047,75.359375),2,2,false,0,None,None), SquadStateInfo(2229172738,22,0,Vector3(3268.4453,3690.3906,66.296875),2,2,false,728,None,None), SquadStateInfo(3976320257,64,64,Vector3(3530.6875,4635.1484,128.875),2,2,false,0,Some(441),Some(true)), SquadStateInfo(1088518658,64,64,Vector3(3336.3203,4601.3438,60.78125),2,2,false,0,Some(896),Some(true)), SquadStateInfo(1816627714,64,0,Vector3(5027.0625,4931.7734,48.234375),2,2,false,728,None,None)))
final case class SquadState(guid : PlanetSideGUID,
info_list : List[SquadStateInfo])
extends PlanetSideGamePacket {
type Packet = SquadState
def opcode = GamePacketOpcode.SquadState
def encode = SquadState.encode(this)
}
object SquadStateInfo {
def apply(unk1 : Long, unk2 : Int, unk3 : Int, pos : Vector3, unk4 : Int, unk5 : Int, unk6 : Boolean, unk7 : Int) : SquadStateInfo =
SquadStateInfo(unk1, unk2, unk3, pos, unk4, unk5, unk6, unk7, None, None)
def apply(unk1 : Long, unk2 : Int, unk3 : Int, pos : Vector3, unk4 : Int, unk5 : Int, unk6 : Boolean, unk7 : Int, unk8 : Int, unk9 : Boolean) : SquadStateInfo =
SquadStateInfo(unk1, unk2, unk3, pos, unk4, unk5, unk6, unk7, Some(unk8), Some(unk9))
}
object SquadState extends Marshallable[SquadState] {
private val info_codec : Codec[SquadStateInfo] = (
("char_id" | uint32L) ::
("unk2" | uint(7)) ::
("unk3" | uint(7)) ::
("pos" | Vector3.codec_pos) ::
("unk4" | uint2) ::
("unk5" | uint2) ::
("unk6" | bool) ::
("unk7" | uint16L) ::
(bool >>:~ { out =>
conditional(out, "unk8" | uint16L) ::
conditional(out, "unk9" | bool)
})
).exmap[SquadStateInfo] (
{
case char_id :: u2 :: u3 :: pos :: u4 :: u5 :: u6 :: u7 :: _ :: u8 :: u9 :: HNil =>
Attempt.Successful(SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, u8, u9))
},
{
case SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, Some(u8), Some(u9)) =>
Attempt.Successful(char_id :: u2 :: u3 :: pos :: u4 :: u5 :: u6 :: u7 :: true :: Some(u8) :: Some(u9) :: HNil)
case SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, None, None) =>
Attempt.Successful(char_id :: u2 :: u3 :: pos :: u4 :: u5 :: u6 :: u7 :: false :: None :: None :: HNil)
case data @ (SquadStateInfo(_, _, _, _, _, _, _, _, Some(_), None) | SquadStateInfo(_, _, _, _, _, _, _, _, None, Some(_))) =>
Attempt.Failure(Err(s"SquadStateInfo requires both unk8 and unk9 to be either defined or undefined at the same time - $data"))
}
)
implicit val codec : Codec[SquadState] = (
("guid" | PlanetSideGUID.codec) ::
("info_list" | listOfN(uint4, info_codec))
).as[SquadState]
}

View file

@ -0,0 +1,32 @@
// Copyright (c) 2019 PSForever
package game
import net.psforever.packet._
import net.psforever.packet.game._
import org.specs2.mutable._
import scodec.bits._
class SquadMemberEventTest extends Specification {
val string = hex"7000e008545180410848006f0066004400070051150800"
"decode" in {
PacketCoding.DecodePacket(string).require match {
case SquadMemberEvent(u1, u2, u3, u4, u5, u6, u7) =>
u1 mustEqual 0
u2 mustEqual 7
u3 mustEqual 42771010L
u4 mustEqual 0
u5.contains("HofD") mustEqual true
u6.contains(7) mustEqual true
u7.contains(529745L) mustEqual true
case _ =>
ko
}
}
"encode" in {
val msg = SquadMemberEvent(0, 7, 42771010L, 0, Some("HofD"), Some(7), Some(529745L))
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
}
}

View file

@ -0,0 +1,244 @@
// Copyright (c) 2019 PSForever
package game
import net.psforever.packet._
import net.psforever.packet.game._
import net.psforever.types.Vector3
import org.specs2.mutable._
import scodec.bits._
class SquadStateTest extends Specification {
val string1 = hex"770700186d9130081001b11b27c1c041680000"
val string2 = hex"770700242a28c020003e9237a90e3382695004eab58a0281017eb95613df4c42950040"
val stringx = hex"7704008dd9ccf010042a9837310e1b82a8c006646c7a028103984f34759c904a800014f01c26f3d014081ddd3896931bc25478037680ea80c081d699a147b01e154000031c0bc81407e08c1a3a890de1542c022070bd0140815958bf29efa6214300108023c01000ae491ac68d1a61342c023623c50140011d6ea0878f3026a00009e014"
"decode (1)" in {
PacketCoding.DecodePacket(string1).require match {
case SquadState(guid, list) =>
guid mustEqual PlanetSideGUID(7)
list.size mustEqual 1
//0
list.head match {
case SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, u8, u9) =>
char_id mustEqual 1300870L
u2 mustEqual 64
u3 mustEqual 64
pos mustEqual Vector3(3464.0469f, 4065.5703f, 20.015625f)
u4 mustEqual 2
u5 mustEqual 2
u6 mustEqual false
u7 mustEqual 0
u8.isEmpty mustEqual true
u9.isEmpty mustEqual true
case _ =>
ko
}
case _ =>
ko
}
}
"decode (2)" in {
PacketCoding.DecodePacket(string2).require match {
case SquadState(guid, list) =>
guid mustEqual PlanetSideGUID(7)
list.size mustEqual 2
//0
list.head match {
case SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, u8, u9) =>
char_id mustEqual 42771010L
u2 mustEqual 0
u3 mustEqual 0
pos mustEqual Vector3(6801.953f, 4231.828f, 39.21875f)
u4 mustEqual 2
u5 mustEqual 2
u6 mustEqual false
u7 mustEqual 680
u8.isEmpty mustEqual true
u9.isEmpty mustEqual true
case _ =>
ko
}
list(1) match {
case SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, u8, u9) =>
char_id mustEqual 42644970L
u2 mustEqual 64
u3 mustEqual 64
pos mustEqual Vector3(2908.7422f, 3742.6875f, 67.296875f)
u4 mustEqual 2
u5 mustEqual 2
u6 mustEqual false
u7 mustEqual 680
u8.isEmpty mustEqual true
u9.isEmpty mustEqual true
case _ =>
ko
}
case _ =>
ko
}
}
"decode (8)" in {
PacketCoding.DecodePacket(stringx).require match {
case SquadState(guid, list) =>
guid mustEqual PlanetSideGUID(4)
list.size mustEqual 8
//0
list.head match {
case SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, u8, u9) =>
char_id mustEqual 30383325L
u2 mustEqual 0
u3 mustEqual 16
pos mustEqual Vector3(6849.328f, 4231.5938f, 41.71875f)
u4 mustEqual 2
u5 mustEqual 2
u6 mustEqual false
u7 mustEqual 864
u8.isEmpty mustEqual true
u9.isEmpty mustEqual true
case _ =>
ko
}
list(1) match {
case SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, u8, u9) =>
char_id mustEqual 41577572L
u2 mustEqual 64
u3 mustEqual 64
pos mustEqual Vector3(6183.797f, 4013.6328f, 72.5625f)
u4 mustEqual 2
u5 mustEqual 2
u6 mustEqual false
u7 mustEqual 0
u8.contains(335) mustEqual true
u9.contains(true) mustEqual true
case _ =>
ko
}
list(2) match {
case SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, u8, u9) =>
char_id mustEqual 41606788L
u2 mustEqual 64
u3 mustEqual 64
pos mustEqual Vector3(6611.8594f, 4242.586f, 75.46875f)
u4 mustEqual 2
u5 mustEqual 2
u6 mustEqual false
u7 mustEqual 888
u8.isEmpty mustEqual true
u9.isEmpty mustEqual true
case _ =>
ko
}
list(3) match {
case SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, u8, u9) =>
char_id mustEqual 30736877L
u2 mustEqual 64
u3 mustEqual 64
pos mustEqual Vector3(6809.836f, 4218.078f, 40.234375f)
u4 mustEqual 2
u5 mustEqual 2
u6 mustEqual false
u7 mustEqual 0
u8.isEmpty mustEqual true
u9.isEmpty mustEqual true
case _ =>
ko
}
list(4) match {
case SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, u8, u9) =>
char_id mustEqual 41517411L
u2 mustEqual 64
u3 mustEqual 63
pos mustEqual Vector3(6848.0312f, 4232.2266f, 41.734375f)
u4 mustEqual 2
u5 mustEqual 2
u6 mustEqual false
u7 mustEqual 556
u8.isEmpty mustEqual true
u9.isEmpty mustEqual true
case _ =>
ko
}
list(5) match {
case SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, u8, u9) =>
char_id mustEqual 41607488L
u2 mustEqual 64
u3 mustEqual 64
pos mustEqual Vector3(2905.3438f, 3743.9453f, 67.296875f)
u4 mustEqual 2
u5 mustEqual 2
u6 mustEqual false
u7 mustEqual 304
u8.isEmpty mustEqual true
u9.isEmpty mustEqual true
case _ =>
ko
}
list(6) match {
case SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, u8, u9) =>
char_id mustEqual 41419792L
u2 mustEqual 0
u3 mustEqual 5
pos mustEqual Vector3(6800.8906f ,4236.7734f, 39.296875f)
u4 mustEqual 2
u5 mustEqual 2
u6 mustEqual false
u7 mustEqual 556
u8.isEmpty mustEqual true
u9.isEmpty mustEqual true
case _ =>
ko
}
list(7) match {
case SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, u8, u9) =>
char_id mustEqual 42616684L
u2 mustEqual 64
u3 mustEqual 0
pos mustEqual Vector3(2927.1094f, 3704.0312f, 78.375f)
u4 mustEqual 1
u5 mustEqual 1
u6 mustEqual false
u7 mustEqual 0
u8.contains(572) mustEqual true
u9.contains(true) mustEqual true
case _ =>
ko
}
case _ =>
ko
}
}
"encode (1)" in {
val msg = SquadState(PlanetSideGUID(7), List(
SquadStateInfo(1300870L, 64, 64, Vector3(3464.0469f, 4065.5703f, 20.015625f), 2, 2, false, 0)
))
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string1
}
"encode (2)" in {
val msg = SquadState(PlanetSideGUID(7), List(
SquadStateInfo(42771010L, 0, 0, Vector3(6801.953f, 4231.828f, 39.21875f), 2, 2, false, 680),
SquadStateInfo(42644970L, 64, 64, Vector3(2908.7422f, 3742.6875f, 67.296875f), 2, 2, false, 680)
))
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string2
}
"encode (8)" in {
val msg = SquadState(PlanetSideGUID(4), List(
SquadStateInfo(30383325L, 0, 16, Vector3(6849.328f, 4231.5938f, 41.71875f), 2, 2, false, 864),
SquadStateInfo(41577572L, 64, 64, Vector3(6183.797f, 4013.6328f, 72.5625f), 2, 2, false, 0, 335, true),
SquadStateInfo(41606788L, 64, 64, Vector3(6611.8594f, 4242.586f, 75.46875f), 2, 2, false, 888),
SquadStateInfo(30736877L, 64, 64, Vector3(6809.836f, 4218.078f, 40.234375f), 2, 2, false, 0),
SquadStateInfo(41517411L, 64, 63, Vector3(6848.0312f, 4232.2266f, 41.734375f), 2, 2, false, 556),
SquadStateInfo(41607488L, 64, 64, Vector3(2905.3438f, 3743.9453f, 67.296875f), 2, 2, false, 304),
SquadStateInfo(41419792L, 0, 5, Vector3(6800.8906f, 4236.7734f, 39.296875f), 2, 2, false, 556),
SquadStateInfo(42616684L, 64, 0, Vector3(2927.1094f, 3704.0312f, 78.375f), 1, 1, false, 0, 572, true)
))
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual stringx
}
}