Packet: BindPlayerMessage and tests (#104)

This commit is contained in:
Fate-JH 2017-03-05 12:24:57 -05:00 committed by pschord
parent b6c1fcc29d
commit a6a51009fa
4 changed files with 164 additions and 1 deletions

View file

@ -344,7 +344,7 @@ object GamePacketOpcode extends Enumeration {
case 0x13 => game.CharacterNoRecordMessage.decode
case 0x14 => game.CharacterInfoMessage.decode
case 0x15 => noDecoder(UnknownMessage21)
case 0x16 => noDecoder(BindPlayerMessage)
case 0x16 => game.BindPlayerMessage.decode
case 0x17 => noDecoder(ObjectCreateMessage_Duplicate)
// 0x18
case 0x18 => game.ObjectCreateMessage.decode

View file

@ -0,0 +1,88 @@
// Copyright (c) 2016 PSForever.net to present
package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.Vector3
import scodec.Codec
import scodec.codecs._
/**
* A packet dispatched to maintain a manually-set respawn location.<br>
* <br>
* The packet establishes the player's ability to spawn in an arbitrary location that is not a normal local option.
* This process is called "binding."
* In addition to player establishing the binding, the packet updates as conditions of the respawn location changes.<br>
* <br>
* If `logging` is turned on, packets will display different messages depending on context.
* As long as the event is marked to be logged, when the packet is received, a message is displayed in the events window.
* If the logged action is applicable, the matrixing sound effect will be played too.
* Not displaying events is occasionally warranted for aesthetics.
* The game will always note if this is your first time binding.<br>
* <br>
* One common occurrence of this packet is during zone transport.
* Specifically, a packet is dispatched after unloading the current zone but before beginning loading in the new zone.
* It is preceded by all of the `ObjectDeleteMessage` packets and itself precedes the `LoadMapMessage` packet.<br>
* <br>
* Actions:<br>
* `1` - bound to respawn point<br>
* `2` - general unbound / unbinding from respawn point<br>
* `3` - respawn point lost<br>
* `4` - bound spawn point became available<br>
* `5` - bound spawn point became unavailable (different from 3)<br>
* <br>
* Bind Descriptors:<br>
* `&#64;amp_station`<br>
* `&#64;ams`<br>
* `&#64;comm_station` (interlink facility?)<br>
* `&#64;comm_station_dsp` (dropship center?)<br>
* `&#64;cryo_facility` (biolab?)<br>
* `&#64;tech_plant`<br>
* <br>
* Exploration:<br>
* Find other bind descriptors.
* @param action the purpose of the packet
* @param bindDesc a description of the respawn binding point
* @param unk1 na; usually set `true` if there is more data in the packet ...
* @param logging true, to report on bind point change visible in the events window;
* false, to render spawn change silent;
* a first time event notification will always show
* @param unk2 na; if a value, it is usually 40 (hex`28`)
* @param unk3 na
* @param unk4 na
* @param pos a position associated with the binding
*/
final case class BindPlayerMessage(action : Int,
bindDesc : String,
unk1 : Boolean,
logging : Boolean,
unk2 : Int,
unk3 : Long,
unk4 : Long,
pos : Vector3)
extends PlanetSideGamePacket {
type Packet = BindPlayerMessage
def opcode = GamePacketOpcode.BindPlayerMessage
def encode = BindPlayerMessage.encode(this)
}
object BindPlayerMessage extends Marshallable[BindPlayerMessage] {
/**
* A common variant of this packet.
* `16028004000000000000000000000000000000`
*/
val STANDARD = BindPlayerMessage(2, "", false, false, 2, 0, 0, Vector3(0, 0, 0))
//TODO: there are two ignore(1) in this packet; are they in a good position?
implicit val codec : Codec[BindPlayerMessage] = (
("action" | uint8L) ::
("bindDesc" | PacketHelpers.encodedString) ::
("unk1" | bool) ::
("logging" | bool) ::
ignore(1) ::
("unk2" | uint4L) ::
ignore(1) ::
("unk3" | uint32L) ::
("unk4" | uint32L) ::
("pos" | Vector3.codec_pos)
).as[BindPlayerMessage]
}

View file

@ -0,0 +1,72 @@
// Copyright (c) 2016 PSForever.net to present
package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
import net.psforever.types.Vector3
import scodec.bits._
class BindPlayerMessageTest extends Specification {
val string_ams = hex"16 05 8440616D73 08 28000000 00000000 00000 00000 0000"
val string_tech = hex"16 01 8b40746563685f706c616e74 d4 28000000 38000000 00064 012b1 a044"
"decode (ams)" in {
PacketCoding.DecodePacket(string_ams).require match {
case BindPlayerMessage(action, bindDesc, unk1, logging, unk2, unk3, unk4, pos) =>
action mustEqual 5
bindDesc.length mustEqual 4
bindDesc mustEqual "@ams"
unk1 mustEqual false
logging mustEqual false
unk2 mustEqual 4
unk3 mustEqual 40
unk4 mustEqual 0
pos.x mustEqual 0f
pos.y mustEqual 0f
pos.z mustEqual 0f
case _ =>
ko
}
}
"decode (tech)" in {
PacketCoding.DecodePacket(string_tech).require match {
case BindPlayerMessage(action, bindDesc, unk1, logging, unk2, unk3, unk4, pos) =>
action mustEqual 1
bindDesc.length mustEqual 11
bindDesc mustEqual "@tech_plant"
unk1 mustEqual true
logging mustEqual true
unk2 mustEqual 10
unk3 mustEqual 40
unk4 mustEqual 56
pos.x mustEqual 2060.0f
pos.y mustEqual 598.0078f
pos.z mustEqual 274.5f
case _ =>
ko
}
}
"encode (ams)" in {
val msg = BindPlayerMessage(5, "@ams", false, false, 4, 40, 0, Vector3(0, 0, 0))
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_ams
}
"encode (tech)" in {
val msg = BindPlayerMessage(1, "@tech_plant", true, true, 10, 40, 56, Vector3(2060.0f, 598.0078f, 274.5f))
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_tech
}
"standard" in {
val msg = BindPlayerMessage.STANDARD
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual hex"16028004000000000000000000000000000000"
}
}

View file

@ -373,6 +373,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ BugReportMessage(version_major,version_minor,version_date,bug_type,repeatable,location,zone,pos,summary,desc) =>
log.info("BugReportMessage: " + msg)
case msg @ BindPlayerMessage(action, bindDesc, unk1, logging, unk2, unk3, unk4, pos) =>
log.info("BindPlayerMessage: " + msg)
case default => log.error(s"Unhandled GamePacket ${pkt}")
}