Packet: MultiPacketEx, CharacterRequestMessage

Rename AggregatePacket -> MultiPacketEx as it makes more sense
This commit is contained in:
Chord 2016-06-03 20:25:20 -04:00
parent 9a0ef28723
commit 7b82491d0e
6 changed files with 143 additions and 4 deletions

View file

@ -41,7 +41,7 @@ object ControlPacketOpcode extends Enumeration {
RelatedB1,
RelatedB2,
RelatedB3,
AggregatePacket, // same as MultiPacket, but with the ability to send extended length packets
MultiPacketEx, // same as MultiPacket, but with the ability to send extended length packets
Unknown26,
Unknown27,
Unknown28,
@ -85,7 +85,7 @@ object ControlPacketOpcode extends Enumeration {
case RelatedB1 => noDecoder(opcode)
case RelatedB2 => noDecoder(opcode)
case RelatedB3 => noDecoder(opcode)
case AggregatePacket => noDecoder(opcode)
case MultiPacketEx => control.MultiPacketEx.decode
case Unknown26 => noDecoder(opcode)
case Unknown27 => noDecoder(opcode)
case Unknown28 => noDecoder(opcode)

View file

@ -67,7 +67,7 @@ object GamePacketOpcode extends Enumeration {
UnknownMessage45,
UnknownMessage46,
UnknownMessage47,
UnknownMessage48,
CharacterRequestMessage,
LoadMapMessage,
// OPCODE 50
@ -380,7 +380,7 @@ object GamePacketOpcode extends Enumeration {
case UnknownMessage45 => noDecoder(opcode)
case UnknownMessage46 => noDecoder(opcode)
case UnknownMessage47 => noDecoder(opcode)
case UnknownMessage48 => noDecoder(opcode)
case CharacterRequestMessage => game.CharacterRequestMessage.decode
case LoadMapMessage => noDecoder(opcode)
// OPCODE 50

View file

@ -0,0 +1,66 @@
// Copyright (c) 2016 PSForever.net to present
package net.psforever.packet.control
import net.psforever.packet.{ControlPacketOpcode, Marshallable, PlanetSideControlPacket}
import scodec.{Attempt, Codec, DecodeResult, SizeBound}
import scodec.bits._
import scodec.codecs._
final case class MultiPacketEx(packets : Vector[ByteVector])
extends PlanetSideControlPacket {
type Packet = MultiPacketEx
def opcode = ControlPacketOpcode.MultiPacketEx
def encode = MultiPacketEx.encode(this)
}
object MultiPacketEx extends Marshallable[MultiPacketEx] {
val ffFound: Codec[Boolean] = new Codec[Boolean] {
def sizeBound = SizeBound.exact(0)
def encode(b: Boolean) =
if(b)
Attempt.successful(hex"ff".bits)
else
Attempt.successful(bin"")
def decode(b: BitVector) = {
if (b.length >= 8 && b.take(8) == hex"ff".bits)
Attempt.successful(DecodeResult(true, b.drop(8)))
else
Attempt.successful(DecodeResult(false, b))
}
override def toString = "bitsRemaining"
}
val twoffFound: Codec[Boolean] = new Codec[Boolean] {
def sizeBound = SizeBound.exact(0)
def encode(b: Boolean) =
if(b)
Attempt.successful(hex"ffff".bits)
else
Attempt.successful(bin"")
def decode(b: BitVector) = {
if (b.length >= 16 && b.take(16) == hex"ffff".bits)
Attempt.successful(DecodeResult(true, b.drop(16)))
else
Attempt.successful(DecodeResult(false, b))
}
override def toString = "bitsRemaining"
}
val sizeCodec = either(ffFound, uint8L,
either(twoffFound, uint16L, uint32L).xmap[Long](
(a : Either[Int, Long]) => a.fold[Long](a => a, a => a),
(a : Long) =>
if(a < 0xffff) Left(a.toInt) else Right(a)
)
).xmap[Long](
(a : Either[Int, Long]) => a.fold[Long](a => a, a => a),
(a : Long) =>
if(a < 0xff) Left(a.toInt) else Right(a)
)
implicit val codec : Codec[MultiPacketEx] = (
("packets" | vector(variableSizeBytesLong(sizeCodec, bytes)))
).as[MultiPacketEx]
}

View file

@ -0,0 +1,24 @@
// Copyright (c) 2016 PSForever.net to present
package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import scodec.Codec
import scodec.codecs._
/**
* Is sent by the PlanetSide client when selecting a character to play from the character selection
* menu.
*/
final case class CharacterRequestMessage(unk : Long, unk2 : Long)
extends PlanetSideGamePacket {
type Packet = CharacterRequestMessage
def opcode = GamePacketOpcode.CharacterRequestMessage
def encode = CharacterRequestMessage.encode(this)
}
object CharacterRequestMessage extends Marshallable[CharacterRequestMessage] {
implicit val codec : Codec[CharacterRequestMessage] = (
("unk1" | uint32L) ::
("unk2" | uint32L)
).as[CharacterRequestMessage]
}

View file

@ -129,5 +129,45 @@ class ControlPacketTest extends Specification {
PacketCoding.EncodePacket(SlottedMetaPacket(0, 0x10000, hex"00")).require must throwA[IllegalArgumentException]
}
}
"MultiPacketEx" should {
val strings = Vector(
hex"00",
hex"01 41",
hex"01 41" ++ hex"02 4142",
hex"fe" ++ ByteVector.fill(0xfe)(0x41),
hex"ffff00" ++ ByteVector.fill(0xff)(0x41),
hex"ff0001" ++ ByteVector.fill(0x100)(0x41),
hex"ff ffff ffff 0000" ++ ByteVector.fill(0x0000ffff)(0x41),
hex"ff ffff 0000 0100" ++ ByteVector.fill(0x00010000)(0x41)
)
val packets = Vector(
MultiPacketEx(Vector(ByteVector.empty)),
MultiPacketEx(Vector(hex"41")),
MultiPacketEx(Vector(hex"41", hex"4142")),
MultiPacketEx(Vector(ByteVector.fill(0xfe)(0x41))),
MultiPacketEx(Vector(ByteVector.fill(0xff)(0x41))),
MultiPacketEx(Vector(ByteVector.fill(0x100)(0x41))),
MultiPacketEx(Vector(ByteVector.fill(0x0000ffff)(0x41))),
MultiPacketEx(Vector(ByteVector.fill(0x00010000)(0x41)))
)
"decode" in {
for(i <- strings.indices) {
MultiPacketEx.decode(strings{i}.bits).require.value mustEqual packets{i}
}
true mustEqual true
}
"encode" in {
for(i <- packets.indices) {
MultiPacketEx.encode(packets{i}).require.toByteVector mustEqual strings{i}
}
true mustEqual true
}
}
}
}

View file

@ -78,6 +78,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
handlePkt(v)
}
}
case MultiPacketEx(packets) =>
packets.foreach { pkt =>
PacketCoding.DecodePacket(pkt) match {
case Failure(e) =>
log.error(s"Failed to decode inner packet of MultiPacketEx: $e")
case Successful(v) =>
handlePkt(v)
}
}
case default =>
log.debug(s"Unhandled ControlPacket $default")
}