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 050c4cdae..329d67429 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 020a1e808..f62e181e1 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 804149d78..1e502e8d8 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) {