From 3789bc2ae553a63621e205684c7312b4f4952861 Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 14 Sep 2016 00:18:08 -0400 Subject: [PATCH 1/3] added FavoritesMessage packet and tests --- .../psforever/packet/GamePacketOpcode.scala | 2 +- .../packet/game/FavoritesMessage.scala | 38 ++++++++++++++ common/src/test/scala/GamePacketTest.scala | 49 +++++++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index 6e9984e5d..c9b7d65ef 100644 --- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -433,7 +433,7 @@ object GamePacketOpcode extends Enumeration { case 0x5f => noDecoder(FavoritesResponse) // OPCODES 0x60-6f - case 0x60 => noDecoder(FavoritesMessage) + case 0x60 => game.FavoritesMessage.decode case 0x61 => noDecoder(ObjectDetectedMessage) case 0x62 => noDecoder(SplashHitMessage) case 0x63 => noDecoder(SetChatFilterMessage) diff --git a/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala b/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala new file mode 100644 index 000000000..e9884672a --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala @@ -0,0 +1,38 @@ +// Copyright (c) 2016 PSForever.net to present +package net.psforever.packet.game + +import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} +import scodec.Codec +import scodec.codecs._ + +/** + * na + * @param list na + * @param player_guid the player + * @param line na + * @param subject na + * @param armor the type of exo-suit + * @param subtype the exo-suit subtype, if any + */ +final case class FavoritesMessage(list : Int, + player_guid : PlanetSideGUID, + line : Int, + subject : String, + armor : Option[Int] = None, + subtype : Option[Int] = None) + extends PlanetSideGamePacket { + type Packet = FavoritesMessage + def opcode = GamePacketOpcode.FavoritesMessage + def encode = FavoritesMessage.encode(this) +} + +object FavoritesMessage extends Marshallable[FavoritesMessage] { + implicit val codec : Codec[FavoritesMessage] = ( + ("list" | uintL(2)) >>:~ { value => + ("player_guid" | PlanetSideGUID.codec) :: + ("line" | uintL(4)) :: + ("subject" | PacketHelpers.encodedWideStringAligned(2)) :: + conditional(value == 0, "armor" | uintL(3)) :: + conditional(value == 0, "subtype" | uintL(3)) + }).as[FavoritesMessage] +} diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index 2f7e8a11d..58760ff48 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -850,6 +850,55 @@ class GamePacketTest extends Specification { } } + "FavoritesMessage" should { + val stringVehicles = hex"60 5C 84 02 20 5300 6B00 7900 6700 7500 6100 7200 6400" + val stringInfantry = hex"60 2C 03 82 34 4100 6700 6900 6C00 6500 2000 2800 6200 6100 7300 6900 6300 2900 20" + + "decode (for infantry)" in { + PacketCoding.DecodePacket(stringInfantry).require match { + case FavoritesMessage(list, player_guid, line, subject, armor, subtype) => + list mustEqual 0 + player_guid mustEqual PlanetSideGUID(3760) + line mustEqual 0 + subject mustEqual "Agile (basic)" + armor.isDefined mustEqual true + armor.get mustEqual 1 + subtype.isDefined mustEqual true + subtype.get mustEqual 0 + case default => + ko + } + } + + "encode (for infantry)" in { + val msg = FavoritesMessage(0, PlanetSideGUID(3760), 0, "Agile (basic)", Option(1), Option(0)) + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual stringInfantry + } + + "decode (for vehicles)" in { + PacketCoding.DecodePacket(stringVehicles).require match { + case FavoritesMessage(list, player_guid, line, subject, armor, subtype) => + list mustEqual 1 + player_guid mustEqual PlanetSideGUID(4210) + line mustEqual 0 + subject mustEqual "Skyguard" + armor.isDefined mustEqual false + subtype.isDefined mustEqual false + case default => + ko + } + } + + "encode (for vehicles)" in { + val msg = FavoritesMessage(1, PlanetSideGUID(4210), 0, "Skyguard") + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual stringVehicles + } + } + "WeaponJammedMessage" should { val string = hex"66 4C00" From bc2a231d45992f5740225f19dd4360af1c3d11f1 Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 14 Sep 2016 19:37:15 -0400 Subject: [PATCH 2/3] handling Infantry packets, which have an extra field; added tests and comments --- .../packet/game/FavoritesMessage.scala | 61 +++++++++++++++---- common/src/test/scala/GamePacketTest.scala | 13 ++-- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala b/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala index e9884672a..989699a96 100644 --- a/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala @@ -6,20 +6,58 @@ import scodec.Codec import scodec.codecs._ /** - * na - * @param list na + * Load the designator for an entry in the player's favorites list.
+ *
+ * This entry defines a loadout but does not directly associate itself with the configurations and contents of the loadout. + * It is just the user-defined name that appears on a "Favorites" tab list and can be selected. + * A subsequent server request - `ItemTransactionMessage` - must be made to retrieve the said configuration and content for loading. + * Multiple separated favorites lists are present in the game. + * All entries are prepended with their destination list, which determines what kind of terminal or menu allows them to be viewable.
+ *
+ * Infantry equipment favorites are appended with a code for the type of exo-suit that they will load on a player. + * This does not match the same two field numbering system as in `ArmorChangedMessage` packets. + * Subtypes are not considered separately.
+ *
+ * Lists:
+ * ` + * 0 - Equipment Terminal (infantry)
+ * 1 - Repair/Rearm (standard vehicles)
+ * ` + *
+ * Armors:
+ * ` + * 1 - Agile
+ * 2 - Reinforced
+ * 4 - AA MAX
+ * 5 - AI MAX
+ * 6 - AV MAX
+ * ` + *
+ * Exploration 1:
+ * The identifier for the list is two bits so four separated lists of Favorites are supportable. + * Two of the lists are common enough and we can assume one of the others is related to Battleframe Robotics. + * What, if anything, is the fourth indexed list?
+ *
+ * Exploration 2:
+ * There are three unaccounted exo-suit indices - '0', '3', and '7' - and two specific kinds of exo-suit that are not defined - Infiltration and Standard. + * It is possible that one of the indices also defines the generic MAX (see `ArmorChangedMessage`). + * Which exo-suit is associated with which index?
+ *
+ * Exploration 3:
+ * The `armor` parameter coincides with the type of exo-suit maintained by the loadout. + * Does this value help the client judge when a favorite can be selected by checking against certifications for exo-suit permission? + * (If so, why isn't it working?) + * @param list the destination list * @param player_guid the player - * @param line na - * @param subject na - * @param armor the type of exo-suit - * @param subtype the exo-suit subtype, if any + * @param line the zero-indexed line number of this entry in its list + * @param label the identifier for this entry + * @param armor the type of exo-suit, if an Infantry loadout */ final case class FavoritesMessage(list : Int, player_guid : PlanetSideGUID, line : Int, - subject : String, - armor : Option[Int] = None, - subtype : Option[Int] = None) + label : String, + armor : Option[Int] = None) extends PlanetSideGamePacket { type Packet = FavoritesMessage def opcode = GamePacketOpcode.FavoritesMessage @@ -31,8 +69,7 @@ object FavoritesMessage extends Marshallable[FavoritesMessage] { ("list" | uintL(2)) >>:~ { value => ("player_guid" | PlanetSideGUID.codec) :: ("line" | uintL(4)) :: - ("subject" | PacketHelpers.encodedWideStringAligned(2)) :: - conditional(value == 0, "armor" | uintL(3)) :: - conditional(value == 0, "subtype" | uintL(3)) + ("label" | PacketHelpers.encodedWideStringAligned(2)) :: + conditional(value == 0, "armor" | uintL(3)) }).as[FavoritesMessage] } diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index 58760ff48..b79311ecd 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -856,22 +856,20 @@ class GamePacketTest extends Specification { "decode (for infantry)" in { PacketCoding.DecodePacket(stringInfantry).require match { - case FavoritesMessage(list, player_guid, line, subject, armor, subtype) => + case FavoritesMessage(list, player_guid, line, label, armor) => list mustEqual 0 player_guid mustEqual PlanetSideGUID(3760) line mustEqual 0 - subject mustEqual "Agile (basic)" + label mustEqual "Agile (basic)" armor.isDefined mustEqual true armor.get mustEqual 1 - subtype.isDefined mustEqual true - subtype.get mustEqual 0 case default => ko } } "encode (for infantry)" in { - val msg = FavoritesMessage(0, PlanetSideGUID(3760), 0, "Agile (basic)", Option(1), Option(0)) + val msg = FavoritesMessage(0, PlanetSideGUID(3760), 0, "Agile (basic)", Option(1)) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual stringInfantry @@ -879,13 +877,12 @@ class GamePacketTest extends Specification { "decode (for vehicles)" in { PacketCoding.DecodePacket(stringVehicles).require match { - case FavoritesMessage(list, player_guid, line, subject, armor, subtype) => + case FavoritesMessage(list, player_guid, line, label, armor) => list mustEqual 1 player_guid mustEqual PlanetSideGUID(4210) line mustEqual 0 - subject mustEqual "Skyguard" + label mustEqual "Skyguard" armor.isDefined mustEqual false - subtype.isDefined mustEqual false case default => ko } From cfdabdaa3dc9db3fd18d302b7f31e74c38da0e24 Mon Sep 17 00:00:00 2001 From: FateJH Date: Tue, 10 Jan 2017 14:51:39 -0500 Subject: [PATCH 3/3] changes to packet prior to merge with master --- .../packet/game/FavoritesMessage.scala | 45 +++++++++++-------- .../net/psforever/types/TransactionType.scala | 2 +- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala b/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala index 989699a96..1969f6377 100644 --- a/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/FavoritesMessage.scala @@ -4,24 +4,24 @@ package net.psforever.packet.game import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} import scodec.Codec import scodec.codecs._ +import shapeless.{::, HNil} /** * Load the designator for an entry in the player's favorites list.
*
- * This entry defines a loadout but does not directly associate itself with the configurations and contents of the loadout. - * It is just the user-defined name that appears on a "Favorites" tab list and can be selected. - * A subsequent server request - `ItemTransactionMessage` - must be made to retrieve the said configuration and content for loading. + * This entry defines a user-defined loadout label that appears on a "Favorites" tab list and can be selected. + * A subsequent server request - `ItemTransactionMessage` - must be made to retrieve the said loadout contents. * Multiple separated favorites lists are present in the game. - * All entries are prepended with their destination list, which determines what kind of terminal or menu allows them to be viewable.
+ * All entries are prepended with their destination list which indicates how from how that list is viewable. + * Different lists also have different numbers of available lines to store loadout entries.
*
* Infantry equipment favorites are appended with a code for the type of exo-suit that they will load on a player. - * This does not match the same two field numbering system as in `ArmorChangedMessage` packets. - * Subtypes are not considered separately.
+ * This does not match the same two field numbering system as in `ArmorChangedMessage` packets.
*
* Lists:
* ` * 0 - Equipment Terminal (infantry)
- * 1 - Repair/Rearm (standard vehicles)
+ * 1 - Repair/Rearm Silo (standard vehicles)
* ` *
* Armors:
@@ -34,19 +34,16 @@ import scodec.codecs._ * ` *
* Exploration 1:
- * The identifier for the list is two bits so four separated lists of Favorites are supportable. + * The identifier for the list is two bits so four separated lists of `Favorites` are supportable. * Two of the lists are common enough and we can assume one of the others is related to Battleframe Robotics. - * What, if anything, is the fourth indexed list?
+ * These lists also do not include `Squad Defintion...` presets. + * What are the unknown lists?
*
* Exploration 2:
- * There are three unaccounted exo-suit indices - '0', '3', and '7' - and two specific kinds of exo-suit that are not defined - Infiltration and Standard. + * There are three unaccounted exo-suit indices - 0, 3, and 7; + * and, there are two specific kinds of exo-suit that are not defined - Infiltration and Standard. * It is possible that one of the indices also defines the generic MAX (see `ArmorChangedMessage`). - * Which exo-suit is associated with which index?
- *
- * Exploration 3:
- * The `armor` parameter coincides with the type of exo-suit maintained by the loadout. - * Does this value help the client judge when a favorite can be selected by checking against certifications for exo-suit permission? - * (If so, why isn't it working?) + * Which exo-suit is associated with which index? * @param list the destination list * @param player_guid the player * @param line the zero-indexed line number of this entry in its list @@ -66,10 +63,20 @@ final case class FavoritesMessage(list : Int, object FavoritesMessage extends Marshallable[FavoritesMessage] { implicit val codec : Codec[FavoritesMessage] = ( - ("list" | uintL(2)) >>:~ { value => + ("list" | uint2L) >>:~ { value => ("player_guid" | PlanetSideGUID.codec) :: - ("line" | uintL(4)) :: + ("line" | uint4L) :: ("label" | PacketHelpers.encodedWideStringAligned(2)) :: conditional(value == 0, "armor" | uintL(3)) - }).as[FavoritesMessage] + }).xmap[FavoritesMessage] ( + { + case lst :: guid :: ln :: str :: arm :: HNil => + FavoritesMessage(lst, guid, ln, str, arm) + }, + { + case FavoritesMessage(lst, guid, ln, str, arm) => + val armset : Option[Int] = if(lst == 0 && arm.isEmpty) { Some(0) } else { arm } + lst :: guid :: ln :: str :: armset :: HNil + } + ) } diff --git a/common/src/main/scala/net/psforever/types/TransactionType.scala b/common/src/main/scala/net/psforever/types/TransactionType.scala index c1b640b7b..e8ed96f34 100644 --- a/common/src/main/scala/net/psforever/types/TransactionType.scala +++ b/common/src/main/scala/net/psforever/types/TransactionType.scala @@ -12,7 +12,7 @@ object TransactionType extends Enumeration { Sell, Unk4, Unk5, - Unk6, + Infantry_Loadout, Unk7 = Value