diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index 369a9299..038f9ace 100644 --- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -381,7 +381,7 @@ object GamePacketOpcode extends Enumeration { // OPCODE 50 case SetCurrentAvatarMessage => game.SetCurrentAvatarMessage.decode case ObjectHeldMessage => game.ObjectHeldMessage.decode - case WeaponFireMessage => noDecoder(opcode) + case WeaponFireMessage => game.WeaponFireMessage.decode case AvatarJumpMessage => noDecoder(opcode) case PickupItemMessage => noDecoder(opcode) case DropItemMessage => game.DropItemMessage.decode diff --git a/common/src/main/scala/net/psforever/packet/game/WeaponFireMessage.scala b/common/src/main/scala/net/psforever/packet/game/WeaponFireMessage.scala new file mode 100644 index 00000000..79e4baa0 --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/WeaponFireMessage.scala @@ -0,0 +1,45 @@ +// Copyright (c) 2016 PSForever.net to present +package net.psforever.packet.game + +import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} +import net.psforever.types.Vector3 +import scodec.Codec +import scodec.codecs._ + +/** WeaponFireMessage seems to be sent each time a weapon actually shoots. + * + * See [[PlayerStateMessageUpstream]] for explanation of seq_time. + */ +final case class WeaponFireMessage(seq_time : Int, + weapon_guid : PlanetSideGUID, + projectile_guid : PlanetSideGUID, + shot_origin : Vector3, + unk1 : Int, + unk2 : Int, + unk3 : Int, + unk4 : Int, + unk5 : Int, + unk6 : Int, + unk7 : Option[Option[Vector3]]) + extends PlanetSideGamePacket { + type Packet = WeaponFireMessage + def opcode = GamePacketOpcode.WeaponFireMessage + def encode = WeaponFireMessage.encode(this) +} + +object WeaponFireMessage extends Marshallable[WeaponFireMessage] { + implicit val codec : Codec[WeaponFireMessage] = ( + ("seq_time" | uintL(10)) :: + ("weapon_guid" | PlanetSideGUID.codec) :: + ("projectile_guid" | PlanetSideGUID.codec) :: + ("shot_origin" | Vector3.codec_pos) :: + ("unk1" | uint16L) :: + ("unk2" | uint16L) :: + ("unk3" | uint16L) :: + ("unk4" | uint16L) :: + ("unk5" | uint8L) :: + (("unk6" | uintL(3)) >>:~ { unk6_value => + conditional(unk6_value == 3, ("unk7" | optional(bool, Vector3.codec_vel))).hlist + }) + ).as[WeaponFireMessage] +} diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index e10ef562..27761f6c 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -601,5 +601,35 @@ class GamePacketTest extends Specification { pkt mustEqual string } } + + "WeaponFireMessage" should { + val string = hex"34 44130029272F0B5DFD4D4EC5C00009BEF78172003FC0" + + "decode" in { + PacketCoding.DecodePacket(string).require match { + case WeaponFireMessage(seq_time, weapon_guid, projectile_guid, shot_origin, unk1, unk2, unk3, unk4, unk5, unk6, unk7) => + seq_time mustEqual 68 + weapon_guid mustEqual PlanetSideGUID(76) + projectile_guid mustEqual PlanetSideGUID(40100) + shot_origin mustEqual Vector3(3675.4688f, 2726.9922f, 92.921875f) + unk1 mustEqual 0 + unk2 mustEqual 64294 + unk3 mustEqual 1502 + unk4 mustEqual 200 + unk5 mustEqual 255 + unk6 mustEqual 0 + unk7 mustEqual None + case default => + ko + } + } + + "encode" in { + val msg = WeaponFireMessage(68, PlanetSideGUID(76), PlanetSideGUID(40100), Vector3(3675.4688f, 2726.9922f, 92.921875f), 0, 64294, 1502, 200, 255, 0, None) + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual string + } + } } } diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index ff4d6ca8..457945a8 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -209,6 +209,9 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ WeaponDelayFireMessage(seq_time, weapon_guid) => log.info("WeaponDelayFire: " + msg) + case msg @ WeaponFireMessage(seq_time, weapon_guid, projectile_guid, shot_origin, unk1, unk2, unk3, unk4, unk5, unk6, unk7) => + log.info("WeaponFire: " + msg) + case default => log.debug(s"Unhandled GamePacket ${pkt}") }