diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index 6e9984e5..6a5de924 100644 --- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -366,7 +366,7 @@ object GamePacketOpcode extends Enumeration { case 0x26 => noDecoder(UnuseItemMessage) case 0x27 => noDecoder(ObjectDetachMessage) // 0x28 - case 0x28 => noDecoder(CreateShortcutMessage) + case 0x28 => game.CreateShortcutMessage.decode case 0x29 => noDecoder(ChangeShortcutBankMessage) case 0x2a => noDecoder(ObjectAttachMessage) case 0x2b => noDecoder(UnknownMessage43) diff --git a/common/src/main/scala/net/psforever/packet/game/CreateShortcutMessage.scala b/common/src/main/scala/net/psforever/packet/game/CreateShortcutMessage.scala new file mode 100644 index 00000000..682867af --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/CreateShortcutMessage.scala @@ -0,0 +1,56 @@ +// 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._ + +/** + * Create a quick-use button for the hotbar.
+ *
+ * Purpose:
+ * `advanced_regen` (regeneration)
+ * `audio_amplifier`
+ * `darklight_vision`
+ * `medkit`
+ * `melee_booster`
+ * `personal_shield`
+ * `range_magnifier`
+ * `second_wind`
+ * `shortcut_macro`
+ * `silent_run` (sensor shield)
+ * `surge`
+ * `targeting` + * @param player_guid the player + * @param slot the hotbar slot number (one-indexed) + * @param unk1 na + * @param unk2 na + * @param purpose the primary purpose/use of this shortcut + * @param effect1 for macros, a three letter acronym displayed in the hotbar + * @param effect2 for macros, the chat message content + */ +//TODO Failed to parse game packet 0x28: purpose: cannot acquire 15 bits from a vector that contains 4 bits -- encountered when attempting to move shortcuts -- look into this +final case class CreateShortcutMessage(player_guid : PlanetSideGUID, + slot : Int, + unk1 : Int, + unk2 : Int, + purpose : String, + effect1 : String = "", + effect2 : String = "") + extends PlanetSideGamePacket { + type Packet = CreateShortcutMessage + def opcode = GamePacketOpcode.CreateShortcutMessage + def encode = CreateShortcutMessage.encode(this) +} + +object CreateShortcutMessage extends Marshallable[CreateShortcutMessage] { + implicit val codec : Codec[CreateShortcutMessage] = ( + ("player_guid" | PlanetSideGUID.codec) :: + ("slot" | uint8L) :: + ("unk1" | uint8L) :: + ("unk2" | uintL(3)) :: + ("purpose" | PacketHelpers.encodedStringAligned(5)) :: + ("effect1" | PacketHelpers.encodedWideString) :: + ("effect2" | PacketHelpers.encodedWideString) + ).as[CreateShortcutMessage] +} diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index 2f7e8a11..e65d3698 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -299,6 +299,78 @@ class GamePacketTest extends Specification { } } + "CreateShortcutMessage" should { + val stringMedkit = hex"28 7210 01 00 90 C0 6D65646B6974 80 80" + val stringImplant = hex"28 7210 04 00 D0 A0 7375726765 80 80" + val stringMacro = hex"28 4C05 08 00 B1 C0 73686F72746375745F6D6163726F 83 4E00 5400 5500 9B 2F00 7000 6C00 6100 7400 6F00 6F00 6E00 2000 4900 6E00 6300 6F00 6D00 6900 6E00 6700 2000 4E00 5400 5500 2000 7300 7000 6100 6D00 2100" + + "decode (medkit)" in { + PacketCoding.DecodePacket(stringMedkit).require match { + case CreateShortcutMessage(player_guid, slot, unk1, unk2, purpose, effect1, effect2) => + player_guid mustEqual PlanetSideGUID(4210) + slot mustEqual 1 + unk1 mustEqual 0 + unk2 mustEqual 4 + purpose mustEqual "medkit" + effect1 mustEqual "" + effect2 mustEqual "" + case default => + ko + } + } + + "decode (implant)" in { + PacketCoding.DecodePacket(stringImplant).require match { + case CreateShortcutMessage(player_guid, slot, unk1, unk2, purpose, effect1, effect2) => + player_guid mustEqual PlanetSideGUID(4210) + slot mustEqual 4 + unk1 mustEqual 0 + unk2 mustEqual 6 + purpose mustEqual "surge" + effect1 mustEqual "" + effect2 mustEqual "" + case default => + ko + } + } + + "decode (macro)" in { + PacketCoding.DecodePacket(stringMacro).require match { + case CreateShortcutMessage(player_guid, slot, unk1, unk2, purpose, effect1, effect2) => + player_guid mustEqual PlanetSideGUID(1356) + slot mustEqual 8 + unk1 mustEqual 0 + unk2 mustEqual 5 + purpose mustEqual "shortcut_macro" + effect1 mustEqual "NTU" + effect2 mustEqual "/platoon Incoming NTU spam!" + case default => + ko + } + } + + "encode (medkit)" in { + val msg = CreateShortcutMessage(PlanetSideGUID(4210), 1, 0, 4, "medkit") + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual stringMedkit + } + + "encode (implant)" in { + val msg = CreateShortcutMessage(PlanetSideGUID(4210), 4, 0, 6, "surge") + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual stringImplant + } + + "encode (macro)" in { + val msg = CreateShortcutMessage(PlanetSideGUID(1356), 8, 0, 5, "shortcut_macro", "NTU", "/platoon Incoming NTU spam!") + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual stringMacro + } + } + "DropItemMessage" should { val string = hex"37 4C00" diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index ea99a03c..625838e7 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -171,6 +171,7 @@ class WorldSessionActor extends Actor with MDCContextAware { true))) //Boosted generator room pain field sendResponse(PacketCoding.CreateGamePacket(0, SetCurrentAvatarMessage(PlanetSideGUID(guid),0,0))) + sendResponse(PacketCoding.CreateGamePacket(0, CreateShortcutMessage(PlanetSideGUID(guid), 1, 0, 6, "medkit"))) import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits.global