From 729b36eba1033ac9bb239f30fceb3c22a9bfe555 Mon Sep 17 00:00:00 2001 From: FateJH Date: Sun, 7 May 2017 23:27:02 -0400 Subject: [PATCH 1/3] initial work on OxygenStateMessage packet and tests --- .../psforever/packet/GamePacketOpcode.scala | 2 +- .../packet/game/OxygenStateMessage.scala | 89 +++++++++++++++++++ .../scala/game/OxygenStateMessageTest.scala | 53 +++++++++++ 3 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 common/src/main/scala/net/psforever/packet/game/OxygenStateMessage.scala create mode 100644 common/src/test/scala/game/OxygenStateMessageTest.scala diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index cad228880..e55065349 100644 --- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -461,7 +461,7 @@ object GamePacketOpcode extends Enumeration { case 0x76 => game.DeployableObjectsInfoMessage.decode case 0x77 => noDecoder(SquadState) // 0x78 - case 0x78 => noDecoder(OxygenStateMessage) + case 0x78 => game.OxygenStateMessage.decode case 0x79 => noDecoder(TradeMessage) case 0x7a => noDecoder(UnknownMessage122) case 0x7b => noDecoder(DamageFeedbackMessage) diff --git a/common/src/main/scala/net/psforever/packet/game/OxygenStateMessage.scala b/common/src/main/scala/net/psforever/packet/game/OxygenStateMessage.scala new file mode 100644 index 000000000..c75239772 --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/OxygenStateMessage.scala @@ -0,0 +1,89 @@ +// Copyright (c) 2017 PSForever +package net.psforever.packet.game + +import net.psforever.newcodecs.newcodecs +import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} +import scodec.{Attempt, Codec} +import scodec.codecs._ +import shapeless.{::, HNil} + +/** + * na + * @param vehicle_guid the player's mounted vehicle + * @param time the countdown's time upon start + * @param active show a new countdown if `true` (resets any active countdown); + * clear any active countdowns if `false` + */ +final case class WaterloggedVehicleState(vehicle_guid : PlanetSideGUID, + time : Double, + active : Boolean) + +/** + * na + * @param player_guid the player + * @param time the countdown's time upon start + * @param active show a new countdown if `true` (resets any active countdown); + * clear any active countdowns if `false` + * @param vehicle_state optional state of the vehicle the player is driving + */ +final case class OxygenStateMessage(player_guid : PlanetSideGUID, + time : Double, + active : Boolean, + vehicle_state : Option[WaterloggedVehicleState] = None) + extends PlanetSideGamePacket { + type Packet = OxygenStateMessage + def opcode = GamePacketOpcode.OxygenStateMessage + def encode = OxygenStateMessage.encode(this) +} + +object OxygenStateMessage extends Marshallable[OxygenStateMessage] { + /** + * Overloaded constructor that removes the optional state of the `WaterloggedVehicleState` parameter. + * @param player_guid the player + * @param time the countdown's time upon start + * @param active show or clear the countdown + * @param vehicle_state state of the vehicle the player is driving + * @return + */ + def apply(player_guid : PlanetSideGUID, time : Double, active : Boolean, vehicle_state : WaterloggedVehicleState) : OxygenStateMessage = + OxygenStateMessage(player_guid, time, active, Some(vehicle_state)) + + /** + * A simple pattern that expands the datatypes of the packet's basic `Codec`. + */ + private type basePattern = PlanetSideGUID :: Double :: Boolean :: HNil + + /** + * A `Codec` for the repeated processing of three values. + * This `Codec` is the basis for the packet's data. + */ + private val base_codec : Codec[basePattern] = + PlanetSideGUID.codec :: + newcodecs.q_double(0.0, 204.8, 11) :: //hackish: 2^11 == 2047, so it should be 204.7; but, 204.8 allows the decode == encode + bool + + implicit val codec : Codec[OxygenStateMessage] = ( + base_codec.exmap[basePattern] ( + { + case guid :: time :: active :: HNil => + Attempt.successful(guid :: time :: active :: HNil) + }, + { + case guid :: time :: active :: HNil => + Attempt.successful(guid :: time :: active :: HNil) + } + ) :+ + optional(bool, + "vehicle_state" | base_codec.exmap[WaterloggedVehicleState] ( + { + case guid :: time :: active :: HNil => + Attempt.successful(WaterloggedVehicleState(guid, time, active)) + }, + { + case WaterloggedVehicleState(guid, time, active) => + Attempt.successful(guid :: time :: active :: HNil) + } + ).as[WaterloggedVehicleState] + ) + ).as[OxygenStateMessage] +} diff --git a/common/src/test/scala/game/OxygenStateMessageTest.scala b/common/src/test/scala/game/OxygenStateMessageTest.scala new file mode 100644 index 000000000..2711128fd --- /dev/null +++ b/common/src/test/scala/game/OxygenStateMessageTest.scala @@ -0,0 +1,53 @@ +// Copyright (c) 2017 PSForever +package game + +import org.specs2.mutable._ +import net.psforever.packet._ +import net.psforever.packet.game._ +import scodec.bits._ + +class OxygenStateMessageTest extends Specification { + val string_self = hex"78 4b00f430" + val string_vehicle = hex"78 4b00f4385037a180" + + "decode (self)" in { + PacketCoding.DecodePacket(string_self).require match { + case OxygenStateMessage(guid, time, active, veh_state) => + guid mustEqual PlanetSideGUID(75) + time mustEqual 50.0 + active mustEqual true + veh_state.isDefined mustEqual false + case _ => + ko + } + } + + "decode (vehicle)" in { + PacketCoding.DecodePacket(string_vehicle).require match { + case OxygenStateMessage(guid, time, active, veh_state) => + guid mustEqual PlanetSideGUID(75) + time mustEqual 50.0 + active mustEqual true + veh_state.isDefined mustEqual true + veh_state.get.vehicle_guid mustEqual PlanetSideGUID(1546) + veh_state.get.time mustEqual 50.0 + veh_state.get.active mustEqual true + case _ => + ko + } + } + + "encode (self)" in { + val msg = OxygenStateMessage(PlanetSideGUID(75), 50.0, true) + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual string_self + } + + "encode (vehicle)" in { + val msg = OxygenStateMessage(PlanetSideGUID(75), 50.0, true, WaterloggedVehicleState(PlanetSideGUID(1546), 50.0, true)) + val pkt = PacketCoding.EncodePacket(msg).require.toByteVector + + pkt mustEqual string_vehicle + } +} From 17cfc1d62638200bff79473fa660f547842f02bb Mon Sep 17 00:00:00 2001 From: FateJH Date: Mon, 8 May 2017 08:34:59 -0400 Subject: [PATCH 2/3] changed Double values into Float values; wrote packet comments --- .../packet/game/OxygenStateMessage.scala | 42 ++++++++++++++----- .../scala/game/OxygenStateMessageTest.scala | 8 ++-- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/common/src/main/scala/net/psforever/packet/game/OxygenStateMessage.scala b/common/src/main/scala/net/psforever/packet/game/OxygenStateMessage.scala index c75239772..16ed7b87c 100644 --- a/common/src/main/scala/net/psforever/packet/game/OxygenStateMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/OxygenStateMessage.scala @@ -8,26 +8,46 @@ import scodec.codecs._ import shapeless.{::, HNil} /** - * na + * Alert the condition of a vehicle the player is using when going too far underwater. + * The player must be mounted to this vehicle at start time for this countdown to display. * @param vehicle_guid the player's mounted vehicle - * @param time the countdown's time upon start + * @param time the countdown's time, in seconds * @param active show a new countdown if `true` (resets any active countdown); - * clear any active countdowns if `false` + * clear any active countdowns if `false`; + * defaults to `true` */ final case class WaterloggedVehicleState(vehicle_guid : PlanetSideGUID, - time : Double, - active : Boolean) + time : Float, + active : Boolean = true) /** - * na + * Dispatched by the server to cause the player to slowly drown. + * If the player is mounted in a vehicle at the time, alert the player that the vehicle may be disabled.
+ *
+ * When a player walks too far underwater, a borderless red progress bar with a countdown from 100 (98) is displayed across the screen. + * The countdown proceeds to zero at a fixed rate and is timed with the depleting progress bar. + * When it reaches zero, the player will be killed. + * If the player is in a vehicle after a certain depth, a blue bar and countdown pair will superimpose the red indicators. + * It depletes much more rapidly than the red indicators. + * When it reaches zero, the vehicle will become disabled. + * All players in the vehicle's seats will be kicked and they will not be allowed back in.
+ *
+ * Normally, the countdowns should be set to begin at 100.0s. + * This is the earliest the drowning GUI will appear for either blue or red indicators. + * Passing greater intervals - up to 204.8s - will start the countdown silently but the GUI will be hidden until 100.0s. + * (The progress indicators will actually appear to start counting from 98%.) + * Managing the secondary vehicle countdown independent of the primary player countdown requires updating with the correct times. + * The countdown can be cancelled by instructing it to be `active = false`.
+ *
+ * Except for updating the indicators, all other functionality of "drowning" is automated by the server. * @param player_guid the player - * @param time the countdown's time upon start + * @param time the countdown's time, in seconds * @param active show a new countdown if `true` (resets any active countdown); * clear any active countdowns if `false` * @param vehicle_state optional state of the vehicle the player is driving */ final case class OxygenStateMessage(player_guid : PlanetSideGUID, - time : Double, + time : Float, active : Boolean, vehicle_state : Option[WaterloggedVehicleState] = None) extends PlanetSideGamePacket { @@ -45,13 +65,13 @@ object OxygenStateMessage extends Marshallable[OxygenStateMessage] { * @param vehicle_state state of the vehicle the player is driving * @return */ - def apply(player_guid : PlanetSideGUID, time : Double, active : Boolean, vehicle_state : WaterloggedVehicleState) : OxygenStateMessage = + def apply(player_guid : PlanetSideGUID, time : Float, active : Boolean, vehicle_state : WaterloggedVehicleState) : OxygenStateMessage = OxygenStateMessage(player_guid, time, active, Some(vehicle_state)) /** * A simple pattern that expands the datatypes of the packet's basic `Codec`. */ - private type basePattern = PlanetSideGUID :: Double :: Boolean :: HNil + private type basePattern = PlanetSideGUID :: Float :: Boolean :: HNil /** * A `Codec` for the repeated processing of three values. @@ -59,7 +79,7 @@ object OxygenStateMessage extends Marshallable[OxygenStateMessage] { */ private val base_codec : Codec[basePattern] = PlanetSideGUID.codec :: - newcodecs.q_double(0.0, 204.8, 11) :: //hackish: 2^11 == 2047, so it should be 204.7; but, 204.8 allows the decode == encode + newcodecs.q_float(0.0f, 204.8f, 11) :: //hackish: 2^11 == 2047, so it should be 204.7; but, 204.8 allows decode == encode bool implicit val codec : Codec[OxygenStateMessage] = ( diff --git a/common/src/test/scala/game/OxygenStateMessageTest.scala b/common/src/test/scala/game/OxygenStateMessageTest.scala index 2711128fd..65830e550 100644 --- a/common/src/test/scala/game/OxygenStateMessageTest.scala +++ b/common/src/test/scala/game/OxygenStateMessageTest.scala @@ -26,11 +26,11 @@ class OxygenStateMessageTest extends Specification { PacketCoding.DecodePacket(string_vehicle).require match { case OxygenStateMessage(guid, time, active, veh_state) => guid mustEqual PlanetSideGUID(75) - time mustEqual 50.0 + time mustEqual 50.0f active mustEqual true veh_state.isDefined mustEqual true veh_state.get.vehicle_guid mustEqual PlanetSideGUID(1546) - veh_state.get.time mustEqual 50.0 + veh_state.get.time mustEqual 50.0f veh_state.get.active mustEqual true case _ => ko @@ -38,14 +38,14 @@ class OxygenStateMessageTest extends Specification { } "encode (self)" in { - val msg = OxygenStateMessage(PlanetSideGUID(75), 50.0, true) + val msg = OxygenStateMessage(PlanetSideGUID(75), 50.0f, true) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string_self } "encode (vehicle)" in { - val msg = OxygenStateMessage(PlanetSideGUID(75), 50.0, true, WaterloggedVehicleState(PlanetSideGUID(1546), 50.0, true)) + val msg = OxygenStateMessage(PlanetSideGUID(75), 50.0f, true, WaterloggedVehicleState(PlanetSideGUID(1546), 50.0f, true)) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector pkt mustEqual string_vehicle From a1d9ad7ee4e8b6d67a4397db8c474bc21204ffde Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 10 May 2017 13:21:54 -0400 Subject: [PATCH 3/3] updated field name to more appropriately match status --- .../packet/game/OxygenStateMessage.scala | 26 ++++++++++--------- .../scala/game/OxygenStateMessageTest.scala | 10 +++---- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/common/src/main/scala/net/psforever/packet/game/OxygenStateMessage.scala b/common/src/main/scala/net/psforever/packet/game/OxygenStateMessage.scala index 16ed7b87c..54da0ab08 100644 --- a/common/src/main/scala/net/psforever/packet/game/OxygenStateMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/OxygenStateMessage.scala @@ -9,15 +9,16 @@ import shapeless.{::, HNil} /** * Alert the condition of a vehicle the player is using when going too far underwater. - * The player must be mounted to this vehicle at start time for this countdown to display. + * The player must be mounted in/on this vehicle at start time for this countdown to display. * @param vehicle_guid the player's mounted vehicle - * @param time the countdown's time, in seconds + * @param progress the remaining countdown; + * for vehicle waterlog condition, the progress per second rate is very high * @param active show a new countdown if `true` (resets any active countdown); * clear any active countdowns if `false`; * defaults to `true` */ final case class WaterloggedVehicleState(vehicle_guid : PlanetSideGUID, - time : Float, + progress : Float, active : Boolean = true) /** @@ -32,22 +33,23 @@ final case class WaterloggedVehicleState(vehicle_guid : PlanetSideGUID, * When it reaches zero, the vehicle will become disabled. * All players in the vehicle's seats will be kicked and they will not be allowed back in.
*
- * Normally, the countdowns should be set to begin at 100.0s. + * Normally, the countdowns should be set to begin at 100 (100.0). * This is the earliest the drowning GUI will appear for either blue or red indicators. - * Passing greater intervals - up to 204.8s - will start the countdown silently but the GUI will be hidden until 100.0s. - * (The progress indicators will actually appear to start counting from 98%.) - * Managing the secondary vehicle countdown independent of the primary player countdown requires updating with the correct times. + * Passing greater intervals - up to 204.8 - will start the countdown silently but the GUI will be hidden until 100.0. + * (The progress indicators will actually appear to start counting from 98.) + * Managing the secondary vehicle countdown independent of the primary player countdown requires updating with the correct levels. * The countdown can be cancelled by instructing it to be `active = false`.
*
* Except for updating the indicators, all other functionality of "drowning" is automated by the server. * @param player_guid the player - * @param time the countdown's time, in seconds + * @param progress the remaining countdown; + * for character oxygen, the progress per second rate is about 1 * @param active show a new countdown if `true` (resets any active countdown); * clear any active countdowns if `false` * @param vehicle_state optional state of the vehicle the player is driving */ final case class OxygenStateMessage(player_guid : PlanetSideGUID, - time : Float, + progress : Float, active : Boolean, vehicle_state : Option[WaterloggedVehicleState] = None) extends PlanetSideGamePacket { @@ -60,13 +62,13 @@ object OxygenStateMessage extends Marshallable[OxygenStateMessage] { /** * Overloaded constructor that removes the optional state of the `WaterloggedVehicleState` parameter. * @param player_guid the player - * @param time the countdown's time upon start + * @param progress the remaining countdown * @param active show or clear the countdown * @param vehicle_state state of the vehicle the player is driving * @return */ - def apply(player_guid : PlanetSideGUID, time : Float, active : Boolean, vehicle_state : WaterloggedVehicleState) : OxygenStateMessage = - OxygenStateMessage(player_guid, time, active, Some(vehicle_state)) + def apply(player_guid : PlanetSideGUID, progress : Float, active : Boolean, vehicle_state : WaterloggedVehicleState) : OxygenStateMessage = + OxygenStateMessage(player_guid, progress, active, Some(vehicle_state)) /** * A simple pattern that expands the datatypes of the packet's basic `Codec`. diff --git a/common/src/test/scala/game/OxygenStateMessageTest.scala b/common/src/test/scala/game/OxygenStateMessageTest.scala index 65830e550..034be18ef 100644 --- a/common/src/test/scala/game/OxygenStateMessageTest.scala +++ b/common/src/test/scala/game/OxygenStateMessageTest.scala @@ -12,9 +12,9 @@ class OxygenStateMessageTest extends Specification { "decode (self)" in { PacketCoding.DecodePacket(string_self).require match { - case OxygenStateMessage(guid, time, active, veh_state) => + case OxygenStateMessage(guid, progress, active, veh_state) => guid mustEqual PlanetSideGUID(75) - time mustEqual 50.0 + progress mustEqual 50.0 active mustEqual true veh_state.isDefined mustEqual false case _ => @@ -24,13 +24,13 @@ class OxygenStateMessageTest extends Specification { "decode (vehicle)" in { PacketCoding.DecodePacket(string_vehicle).require match { - case OxygenStateMessage(guid, time, active, veh_state) => + case OxygenStateMessage(guid, progress, active, veh_state) => guid mustEqual PlanetSideGUID(75) - time mustEqual 50.0f + progress mustEqual 50.0f active mustEqual true veh_state.isDefined mustEqual true veh_state.get.vehicle_guid mustEqual PlanetSideGUID(1546) - veh_state.get.time mustEqual 50.0f + veh_state.get.progress mustEqual 50.0f veh_state.get.active mustEqual true case _ => ko