mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-26 13:24:45 +00:00
Merge pull request #156 from Fate-JH/property-override
Packet: PropertyOverrideMessage
This commit is contained in:
commit
83fd516dcc
|
|
@ -571,7 +571,7 @@ object GamePacketOpcode extends Enumeration {
|
|||
case 0xd2 => noDecoder(RespawnAMSInfoMessage)
|
||||
case 0xd3 => noDecoder(ComponentDamageMessage)
|
||||
case 0xd4 => noDecoder(GenericObjectActionAtPositionMessage)
|
||||
case 0xd5 => noDecoder(PropertyOverrideMessage)
|
||||
case 0xd5 => game.PropertyOverrideMessage.decode
|
||||
case 0xd6 => noDecoder(WarpgateLinkOverrideMessage)
|
||||
case 0xd7 => noDecoder(EmpireBenefitsMessage)
|
||||
// 0xd8
|
||||
|
|
|
|||
|
|
@ -0,0 +1,202 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* Dispatched by the server to alert the client about custom permissions in different zones.<br>
|
||||
* <br>
|
||||
* 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.<br>
|
||||
* <br>
|
||||
* Exploration:<br>
|
||||
* 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
|
||||
def opcode = GamePacketOpcode.PropertyOverrideMessage
|
||||
def encode = PropertyOverrideMessage.encode(this)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
/**
|
||||
* 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.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.GameProperty(key, value)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
object GamePropertyScope {
|
||||
/**
|
||||
* 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] {
|
||||
/**
|
||||
* 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)
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* `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[GameProperty]
|
||||
|
||||
/**
|
||||
* `Codec` for two strings containing a key-value pair.
|
||||
*/
|
||||
private val value_pair_codec : Codec[GameProperty] = (
|
||||
("field1" | PacketHelpers.encodedString) ::
|
||||
("field2" | PacketHelpers.encodedString)
|
||||
).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 =>
|
||||
conditional(len > 0, value_pair_aligned_codec(n)) ::
|
||||
conditional(len > 1, PacketHelpers.listOfNSized((len - 1).toLong, value_pair_codec))
|
||||
})
|
||||
).xmap[GamePropertyTarget] (
|
||||
{
|
||||
case target :: _ :: None :: None :: HNil => //unlikely
|
||||
GamePropertyTarget(target, Nil)
|
||||
|
||||
case target :: _ :: Some(first) :: None :: HNil =>
|
||||
GamePropertyTarget(target, first :: Nil)
|
||||
|
||||
case target :: _ :: Some(first) :: Some(other) :: HNil =>
|
||||
GamePropertyTarget(target, first +: other)
|
||||
},
|
||||
{
|
||||
case GamePropertyTarget(target, list) =>
|
||||
val (first, other) = list match {
|
||||
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`.<br>
|
||||
* <br>
|
||||
* 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 =>
|
||||
conditional(len > 0, game_property_target_codec(2)) ::
|
||||
conditional(len > 1, PacketHelpers.listOfNSized((len - 1).toLong, game_property_target_codec(5)))
|
||||
})
|
||||
).xmap[GamePropertyScope] (
|
||||
{
|
||||
case zone :: _ :: None :: None :: HNil => //unlikely
|
||||
GamePropertyScope(zone, Nil)
|
||||
|
||||
case zone :: _ :: Some(first) :: None :: HNil =>
|
||||
GamePropertyScope(zone, first :: Nil)
|
||||
|
||||
case zone :: _ :: Some(first) :: Some(other) :: HNil =>
|
||||
GamePropertyScope(zone, first +: other)
|
||||
},
|
||||
{
|
||||
case GamePropertyScope(zone, list) =>
|
||||
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) //unlikely
|
||||
}
|
||||
zone :: list.length :: first :: other :: HNil
|
||||
}
|
||||
)
|
||||
|
||||
implicit val codec : Codec[PropertyOverrideMessage] = listOfN(uint16L, game_property_scope_codec).as[PropertyOverrideMessage]
|
||||
}
|
||||
|
|
@ -304,15 +304,27 @@ object ObjectClass {
|
|||
final val ams_destroyed = 47
|
||||
final val ant = 60
|
||||
final val ant_destroyed = 61
|
||||
final val aphelion_flight = 83
|
||||
final val aphelion_gunner = 84
|
||||
final val aurora = 118
|
||||
final val battlewagon = 135 //raider
|
||||
final val colossus_flight = 199
|
||||
final val colossus_gunner = 200
|
||||
final val droppod = 258
|
||||
final val flail = 294
|
||||
final val fury = 335
|
||||
final val galaxy_gunship = 338
|
||||
final val liberator = 432
|
||||
final val lightgunship = 441
|
||||
final val lightning = 446
|
||||
final val lightning_destroyed = 447
|
||||
final val magrider = 470
|
||||
final val mediumtransport = 532
|
||||
final val mediumtransport_destroyed = 533
|
||||
final val orbital_shuttle = 608
|
||||
final val peregrine_flight = 642
|
||||
final val peregrine_gunner = 643
|
||||
final val prowler = 697
|
||||
final val quadassault = 707
|
||||
final val quadassault_destroyed = 708
|
||||
final val quadstealth = 710
|
||||
|
|
@ -328,6 +340,8 @@ object ObjectClass {
|
|||
final val twomanheavybuggy_destroyed = 899
|
||||
final val twomanhoverbuggy = 900 //thresher
|
||||
final val twomanhoverbuggy_destroyed = 901
|
||||
final val vanguard = 923
|
||||
final val vulture = 986
|
||||
//other
|
||||
final val ams_respawn_tube = 49
|
||||
final val avatar = 121
|
||||
|
|
|
|||
548
common/src/test/scala/game/PropertyOverrideMessageTest.scala
Normal file
548
common/src/test/scala/game/PropertyOverrideMessageTest.scala
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue