From fc2ef50be647aadf1140c1c8e688cf0a78c20975 Mon Sep 17 00:00:00 2001 From: tfarley Date: Thu, 28 Jul 2016 19:23:38 -0700 Subject: [PATCH] Various fixes to ChatMsg (#43) * Add BinaryChoiceCodec * Proper ChatMsg structuring --- .../newcodecs/BinaryChoiceCodec.scala | 31 +++++++++++++++++++ .../net/psforever/newcodecs/package.scala | 2 ++ .../net/psforever/packet/game/ChatMsg.scala | 20 +++++++----- common/src/test/scala/GamePacketTest.scala | 22 +++++++------ .../src/main/scala/WorldSessionActor.scala | 8 ++--- 5 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 common/src/main/scala/net/psforever/newcodecs/BinaryChoiceCodec.scala diff --git a/common/src/main/scala/net/psforever/newcodecs/BinaryChoiceCodec.scala b/common/src/main/scala/net/psforever/newcodecs/BinaryChoiceCodec.scala new file mode 100644 index 00000000..4dbdd41b --- /dev/null +++ b/common/src/main/scala/net/psforever/newcodecs/BinaryChoiceCodec.scala @@ -0,0 +1,31 @@ +// Copyright (c) 2016 PSForever.net to present +package net.psforever.newcodecs + +import scodec.{ Codec, SizeBound } +import scodec.bits.BitVector + +private[newcodecs] final class BinaryChoiceCodec[A](choice: Boolean, codec_true: => Codec[A], codec_false: => Codec[A]) extends Codec[A] { + + private lazy val evaluatedCodec_true = codec_true + + private lazy val evaluatedCodec_false = codec_false + + override def sizeBound = if (choice) evaluatedCodec_true.sizeBound else evaluatedCodec_false.sizeBound + + override def encode(a: A) = { + if (choice) + evaluatedCodec_true.encode(a) + else + evaluatedCodec_false.encode(a) + } + + override def decode(buffer: BitVector) = { + if (choice) + evaluatedCodec_true.decode(buffer) + else + evaluatedCodec_false.decode(buffer) + } + + override def toString = if(choice) s"binarychoice(true, $evaluatedCodec_true, ?)" else "binarychoice(false, ?, $evaluatedCodec_false)" + +} diff --git a/common/src/main/scala/net/psforever/newcodecs/package.scala b/common/src/main/scala/net/psforever/newcodecs/package.scala index 8dc532ce..4adbeb8e 100644 --- a/common/src/main/scala/net/psforever/newcodecs/package.scala +++ b/common/src/main/scala/net/psforever/newcodecs/package.scala @@ -11,4 +11,6 @@ package object newcodecs { def q_float(min : Double, max : Double, bits : Int): Codec[Float] = q_double(min, max, bits).narrow(v => Attempt.successful(v.toFloat), _.toDouble) + def binary_choice[A](choice: Boolean, codec_true: => Codec[A], codec_false: => Codec[A]): Codec[A] = new BinaryChoiceCodec(choice, codec_true, codec_false) + } diff --git a/common/src/main/scala/net/psforever/packet/game/ChatMsg.scala b/common/src/main/scala/net/psforever/packet/game/ChatMsg.scala index 62f9e9ba..1cd994a0 100644 --- a/common/src/main/scala/net/psforever/packet/game/ChatMsg.scala +++ b/common/src/main/scala/net/psforever/packet/game/ChatMsg.scala @@ -1,15 +1,17 @@ // Copyright (c) 2016 PSForever.net to present package net.psforever.packet.game +import net.psforever.newcodecs._ import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} import net.psforever.types.ChatMessageType import scodec.Codec import scodec.codecs._ -final case class ChatMsg(channel : ChatMessageType.Value, - unk1 : Boolean, +final case class ChatMsg(messagetype : ChatMessageType.Value, + has_wide_contents : Boolean, recipient : String, - contents : String) + contents : String, + note_contents : Option[String]) extends PlanetSideGamePacket { type Packet = ChatMsg def opcode = GamePacketOpcode.ChatMsg @@ -18,9 +20,11 @@ final case class ChatMsg(channel : ChatMessageType.Value, object ChatMsg extends Marshallable[ChatMsg] { implicit val codec : Codec[ChatMsg] = ( - ("messagetype" | ChatMessageType.codec) :: - ("unk1" | bool) :: - ("recipient" | PacketHelpers.encodedWideStringAligned(7)) :: - ("contents" | PacketHelpers.encodedWideString) - ).as[ChatMsg] + ("messagetype" | ChatMessageType.codec) >>:~ { messagetype_value => + (("has_wide_contents" | bool) >>:~ { has_wide_contents_value => + ("recipient" | PacketHelpers.encodedWideStringAligned(7)) :: + newcodecs.binary_choice(has_wide_contents_value, ("contents" | PacketHelpers.encodedWideString), ("contents" | PacketHelpers.encodedString)) + }) :+ + conditional(messagetype_value == ChatMessageType.Note, ("note_contents" | PacketHelpers.encodedWideString)) + }).as[ChatMsg] } diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index 8f61f883..228bb43e 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -164,40 +164,42 @@ class GamePacketTest extends Specification { ok } } - + "ChatMsg" should { val string_local = hex"12 1A C000 83610062006300" val string_tell = hex"12 20 C180640065006600 83610062006300" - + "decode" in { PacketCoding.DecodePacket(string_local).require match { - case ChatMsg(messagetype, unk1, recipient, contents) => + case ChatMsg(messagetype, has_wide_contents, recipient, contents, note_contents) => messagetype mustEqual ChatMessageType.Local - unk1 mustEqual true + has_wide_contents mustEqual true recipient mustEqual "" contents mustEqual "abc" + note_contents mustEqual None case default => ko } - + PacketCoding.DecodePacket(string_tell).require match { - case ChatMsg(messagetype, unk1, recipient, contents) => + case ChatMsg(messagetype, has_wide_contents, recipient, contents, note_contents) => messagetype mustEqual ChatMessageType.Tell - unk1 mustEqual true + has_wide_contents mustEqual true recipient mustEqual "def" contents mustEqual "abc" + note_contents mustEqual None case default => ko } } "encode" in { - val msg_local = ChatMsg(ChatMessageType.Local, true, "", "abc") + val msg_local = ChatMsg(ChatMessageType.Local, true, "", "abc", None) val pkt_local = PacketCoding.EncodePacket(msg_local).require.toByteVector pkt_local mustEqual string_local - - val msg_tell = ChatMsg(ChatMessageType.Tell, true, "def", "abc") + + val msg_tell = ChatMsg(ChatMessageType.Tell, true, "def", "abc", None) val pkt_tell = PacketCoding.EncodePacket(msg_tell).require.toByteVector pkt_tell mustEqual string_tell diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 9cf776ee..c1e3a880 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -148,13 +148,13 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, unk1, aim_pitch, unk2, seq_time, unk3, is_crouching, unk4, unk5, unk6, unk7, unk8) => //log.info("PlayerState: " + msg) - case msg @ ChatMsg(messagetype, unk1, recipient, contents) => + case msg @ ChatMsg(messagetype, has_wide_contents, recipient, contents, note_contents) => log.info("Chat: " + msg) - + // TODO: Depending on messagetype, may need to prepend sender's name to contents with proper spacing // TODO: Just replays the packet straight back to sender; actually needs to be routed to recipients! - sendResponse(PacketCoding.CreateGamePacket(0, ChatMsg(messagetype, unk1, recipient, contents))) - + sendResponse(PacketCoding.CreateGamePacket(0, ChatMsg(messagetype, has_wide_contents, recipient, contents, note_contents))) + case msg @ ChangeFireModeMessage(item_guid, fire_mode) => log.info("ChangeFireMode: " + msg)