From b4d87bac547b188abee34bbf010fed63975c3901 Mon Sep 17 00:00:00 2001 From: Fate-JH Date: Sun, 26 Feb 2017 23:05:56 -0500 Subject: [PATCH] Added: DestroyDisplayMessage Packet (#69) * initial commit; the packet will not encode or decode properly due to the current form of the victim string * the signature compiles properly (took hours on the HList stuff); I don't care about the tests right now * working packet and tests; added proper comments --- .../psforever/packet/GamePacketOpcode.scala | 2 +- .../packet/game/DestroyDisplayMessage.scala | 80 +++++++++++++++++++ common/src/test/scala/GamePacketTest.scala | 28 +++++++ 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 common/src/main/scala/net/psforever/packet/game/DestroyDisplayMessage.scala diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index 97f58318..b697b677 100644 --- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -472,7 +472,7 @@ object GamePacketOpcode extends Enumeration { // OPCODES 0x80-8f case 0x80 => noDecoder(GenericObjectAction2Message) - case 0x81 => noDecoder(DestroyDisplayMessage) + case 0x81 => game.DestroyDisplayMessage.decode case 0x82 => noDecoder(TriggerBotAction) case 0x83 => noDecoder(SquadWaypointRequest) case 0x84 => noDecoder(SquadWaypointEvent) diff --git a/common/src/main/scala/net/psforever/packet/game/DestroyDisplayMessage.scala b/common/src/main/scala/net/psforever/packet/game/DestroyDisplayMessage.scala new file mode 100644 index 00000000..bbd14756 --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/DestroyDisplayMessage.scala @@ -0,0 +1,80 @@ +// 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._ + +/** + * Display a message in the event window that informs of a player death.
+ *
+ * The message is composed of three parts:
+ * 1) killer information
+ * 2) method information
+ * 3) victim information
+ * In the case of a player kill, the player's name will be attributed directly. + * In the case of an absentee kill, a description of the method of death will be attributed. + * In the case of a suicide, the player attributed is the player who was killed (message format displays only the victim). + * The victim's name is byte-aligned with a 5-bit buffer.
+ *
+ * The four bytes that follow each name seems to be important to the identification of the associated player. + * The same value will be seen in every `DestroyDisplayMessage` that includes the player, with respect to whether they are listed as the "killer" or as the "victim." + * This holds true for every entry within thie same login session, at least. + * Blanking these values out does not change anything about the format of the event message. + * In the case of absentee kills, for example, where there is no killer listed, this field has been zero'd (`00000000`).
+ *
+ * The faction affiliation is different from the normal way `PlanetSideEmpire` values are recorded. + * The higher nibble will reflect the first part of the `PlanetSideEmpire` value - `0` for TR, `4` for NC `8` for TR, `C` for Neutral/BOPs. + * An extra `20` will be added if the player is in a vehicle or turret at the time - `2` for TR, `6` for NC, `A` for VS, `E` for Neutral/BOPs. + * When marked as being in a vehicle or turret, the player's name will be enclosed within square brackets. + * The length of the player's name found at the start of the wide character string does not reflect whether or not there will be square brackets (fortunately).
+ *
+ * The two bytes in between the killer section and the victim section are the method of homicide or suicide. + * The color of the resulting icon is borrowed from the attributed killer's faction affiliation if it can be determined. + * An unidentified method defaults to a skull and crossbones icon. + * The exact range of unique and valid icon values for this parameter is currently unknown. + * It is also unknown what the two bytes preceding `method` specify, as changing them does nothing to the displayed message. + * @param killer the name of the player who did the killing + * @param killer_unk See above + * @param killer_empire the empire affiliation of the killer: + * 0 - TR, 1 - NC, 2 - VS, 3 - Neutral/BOPs + * @param killer_inVehicle true, if the killer was in a vehicle at the time of the kill; false, otherwise + * @param unk na; but does not like being set to 0 + * @param method modifies the icon in the message, related to the way the victim was killed + * @param victim the name of the player who was killed + * @param victim_unk See above + * @param victim_empire the empire affiliation of the victim: + * 0 - TR, 1 - NC, 2 - VS, 3 - Neutral/BOPs + * @param victim_inVehicle true, if the victim was in a vehicle when he was killed; false, otherwise + */ +final case class DestroyDisplayMessage(killer : String, + killer_unk : Long, + killer_empire : Int, + killer_inVehicle : Boolean, + unk : PlanetSideGUID, + method : PlanetSideGUID, + victim : String, + victim_unk : Long, + victim_empire : Int, + victim_inVehicle : Boolean +) + extends PlanetSideGamePacket { + type Packet = DestroyDisplayMessage + def opcode = GamePacketOpcode.DestroyDisplayMessage + def encode = DestroyDisplayMessage.encode(this) +} + +object DestroyDisplayMessage extends Marshallable[DestroyDisplayMessage] { + implicit val codec : Codec[DestroyDisplayMessage] = ( + ("killer" | PacketHelpers.encodedWideString) :: + ("killer_unk" | ulongL(32)) :: + ("killer_empire" | uintL(2)) :: + ("killer_inVehicle" | bool) :: + ("unk" | PlanetSideGUID.codec) :: + ("method" | PlanetSideGUID.codec) :: + ("victim" | PacketHelpers.encodedWideStringAligned(5)) :: + ("victim_unk" | ulongL(32)) :: + ("victim_empire" | uintL(2)) :: + ("victim_inVehicle" | bool) + ).as[DestroyDisplayMessage] +} diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index eb833722..9bcf21bb 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -1324,6 +1324,34 @@ class GamePacketTest extends Specification { } } + "DestroyDisplayMessage" should { + val string = hex"81 87 41006E00670065006C006C006F00 35BCD801 8 F201 9207 0A 0 48004D00460049004300 B18ED901 00" // Angello-VS (???) HMFIC-TR + + "decode" in { + PacketCoding.DecodePacket(string).require match { + case DestroyDisplayMessage(killer, killer_unk, killer_empire, killer_inVehicle, unk, method, victim, victim_unk, victim_empire, victim_inVehicle) => + killer mustEqual "Angello" + killer_unk mustEqual 30981173 + killer_empire mustEqual 2 + killer_inVehicle mustEqual false + unk mustEqual PlanetSideGUID(121) + method mustEqual PlanetSideGUID(969) + victim mustEqual "HMFIC" + victim_unk mustEqual 31035057 + victim_empire mustEqual 0 + victim_inVehicle mustEqual false + case default => + ko + } + } + + "encode" in { + val msg = DestroyDisplayMessage("Angello", 30981173, 2, false, PlanetSideGUID(121), PlanetSideGUID(969), "HMFIC", 31035057, 0, false) + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + pkt mustEqual string + } + } + "WeaponDelayFireMessage" should { val string = hex"88 A3140000"