diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
index e6af16977..58e1ee0e6 100644
--- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
+++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala
@@ -605,7 +605,7 @@ object GamePacketOpcode extends Enumeration {
// OPCODES 0xf0-f3
case 0xf0 => noDecoder(QueueTimedHelpMessage)
- case 0xf1 => noDecoder(MailMessage)
+ case 0xf1 => game.MailMessage.decode
case 0xf2 => noDecoder(GameVarUpdate)
case 0xf3 => noDecoder(ClientCheatedMessage)
case default => noDecoder(opcode)
diff --git a/common/src/main/scala/net/psforever/packet/game/MailMessage.scala b/common/src/main/scala/net/psforever/packet/game/MailMessage.scala
new file mode 100644
index 000000000..79fd3491a
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/MailMessage.scala
@@ -0,0 +1,35 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game
+
+import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
+import scodec.Codec
+import scodec.codecs._
+
+/**
+ * Dispatched from the server, sending a "priority message" to the given client's avatar.
+ * The messaging inbox is generally accessible through the use of `alt`+`i`.
+ * It is also made accessible through use of an icon in the lower right corner when there is an outstanding message.
+ *
+ * Exploration:
+ * How does the PlanetSide Classic mail system work?
+ * At the moment, it only seems possible to receive and read mail from the server.
+ * @param sender the name of the player who sent the mail
+ * @param subject the subject
+ * @param message the message
+ */
+final case class MailMessage(sender : String,
+ subject : String,
+ message : String
+ ) extends PlanetSideGamePacket {
+ type Packet = MailMessage
+ def opcode = GamePacketOpcode.MailMessage
+ def encode = MailMessage.encode(this)
+}
+
+object MailMessage extends Marshallable[MailMessage] {
+ implicit val codec : Codec[MailMessage] = (
+ ("sender" | PacketHelpers.encodedString) ::
+ ("subject" | PacketHelpers.encodedString) ::
+ ("message" | PacketHelpers.encodedString)
+ ).as[MailMessage]
+}
\ No newline at end of file
diff --git a/common/src/test/scala/game/MailMessageTest.scala b/common/src/test/scala/game/MailMessageTest.scala
new file mode 100644
index 000000000..726060c7e
--- /dev/null
+++ b/common/src/test/scala/game/MailMessageTest.scala
@@ -0,0 +1,30 @@
+// Copyright (c) 2017 PSForever
+package game
+
+import org.specs2.mutable._
+import net.psforever.packet._
+import net.psforever.packet.game._
+import scodec.bits._
+
+class MailMessageTest extends Specification {
+ //we've never received this packet before so this whole test is faked
+ val string = hex"F1 86466174654A489250726 96F72697479204D61696C2054657374 8E48656C6C6F204175726178697321"
+
+ "decode" in {
+ PacketCoding.DecodePacket(string).require match {
+ case MailMessage(sender, subject, msg) =>
+ sender mustEqual "FateJH"
+ subject mustEqual "Priority Mail Test"
+ msg mustEqual "Hello Auraxis!"
+ case _ =>
+ ko
+ }
+ }
+
+ "encode" in {
+ val msg = MailMessage("FateJH", "Priority Mail Test", "Hello Auraxis!")
+ val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
+
+ pkt mustEqual string
+ }
+}