From c648b9deff5dd6104feaaf262416702d8acff2c3 Mon Sep 17 00:00:00 2001 From: FateJH Date: Mon, 13 Mar 2017 22:14:24 -0400 Subject: [PATCH 1/3] initial ProjectileStateMessage packet and tests --- .../psforever/packet/GamePacketOpcode.scala | 2 +- .../packet/game/ProjectileStateMessage.scala | 47 +++++++++++++++++++ .../game/ProjectileStateMessageTest.scala | 44 +++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 common/src/main/scala/net/psforever/packet/game/ProjectileStateMessage.scala create mode 100644 common/src/test/scala/game/ProjectileStateMessageTest.scala diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index 1d1f4802..aad4bf0c 100644 --- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -392,7 +392,7 @@ object GamePacketOpcode extends Enumeration { case 0x3c => game.GenericCollisionMsg.decode case 0x3d => game.QuantityUpdateMessage.decode case 0x3e => game.ArmorChangedMessage.decode - case 0x3f => noDecoder(ProjectileStateMessage) + case 0x3f => game.ProjectileStateMessage.decode // OPCODES 0x40-4f case 0x40 => noDecoder(MountVehicleCargoMsg) diff --git a/common/src/main/scala/net/psforever/packet/game/ProjectileStateMessage.scala b/common/src/main/scala/net/psforever/packet/game/ProjectileStateMessage.scala new file mode 100644 index 00000000..2871cd8f --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/ProjectileStateMessage.scala @@ -0,0 +1,47 @@ +// Copyright (c) 2017 PSForever +package net.psforever.packet.game + +import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} +import net.psforever.types.Vector3 +import scodec.Codec +import scodec.codecs._ + +/** + * Dispatched from the server to render the projectiles of one player's weapon on other players' clients. + * @param projectile_guid the projectile + * @param origin a spawning position for the projectile + * @param orient a directional heading for the projectile + * @param unk1 na; + * rarely not 0 + * @param unk2 na + * @param unk3 na + * @param unk4 na; + * rarely not false + * @param unk5 na + */ +final case class ProjectileStateMessage(projectile_guid : PlanetSideGUID, + origin : Vector3, + orient : Vector3, + unk1 : Int, + unk2 : Int, + unk3 : Int, + unk4 : Boolean, + unk5 : Int) + extends PlanetSideGamePacket { + type Packet = ProjectileStateMessage + def opcode = GamePacketOpcode.ProjectileStateMessage + def encode = ProjectileStateMessage.encode(this) +} + +object ProjectileStateMessage extends Marshallable[ProjectileStateMessage] { + implicit val codec : Codec[ProjectileStateMessage] = ( + ("projectile_guid" | PlanetSideGUID.codec) :: + ("origin" | Vector3.codec_pos) :: + ("orient" | Vector3.codec_float) :: + ("unk1" | uint8L) :: + ("unk2" | uint8L) :: + ("unk3" | uint8L) :: + ("unk4" | bool) :: + ("unk5" | uint16L) + ).as[ProjectileStateMessage] +} diff --git a/common/src/test/scala/game/ProjectileStateMessageTest.scala b/common/src/test/scala/game/ProjectileStateMessageTest.scala new file mode 100644 index 00000000..020a1e80 --- /dev/null +++ b/common/src/test/scala/game/ProjectileStateMessageTest.scala @@ -0,0 +1,44 @@ +// Copyright (c) 2017 PSForever +package game + +import org.specs2.mutable._ +import net.psforever.packet._ +import net.psforever.packet.game._ +import net.psforever.types.Vector3 +import scodec.bits._ + +class ProjectileStateMessageTest extends Specification { + val string = hex"3f 259d c5019 30e4a 9514 c52c9541 d9ba05c2 c5973941 00 f8 ec 020000" + + "decode" in { + PacketCoding.DecodePacket(string).require match { + case ProjectileStateMessage(projectile, pos, aim, unk1, unk2, unk3, unk4, unk5) => + projectile mustEqual PlanetSideGUID(40229) + pos.x mustEqual 4611.539f + pos.y mustEqual 5576.375f + pos.z mustEqual 82.328125f + aim.x mustEqual 18.64686f + aim.y mustEqual -33.43247f + aim.z mustEqual 11.599553f + unk1 mustEqual 0 + unk2 mustEqual 248 + unk3 mustEqual 236 + unk4 mustEqual false + unk5 mustEqual 4 + case _ => + ko + } + } + + "encode" in { + val msg = ProjectileStateMessage( + PlanetSideGUID(40229), + Vector3(4611.539f, 5576.375f, 82.328125f), + Vector3(18.64686f, -33.43247f, 11.599553f), + 0, 248, 236, false, 4 + ) + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual string + } +} From 0b0507617ad40155f68703de4863e0006b0a6ced Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 15 Mar 2017 19:43:07 -0400 Subject: [PATCH 2/3] field names and comments --- .../packet/game/ProjectileStateMessage.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/common/src/main/scala/net/psforever/packet/game/ProjectileStateMessage.scala b/common/src/main/scala/net/psforever/packet/game/ProjectileStateMessage.scala index 2871cd8f..050c4cda 100644 --- a/common/src/main/scala/net/psforever/packet/game/ProjectileStateMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/ProjectileStateMessage.scala @@ -9,19 +9,19 @@ import scodec.codecs._ /** * Dispatched from the server to render the projectiles of one player's weapon on other players' clients. * @param projectile_guid the projectile - * @param origin a spawning position for the projectile - * @param orient a directional heading for the projectile + * @param shot_origin a spawning position for the projectile + * @param shot_vector a directional heading for the projectile * @param unk1 na; - * rarely not 0 + * usually 0 * @param unk2 na * @param unk3 na * @param unk4 na; - * rarely not false + * usually false * @param unk5 na */ final case class ProjectileStateMessage(projectile_guid : PlanetSideGUID, - origin : Vector3, - orient : Vector3, + shot_origin : Vector3, + shot_vector : Vector3, unk1 : Int, unk2 : Int, unk3 : Int, @@ -36,8 +36,8 @@ final case class ProjectileStateMessage(projectile_guid : PlanetSideGUID, object ProjectileStateMessage extends Marshallable[ProjectileStateMessage] { implicit val codec : Codec[ProjectileStateMessage] = ( ("projectile_guid" | PlanetSideGUID.codec) :: - ("origin" | Vector3.codec_pos) :: - ("orient" | Vector3.codec_float) :: + ("shot_origin" | Vector3.codec_pos) :: + ("shot_vector" | Vector3.codec_float) :: ("unk1" | uint8L) :: ("unk2" | uint8L) :: ("unk3" | uint8L) :: From 08f9f6a75fd0bb779819a84850b5307fc031550d Mon Sep 17 00:00:00 2001 From: FateJH Date: Tue, 2 May 2017 21:21:55 -0400 Subject: [PATCH 3/3] clarifying field names; updating comments; adding match cases in WSA --- .../packet/game/ProjectileStateMessage.scala | 42 +++++++++++++------ .../game/ProjectileStateMessageTest.scala | 10 ++--- .../src/main/scala/WorldSessionActor.scala | 5 ++- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/common/src/main/scala/net/psforever/packet/game/ProjectileStateMessage.scala b/common/src/main/scala/net/psforever/packet/game/ProjectileStateMessage.scala index 050c4cda..329d6742 100644 --- a/common/src/main/scala/net/psforever/packet/game/ProjectileStateMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/ProjectileStateMessage.scala @@ -7,26 +7,44 @@ import scodec.Codec import scodec.codecs._ /** - * Dispatched from the server to render the projectiles of one player's weapon on other players' clients. + * Dispatched to deliberately render certain projectiles of a weapon on other players' clients.
+ *
+ * This packet is generated by firing specific weapons in specific fire modes. + * For example, the Phoenix (`hunterseeker`) discharged in its primary fire mode generates this packet; + * but, the Phoenix in secondary fire mode does not. + * The Striker (`striker`) discharged in its primary fire mode generates this packet; + * but, the Striker in secondary fire mode does not. + * The chosen fire mode(s) are not a straight-fire projectile but one that has special control asserted over it. + * For the Phoenix, it is user-operated. + * For the Striker, it tracks towards a target while the weapon's reticle hovers over that target.
+ *
+ * This packet will continue to be dispatched by the client for as long as the projectile being tracked is in the air. + * All projectiles have a maximum lifespan before they will lose control and either despawn and/or explode. + * This number is tracked in the packet for simplicity. + * If the projectile strikes a valid target, the count will jump to a significantly enormous value beyond its normal lifespan. + * This ensures that the projectile - locally and the shared model - will despawn. * @param projectile_guid the projectile - * @param shot_origin a spawning position for the projectile - * @param shot_vector a directional heading for the projectile + * @param shot_pos the position of the projectile + * @param shot_vel the velocity of the projectile * @param unk1 na; * usually 0 - * @param unk2 na - * @param unk3 na + * @param unk2 na; + * will remain consistent for the lifespan of a given projectile in most cases + * @param unk3 na; + * will remain consistent for the lifespan of a given projectile in most cases * @param unk4 na; * usually false - * @param unk5 na + * @param time_alive how long the projectile has been in the air; + * often expressed in multiples of 2 */ final case class ProjectileStateMessage(projectile_guid : PlanetSideGUID, - shot_origin : Vector3, - shot_vector : Vector3, + shot_pos : Vector3, + shot_vel : Vector3, unk1 : Int, unk2 : Int, unk3 : Int, unk4 : Boolean, - unk5 : Int) + time_alive : Int) extends PlanetSideGamePacket { type Packet = ProjectileStateMessage def opcode = GamePacketOpcode.ProjectileStateMessage @@ -36,12 +54,12 @@ final case class ProjectileStateMessage(projectile_guid : PlanetSideGUID, object ProjectileStateMessage extends Marshallable[ProjectileStateMessage] { implicit val codec : Codec[ProjectileStateMessage] = ( ("projectile_guid" | PlanetSideGUID.codec) :: - ("shot_origin" | Vector3.codec_pos) :: - ("shot_vector" | Vector3.codec_float) :: + ("shot_pos" | Vector3.codec_pos) :: + ("shot_vel" | Vector3.codec_float) :: ("unk1" | uint8L) :: ("unk2" | uint8L) :: ("unk3" | uint8L) :: ("unk4" | bool) :: - ("unk5" | uint16L) + ("time_alive" | uint16L) ).as[ProjectileStateMessage] } diff --git a/common/src/test/scala/game/ProjectileStateMessageTest.scala b/common/src/test/scala/game/ProjectileStateMessageTest.scala index 020a1e80..f62e181e 100644 --- a/common/src/test/scala/game/ProjectileStateMessageTest.scala +++ b/common/src/test/scala/game/ProjectileStateMessageTest.scala @@ -12,19 +12,19 @@ class ProjectileStateMessageTest extends Specification { "decode" in { PacketCoding.DecodePacket(string).require match { - case ProjectileStateMessage(projectile, pos, aim, unk1, unk2, unk3, unk4, unk5) => + case ProjectileStateMessage(projectile, pos, vel, unk1, unk2, unk3, unk4, time_alive) => projectile mustEqual PlanetSideGUID(40229) pos.x mustEqual 4611.539f pos.y mustEqual 5576.375f pos.z mustEqual 82.328125f - aim.x mustEqual 18.64686f - aim.y mustEqual -33.43247f - aim.z mustEqual 11.599553f + vel.x mustEqual 18.64686f + vel.y mustEqual -33.43247f + vel.z mustEqual 11.599553f unk1 mustEqual 0 unk2 mustEqual 248 unk3 mustEqual 236 unk4 mustEqual false - unk5 mustEqual 4 + time_alive mustEqual 4 case _ => ko } diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 804149d7..1e502e8d 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -190,7 +190,7 @@ class WorldSessionActor extends Actor with MDCContextAware { log.debug("Object: " + obj) // LoadMapMessage 13714 in mossy .gcap // XXX: hardcoded shit - sendResponse(PacketCoding.CreateGamePacket(0, LoadMapMessage("map10","z10",40100,25,true,3770441820L))) //VS Sanctuary + sendResponse(PacketCoding.CreateGamePacket(0, LoadMapMessage("map13","home3",40100,25,true,3770441820L))) //VS Sanctuary sendResponse(PacketCoding.CreateGamePacket(0, ZonePopulationUpdateMessage(PlanetSideGUID(13), 414, 138, 0, 138, 0, 138, 0, 138, 0))) sendResponse(PacketCoding.CreateGamePacket(0, objectHex)) @@ -255,6 +255,9 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ ChildObjectStateMessage(object_guid : PlanetSideGUID, pitch : Int, yaw : Int) => //log.info("ChildObjectState: " + msg) + case msg @ ProjectileStateMessage(projectile_guid, shot_pos, shot_vector, unk1, unk2, unk3, unk4, time_alive) => + //log.info("ProjectileState: " + msg) + case msg @ ChatMsg(messagetype, has_wide_contents, recipient, contents, note_contents) => // TODO: Prevents log spam, but should be handled correctly if (messagetype != ChatMessageType.CMT_TOGGLE_GM) {