diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index cd45fa0e..2146e5d7 100644 --- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -535,7 +535,7 @@ object GamePacketOpcode extends Enumeration { case 0xb4 => game.BattleExperienceMessage.decode case 0xb5 => noDecoder(TargetingImplantRequest) case 0xb6 => game.ZonePopulationUpdateMessage.decode - case 0xb7 => noDecoder(DisconnectMessage) + case 0xb7 => game.DisconnectMessage.decode // 0xb8 case 0xb8 => noDecoder(ExperienceAddedMessage) case 0xb9 => game.OrbitalStrikeWaypointMessage.decode diff --git a/common/src/main/scala/net/psforever/packet/game/DisconnectMessage.scala b/common/src/main/scala/net/psforever/packet/game/DisconnectMessage.scala new file mode 100644 index 00000000..b6ef7539 --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/DisconnectMessage.scala @@ -0,0 +1,36 @@ +// 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._ + +/** + * Dispatched to the client to force a disconnect.
+ *
+ * The client's render will fade and be centered with a system textbox with the given message. + * Using the button on the textbox will drop the current world session and return the player to the world select screen. + * Being disconnected like this has no client-based consequences on its own.
+ *
+ * Exploration:
+ * When do the other two messages appear, if at all? + * @param msg the displayed message + * @param unk2 na + * @param unk3 na + */ +final case class DisconnectMessage(msg : String, + unk2 : String = "", + unk3 : String = "") + extends PlanetSideGamePacket { + type Packet = DisconnectMessage + def opcode = GamePacketOpcode.DisconnectMessage + def encode = DisconnectMessage.encode(this) +} + +object DisconnectMessage extends Marshallable[DisconnectMessage] { + implicit val codec : Codec[DisconnectMessage] = ( + ("msg" | PacketHelpers.encodedString) :: + ("unk2" | PacketHelpers.encodedString) :: + ("unk3" | PacketHelpers.encodedString) + ).as[DisconnectMessage] +} diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index ad5c5017..776380ae 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -1492,6 +1492,28 @@ class GamePacketTest extends Specification { } } + "DisconnectMessage" should { + val string = hex"B7 85 46 69 72 73 74 86 53 65 63 6F 6E 64 8E 46 69 72 73 74 20 26 20 73 65 63 6F 6E 64" + + "decode" in { + PacketCoding.DecodePacket(string).require match { + case DisconnectMessage(unk1, unk2, unk3) => + unk1 mustEqual "First" + unk2 mustEqual "Second" + unk3 mustEqual "First & second" + case default => + ko + } + } + + "encode" in { + val msg = DisconnectMessage("First", "Second", "First & second") + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual string + } + } + "OrbitalStrikeWaypointMessage" should { val string_on = hex"B9 46 0C AA E3 D2 2A 92 00" val string_off = hex"B9 46 0C 00"