From edc0f57d39f0e24a79634a30db95828a044eb8dc Mon Sep 17 00:00:00 2001 From: FateJH Date: Wed, 9 Aug 2017 12:51:03 -0400 Subject: [PATCH] added comments, mainly --- .../packet/game/PropertyOverrideMessage.scala | 124 +++++++++++++++--- .../game/PropertyOverrideMessageTest.scala | 2 +- 2 files changed, 105 insertions(+), 21 deletions(-) diff --git a/common/src/main/scala/net/psforever/packet/game/PropertyOverrideMessage.scala b/common/src/main/scala/net/psforever/packet/game/PropertyOverrideMessage.scala index b7419ce9..39a5a7ae 100644 --- a/common/src/main/scala/net/psforever/packet/game/PropertyOverrideMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/PropertyOverrideMessage.scala @@ -6,6 +6,16 @@ import scodec.Codec import scodec.codecs._ import shapeless.{::, HNil} +/** + * Dispatched by the server to alert the client about custom permissions in different zones.
+ *
+ * The primarily way this packet was used on Gemini Live was to restrict weapons per zone. + * The Battle Island restrictions, for example, were enforced by these properties.
+ *
+ * Exploration:
+ * What else can you do with this packet? + * @param list a `List` defining scopes for the targets of internal property changes + */ final case class PropertyOverrideMessage(list : List[PropertyOverrideMessage.GamePropertyScope]) extends PlanetSideGamePacket { type Packet = PropertyOverrideMessage @@ -14,55 +24,116 @@ final case class PropertyOverrideMessage(list : List[PropertyOverrideMessage.Gam } object GamePropertyTarget { + /** + * A target value referring to general game properties. + * In the context of this `GamePacket`, usually scoped to a "global" zone. + */ final val game_properties : Int = 343 - def apply(target : Int) : PropertyOverrideMessage.GamePropertyTarget = { - PropertyOverrideMessage.GamePropertyTarget(target, Nil) - } - + /** + * Overloaded constructor for defining a target for a single paired key and value (String -> String). + * @param target the target + * @param kv the key-value pair + * @return a `PropertyOverrideMessage.GamePropertyTarget` association object + */ def apply(target : Int, kv : (String, String)) : PropertyOverrideMessage.GamePropertyTarget = { - PropertyOverrideMessage.GamePropertyTarget(target, PropertyOverrideMessage.GamePropertyValues(kv._1, kv._2) :: Nil) + PropertyOverrideMessage.GamePropertyTarget(target, PropertyOverrideMessage.GameProperty(kv._1, kv._2) :: Nil) } + /** + * Overloaded constructor for defining a target for a list of paired key and value. + * @param target the target + * @param list a `List` of key-value pairs + * @return a `PropertyOverrideMessage.GamePropertyTarget` association object + */ def apply(target : Int, list : List[(String, String)]) : PropertyOverrideMessage.GamePropertyTarget = { PropertyOverrideMessage.GamePropertyTarget(target, list.map({ case(key, value) => - PropertyOverrideMessage.GamePropertyValues(key, value) + PropertyOverrideMessage.GameProperty(key, value) })) } } object GamePropertyScope { - def apply(zone : Int, list : PropertyOverrideMessage.GamePropertyTarget) : PropertyOverrideMessage.GamePropertyScope = { - PropertyOverrideMessage.GamePropertyScope(zone, list :: Nil) + /** + * Overloaded constructor for defining a scope for the contained property. + * @param zone a game zone where this property is valid + * @param property a targeted key-value pair + * @return a `PropertyOverrideMessage.GamePropertyScope` association object + */ + def apply(zone : Int, property : PropertyOverrideMessage.GamePropertyTarget) : PropertyOverrideMessage.GamePropertyScope = { + PropertyOverrideMessage.GamePropertyScope(zone, property :: Nil) } + /** + * Overloaded constructor for defining a scope for the contained properties. + * @param zone a game zone where this property is valid + * @param list a `List` of targeted key-value pairs + * @return a `PropertyOverrideMessage.GamePropertyScope` association object + */ def apply(zone : Int, list : List[PropertyOverrideMessage.GamePropertyTarget]) : PropertyOverrideMessage.GamePropertyScope = { PropertyOverrideMessage.GamePropertyScope(zone, list) } } object PropertyOverrideMessage extends Marshallable[PropertyOverrideMessage] { - final case class GamePropertyValues(field1 : String, field2 : String) + /** + * A wrapper class for the key-value pair. + * Another class's overloading allows this to be parsed in a format `field1 -> field2` slightly more idiomatic to pairs. + * @param field1 usually the "key;" + * occasionally, the only param + * @param field2 the "value" + */ + final case class GameProperty(field1 : String, field2 : String) - final case class GamePropertyTarget(target : Int, list : List[GamePropertyValues]) + /** + * The association between a target and the properties that affect it. + * @param target what game object is affected by these properties + * @param list the properties + * @see `ObjectClass` + */ + final case class GamePropertyTarget(target : Int, list : List[GameProperty]) + /** + * The association between a continent/zone and how game objects are affected differently in that region. + * @param zone the continent/zone number; + * 0 refers to server-wide properties + * @param list the target and its property changes + */ final case class GamePropertyScope(zone : Int, list : List[GamePropertyTarget]) + /** + * Overloaded constructor for defining a single region where object properties are to be changed. + * @param list a list of regions, objects, and changed properties + * @return a `PropertyOverrideMessage` object + */ def apply(list : PropertyOverrideMessage.GamePropertyScope) : PropertyOverrideMessage = { PropertyOverrideMessage(list :: Nil) } - private def value_pair_aligned_codec(n : Int) : Codec[GamePropertyValues] = ( + /** + * `Codec` for two strings containing a key-value pair, with the key being padded. + * @param n the padding of the first `String` + * @return a `GameProperty` object + */ + private def value_pair_aligned_codec(n : Int) : Codec[GameProperty] = ( ("field1" | PacketHelpers.encodedStringAligned(n)) :: ("field2" | PacketHelpers.encodedString) - ).as[GamePropertyValues] + ).as[GameProperty] - private val value_pair_codec : Codec[GamePropertyValues] = ( + /** + * `Codec` for two strings containing a key-value pair. + */ + private val value_pair_codec : Codec[GameProperty] = ( ("field1" | PacketHelpers.encodedString) :: ("field2" | PacketHelpers.encodedString) - ).as[GamePropertyValues] + ).as[GameProperty] + /** + * `Codec` for defining the target and switching between and concatenating different key-value pair `Codec`s. + * @param n the padding of the first key in the first entry of the contents + * @return a `GamePropertyTarget` object + */ private def game_property_target_codec(n : Int) : Codec[GamePropertyTarget] = ( ("target" | uintL(11)) :: (uint16L >>:~ { len => @@ -71,6 +142,9 @@ object PropertyOverrideMessage extends Marshallable[PropertyOverrideMessage] { }) ).xmap[GamePropertyTarget] ( { + case target :: _ :: None :: None :: HNil => //unlikely + GamePropertyTarget(target, Nil) + case target :: _ :: Some(first) :: None :: HNil => GamePropertyTarget(target, first :: Nil) @@ -80,14 +154,22 @@ object PropertyOverrideMessage extends Marshallable[PropertyOverrideMessage] { { case GamePropertyTarget(target, list) => val (first, other) = list match { - case ((f : GamePropertyValues) +: (rest : List[GamePropertyValues])) => (Some(f), Some(rest)) - case (f : GamePropertyValues) +: Nil => (Some(f), None) - case Nil => (None, None) + case ((f : GameProperty) +: (rest : List[GameProperty])) => (Some(f), Some(rest)) + case (f : GameProperty) +: Nil => (Some(f), None) + case Nil => (None, None) //unlikely } target :: list.length :: first :: other :: HNil } ) + /** + * `Codec` for defining the scope and switching between and concatenating different alignments for the target `Codec`.
+ *
+ * For every first target entry of a scope, the leading property string will incur the displacement of two 11-bit fields. + * That property will be byte-aligned by two bits. + * For every subsequent scope, the leading property will only incur the displacement of a single 11-bit field. + * These properties will be byte-aligned by five bits. + */ private val game_property_scope_codec : Codec[GamePropertyScope] = ( ("zone" | uint16L) :: (uintL(11) >>:~ { len => @@ -96,6 +178,9 @@ object PropertyOverrideMessage extends Marshallable[PropertyOverrideMessage] { }) ).xmap[GamePropertyScope] ( { + case zone :: _ :: None :: None :: HNil => //unlikely + GamePropertyScope(zone, Nil) + case zone :: _ :: Some(first) :: None :: HNil => GamePropertyScope(zone, first :: Nil) @@ -107,12 +192,11 @@ object PropertyOverrideMessage extends Marshallable[PropertyOverrideMessage] { val (first, other) = list match { case ((f : GamePropertyTarget) +: (rest : List[GamePropertyTarget])) => (Some(f), Some(rest)) case (f : GamePropertyTarget) +: Nil => (Some(f), None) - case Nil => (None, None) + case Nil => (None, None) //unlikely } zone :: list.length :: first :: other :: HNil } ) - implicit val codec : Codec[PropertyOverrideMessage] = - listOfN(uint16L, game_property_scope_codec).as[PropertyOverrideMessage] + implicit val codec : Codec[PropertyOverrideMessage] = listOfN(uint16L, game_property_scope_codec).as[PropertyOverrideMessage] } diff --git a/common/src/test/scala/game/PropertyOverrideMessageTest.scala b/common/src/test/scala/game/PropertyOverrideMessageTest.scala index bf4c22dd..4eb301cd 100644 --- a/common/src/test/scala/game/PropertyOverrideMessageTest.scala +++ b/common/src/test/scala/game/PropertyOverrideMessageTest.scala @@ -157,7 +157,7 @@ class PropertyOverrideMessageTest extends Specification { // list(8).zone mustEqual 30 list(8).list.length mustEqual 21 - list(7).list.head.target mustEqual 83 + list(8).list.head.target mustEqual 83 list(8).list.head.list.length mustEqual 1 list(8).list.head.list.head.field1 mustEqual "allowed" list(8).list.head.list.head.field2 mustEqual "false"