mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-15 16:34:39 +00:00
Finish coding for SlottedMetaPacket and wrote test
This commit is contained in:
parent
ff4ea792ce
commit
c7f70e3543
7 changed files with 147 additions and 38 deletions
|
|
@ -51,19 +51,23 @@ object ControlPacketOpcode extends Enumeration {
|
||||||
Unknown30
|
Unknown30
|
||||||
= Value
|
= Value
|
||||||
|
|
||||||
def getPacketDecoder(opcode : ControlPacketOpcode.Type) : (BitVector) => Attempt[DecodeResult[PlanetSideControlPacket]] = {
|
def getPacketDecoder(opcode : ControlPacketOpcode.Type) : (BitVector) => Attempt[DecodeResult[PlanetSideControlPacket]] = opcode match {
|
||||||
import net.psforever
|
|
||||||
|
|
||||||
opcode match {
|
|
||||||
case HandleGamePacket => control.HandleGamePacket.decode
|
case HandleGamePacket => control.HandleGamePacket.decode
|
||||||
case ServerStart => control.ServerStart.decode
|
case ServerStart => control.ServerStart.decode
|
||||||
case ClientStart => control.ClientStart.decode
|
case ClientStart => control.ClientStart.decode
|
||||||
case MultiPacket => control.MultiPacket.decode
|
case MultiPacket => control.MultiPacket.decode
|
||||||
case SlottedMetaPacket0 => SlottedMetaPacket.decode
|
// IT'S GETTING "WET" IN HERE
|
||||||
|
case SlottedMetaPacket0 => SlottedMetaPacket.decodeWithOpcode(SlottedMetaPacket0)
|
||||||
|
case SlottedMetaPacket1 => SlottedMetaPacket.decodeWithOpcode(SlottedMetaPacket1)
|
||||||
|
case SlottedMetaPacket2 => SlottedMetaPacket.decodeWithOpcode(SlottedMetaPacket2)
|
||||||
|
case SlottedMetaPacket3 => SlottedMetaPacket.decodeWithOpcode(SlottedMetaPacket3)
|
||||||
|
case SlottedMetaPacket4 => SlottedMetaPacket.decodeWithOpcode(SlottedMetaPacket4)
|
||||||
|
case SlottedMetaPacket5 => SlottedMetaPacket.decodeWithOpcode(SlottedMetaPacket5)
|
||||||
|
case SlottedMetaPacket6 => SlottedMetaPacket.decodeWithOpcode(SlottedMetaPacket6)
|
||||||
|
case SlottedMetaPacket7 => SlottedMetaPacket.decodeWithOpcode(SlottedMetaPacket7)
|
||||||
case ConnectionClose => control.ConnectionClose.decode
|
case ConnectionClose => control.ConnectionClose.decode
|
||||||
case default => (a : BitVector) => Attempt.failure(Err(s"Could not find a marshaller for control packet ${opcode}"))
|
case default => (a : BitVector) => Attempt.failure(Err(s"Could not find a marshaller for control packet ${opcode}"))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
implicit val codec: Codec[this.Value] = PacketHelpers.createEnumerationCodec(this, uint8L)
|
implicit val codec: Codec[this.Value] = PacketHelpers.createEnumerationCodec(this, uint8L)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,7 @@ object CryptoPacketOpcode extends Enumeration {
|
||||||
val Ignore, ClientChallengeXchg, ServerChallengeXchg,
|
val Ignore, ClientChallengeXchg, ServerChallengeXchg,
|
||||||
ClientFinished, ServerFinished = Value
|
ClientFinished, ServerFinished = Value
|
||||||
|
|
||||||
def getPacketDecoder(opcode : CryptoPacketOpcode.Type) : (BitVector) => Attempt[DecodeResult[PlanetSideCryptoPacket]] = {
|
def getPacketDecoder(opcode : CryptoPacketOpcode.Type) : (BitVector) => Attempt[DecodeResult[PlanetSideCryptoPacket]] = opcode match {
|
||||||
import net.psforever
|
|
||||||
|
|
||||||
opcode match {
|
|
||||||
case ClientChallengeXchg => crypto.ClientChallengeXchg.decode
|
case ClientChallengeXchg => crypto.ClientChallengeXchg.decode
|
||||||
case ServerChallengeXchg => crypto.ServerChallengeXchg.decode
|
case ServerChallengeXchg => crypto.ServerChallengeXchg.decode
|
||||||
case ServerFinished => crypto.ServerFinished.decode
|
case ServerFinished => crypto.ServerFinished.decode
|
||||||
|
|
@ -21,5 +18,4 @@ object CryptoPacketOpcode extends Enumeration {
|
||||||
case default => (a : BitVector) => Attempt.failure(Err(s"Could not find a marshaller for crypto packet ${opcode}")
|
case default => (a : BitVector) => Attempt.failure(Err(s"Could not find a marshaller for crypto packet ${opcode}")
|
||||||
.pushContext("get_marshaller"))
|
.pushContext("get_marshaller"))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,16 +31,12 @@ object GamePacketOpcode extends Enumeration {
|
||||||
DismountVehicleMsg
|
DismountVehicleMsg
|
||||||
= Value
|
= Value
|
||||||
|
|
||||||
def getPacketDecoder(opcode : GamePacketOpcode.Type) : (BitVector) => Attempt[DecodeResult[PlanetSideGamePacket]] = {
|
def getPacketDecoder(opcode : GamePacketOpcode.Type) : (BitVector) => Attempt[DecodeResult[PlanetSideGamePacket]] = opcode match {
|
||||||
import net.psforever
|
|
||||||
|
|
||||||
opcode match {
|
|
||||||
case LoginMessage => game.LoginMessage.decode
|
case LoginMessage => game.LoginMessage.decode
|
||||||
case LoginRespMessage => game.LoginRespMessage.decode
|
case LoginRespMessage => game.LoginRespMessage.decode
|
||||||
case VNLWorldStatusMessage => game.VNLWorldStatusMessage.decode
|
case VNLWorldStatusMessage => game.VNLWorldStatusMessage.decode
|
||||||
case default => (a : BitVector) => Attempt.failure(Err(s"Could not find a marshaller for game packet ${opcode}"))
|
case default => (a : BitVector) => Attempt.failure(Err(s"Could not find a marshaller for game packet ${opcode}"))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
implicit val codec: Codec[this.Value] = PacketHelpers.createEnumerationCodec(this, uint8L)
|
implicit val codec: Codec[this.Value] = PacketHelpers.createEnumerationCodec(this, uint8L)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,27 +3,34 @@ package net.psforever.packet.control
|
||||||
|
|
||||||
import net.psforever.packet.{ControlPacketOpcode, Marshallable, PlanetSideControlPacket}
|
import net.psforever.packet.{ControlPacketOpcode, Marshallable, PlanetSideControlPacket}
|
||||||
import scodec.Codec
|
import scodec.Codec
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.{BitVector, ByteOrdering, ByteVector}
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
|
|
||||||
final case class SlottedMetaPacket(/*slot : Int,*/ packet : ByteVector)
|
final case class SlottedMetaPacket(slot : Int, subslot : Int, packet : ByteVector)
|
||||||
extends PlanetSideControlPacket {
|
extends PlanetSideControlPacket {
|
||||||
type Packet = SlottedMetaPacket
|
type Packet = SlottedMetaPacket
|
||||||
|
|
||||||
//assert(slot >= 0 && slot <= 7, "Slot number is out of range")
|
assert(slot >= 0 && slot <= 7, s"Slot number ($slot) is out of range")
|
||||||
|
|
||||||
def opcode = {
|
def opcode = {
|
||||||
val base = ControlPacketOpcode.SlottedMetaPacket0.id
|
val base = ControlPacketOpcode.SlottedMetaPacket0.id
|
||||||
ControlPacketOpcode(base/* + slot*/)
|
ControlPacketOpcode(base + slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
def encode = SlottedMetaPacket.encode(this)
|
// XXX: a nasty hack to ignore the "slot" field
|
||||||
|
// There is so much wrong with this it's not even funny. Why scodec, whyyyy...
|
||||||
|
// I've never had a library make me feel so stupid and smart at the same time
|
||||||
|
def encode = SlottedMetaPacket.encode(this).map(vect => vect.drop(8))
|
||||||
}
|
}
|
||||||
|
|
||||||
object SlottedMetaPacket extends Marshallable[SlottedMetaPacket] {
|
object SlottedMetaPacket extends Marshallable[SlottedMetaPacket] {
|
||||||
implicit val codec : Codec[SlottedMetaPacket] = (
|
implicit val codec : Codec[SlottedMetaPacket] = (
|
||||||
("unknown" | constant(0)) ::
|
("slot" | uint8L.xmap[Int](a => a - ControlPacketOpcode.SlottedMetaPacket0.id, a=>a) ) ::
|
||||||
("unknown" | constant(0)) ::
|
("subslot" | uint16) :: // the slot is big endian. see 0x00A42F76
|
||||||
("rest" | bytes)
|
("rest" | bytes)
|
||||||
).as[SlottedMetaPacket]
|
).as[SlottedMetaPacket]
|
||||||
|
|
||||||
|
def decodeWithOpcode(slot : ControlPacketOpcode.Value)(bits : BitVector) = {
|
||||||
|
decode(ControlPacketOpcode.codec.encode(slot).require ++ bits)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
82
common/src/test/scala/ControlPacketTest.scala
Normal file
82
common/src/test/scala/ControlPacketTest.scala
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
// Copyright (c) 2016 PSForever.net to present
|
||||||
|
|
||||||
|
import org.specs2.mutable._
|
||||||
|
import net.psforever.packet._
|
||||||
|
import net.psforever.packet.control._
|
||||||
|
import scodec.Attempt.Successful
|
||||||
|
import scodec.bits._
|
||||||
|
import scodec.codecs.uint16
|
||||||
|
|
||||||
|
class ControlPacketTest extends Specification {
|
||||||
|
|
||||||
|
"PlanetSide control packet" in {
|
||||||
|
val cNonce = 656287232
|
||||||
|
|
||||||
|
"SlottedMetaPacket" should {
|
||||||
|
val string = hex"00 09 00 00 00194302484C36563130433F" ++
|
||||||
|
hex"4C6835316369774A0000000018FABE0C" ++
|
||||||
|
hex"00000000000000000000000001000000" ++
|
||||||
|
hex"020000006B7BD8288C6469666671756F" ++
|
||||||
|
hex"7469656E740000000000440597570065" ++
|
||||||
|
hex"006C0063006F006D006500200074006F" ++
|
||||||
|
hex"00200050006C0061006E006500740053" ++
|
||||||
|
hex"0069006400650021002000018667656D" ++
|
||||||
|
hex"696E690100040001459E2540377540"
|
||||||
|
|
||||||
|
def createMetaPacket(slot : Int, subslot : Int, rest : ByteVector) = hex"00" ++
|
||||||
|
ControlPacketOpcode.codec.encode(
|
||||||
|
ControlPacketOpcode(ControlPacketOpcode.SlottedMetaPacket0.id + slot)
|
||||||
|
).require.toByteVector ++ uint16.encode(subslot).require.toByteVector ++ rest
|
||||||
|
|
||||||
|
"decode as the base slot and subslot" in {
|
||||||
|
PacketCoding.DecodePacket(string).require match {
|
||||||
|
case SlottedMetaPacket(slot, subslot, rest) =>
|
||||||
|
slot mustEqual 0
|
||||||
|
subslot mustEqual 0
|
||||||
|
rest mustEqual string.drop(4)
|
||||||
|
case default =>
|
||||||
|
true mustEqual false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"decode as an arbitrary slot and subslot" in {
|
||||||
|
val maxSlots = ControlPacketOpcode.SlottedMetaPacket7.id - ControlPacketOpcode.SlottedMetaPacket0.id
|
||||||
|
|
||||||
|
// create all possible SlottedMetaPackets
|
||||||
|
for(i <- 0 until maxSlots) {
|
||||||
|
val subslot = 12323
|
||||||
|
val pkt = createMetaPacket(i, subslot, ByteVector.empty)
|
||||||
|
|
||||||
|
PacketCoding.DecodePacket(pkt).require match {
|
||||||
|
case SlottedMetaPacket(slot, subslotDecoded, rest) =>
|
||||||
|
|
||||||
|
// XXX: there isn't a simple solution to Slot0 and Slot4 be aliases of each other structurally
|
||||||
|
// This is probably best left to higher layers
|
||||||
|
//slot mustEqual i % 4 // this is seen at 0x00A3FBFA
|
||||||
|
slot mustEqual i
|
||||||
|
subslotDecoded mustEqual subslot
|
||||||
|
rest mustEqual ByteVector.empty // empty in this case
|
||||||
|
case default =>
|
||||||
|
true mustEqual false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode" in {
|
||||||
|
val encoded = PacketCoding.EncodePacket(SlottedMetaPacket(0, 0x1000, ByteVector.empty)).require
|
||||||
|
val encoded2 = PacketCoding.EncodePacket(SlottedMetaPacket(3, 0xffff, hex"414243")).require
|
||||||
|
val encoded3 = PacketCoding.EncodePacket(SlottedMetaPacket(7, 0, hex"00")).require
|
||||||
|
|
||||||
|
encoded.toByteVector mustEqual createMetaPacket(0, 0x1000, ByteVector.empty)
|
||||||
|
encoded2.toByteVector mustEqual createMetaPacket(3, 0xffff, hex"414243")
|
||||||
|
encoded3.toByteVector mustEqual createMetaPacket(7, 0, hex"00")
|
||||||
|
|
||||||
|
PacketCoding.EncodePacket(SlottedMetaPacket(8, 0, hex"00")).require must throwA[AssertionError]
|
||||||
|
PacketCoding.EncodePacket(SlottedMetaPacket(-1, 0, hex"00")).require must throwA[AssertionError]
|
||||||
|
PacketCoding.EncodePacket(SlottedMetaPacket(0, 0x10000, hex"00")).require must throwA[IllegalArgumentException]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -260,18 +260,23 @@ class CryptoSessionActor extends Actor with MDCContextAware {
|
||||||
val packet = PacketCoding.encryptPacket(cryptoState.get, cont).require
|
val packet = PacketCoding.encryptPacket(cryptoState.get, cont).require
|
||||||
sendResponse(packet)
|
sendResponse(packet)
|
||||||
} else {
|
} else {
|
||||||
log.error("Invalid sender")
|
log.error(s"Invalid sender when handling a message in Established ${from}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def sendResponse(cont : PlanetSidePacketContainer) : ByteVector = {
|
def sendResponse(cont : PlanetSidePacketContainer) : ByteVector = {
|
||||||
//println("CRYPTO SEND: " + cont)
|
//println("CRYPTO SEND: " + cont)
|
||||||
val pkt = PacketCoding.MarshalPacket(cont).require
|
val pkt = PacketCoding.MarshalPacket(cont)
|
||||||
val bytes = pkt.toByteVector
|
|
||||||
|
|
||||||
leftRef ! ResponsePacket(bytes)
|
pkt match {
|
||||||
|
case Failure(e) =>
|
||||||
bytes
|
log.error(s"Failed to marshal packet ${cont.getClass.getName} when sending response")
|
||||||
|
ByteVector.empty
|
||||||
|
case Successful(v) =>
|
||||||
|
val bytes = v.toByteVector
|
||||||
|
leftRef ! ResponsePacket(bytes)
|
||||||
|
bytes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def getRandBytes(amount : Int) : ByteVector = {
|
def getRandBytes(amount : Int) : ByteVector = {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
// Copyright (c) 2016 PSForever.net to present
|
// Copyright (c) 2016 PSForever.net to present
|
||||||
import java.net.{InetAddress, InetSocketAddress}
|
import java.net.{InetAddress, InetSocketAddress}
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorLogging, ActorRef, Identify, MDCContextAware}
|
import akka.actor.{Actor, ActorRef, MDCContextAware}
|
||||||
import net.psforever.packet._
|
import net.psforever.packet._
|
||||||
import net.psforever.packet.control.{ConnectionClose, SlottedMetaPacket}
|
import net.psforever.packet.control._
|
||||||
import net.psforever.packet.game._
|
import net.psforever.packet.game._
|
||||||
import scodec.Attempt.{Failure, Successful}
|
import scodec.Attempt.{Failure, Successful}
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
|
@ -28,16 +28,24 @@ class LoginSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
|
|
||||||
def Started : Receive = {
|
def Started : Receive = {
|
||||||
case ctrl @ ControlPacket(opcode, pkt) =>
|
case ctrl @ ControlPacket(_, _) =>
|
||||||
handleControlPkt(pkt)
|
handlePkt(ctrl)
|
||||||
case game @ GamePacket(opcode, seq, pkt) =>
|
case game @ GamePacket(_, _, _) =>
|
||||||
handleGamePkt(pkt)
|
handlePkt(game)
|
||||||
case default => failWithError(s"Invalid message received $default")
|
case default => failWithError(s"Invalid packet class received: $default")
|
||||||
|
}
|
||||||
|
|
||||||
|
def handlePkt(pkt : PlanetSidePacketContainer) : Unit = pkt match {
|
||||||
|
case ctrl @ ControlPacket(opcode, ctrlPkt) =>
|
||||||
|
handleControlPkt(ctrlPkt)
|
||||||
|
case game @ GamePacket(opcode, seq, gamePkt) =>
|
||||||
|
handleGamePkt(gamePkt)
|
||||||
|
case default => failWithError(s"Invalid packet class received: $default")
|
||||||
}
|
}
|
||||||
|
|
||||||
def handleControlPkt(pkt : PlanetSideControlPacket) = {
|
def handleControlPkt(pkt : PlanetSideControlPacket) = {
|
||||||
pkt match {
|
pkt match {
|
||||||
case SlottedMetaPacket(innerPacket) =>
|
case meta @ SlottedMetaPacket(slot, subslot, innerPacket) =>
|
||||||
PacketCoding.DecodePacket(innerPacket) match {
|
PacketCoding.DecodePacket(innerPacket) match {
|
||||||
case Successful(p) =>
|
case Successful(p) =>
|
||||||
log.trace("RECV[INNER]: " + p)
|
log.trace("RECV[INNER]: " + p)
|
||||||
|
|
@ -59,6 +67,17 @@ class LoginSessionActor extends Actor with MDCContextAware {
|
||||||
sendResponse(PacketCoding.CreateGamePacket(0, msg))
|
sendResponse(PacketCoding.CreateGamePacket(0, msg))
|
||||||
case Failure(e) => log.error("Failed to decode inner packet " + e)
|
case Failure(e) => log.error("Failed to decode inner packet " + e)
|
||||||
}
|
}
|
||||||
|
case MultiPacket(packets) =>
|
||||||
|
packets.foreach { pkt =>
|
||||||
|
PacketCoding.UnmarshalPacket(pkt) match {
|
||||||
|
case Failure(e) =>
|
||||||
|
log.error(s"Failed to decode inner packet of MultiPacket: $e")
|
||||||
|
case Successful(v) =>
|
||||||
|
handlePkt(v) // dont send a message to ourselves as then packets will be processed in the wrong order
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case default =>
|
||||||
|
log.debug(s"Unhandled ControlPacket $default")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,7 +87,7 @@ class LoginSessionActor extends Actor with MDCContextAware {
|
||||||
|
|
||||||
def failWithError(error : String) = {
|
def failWithError(error : String) = {
|
||||||
log.error(error)
|
log.error(error)
|
||||||
sendResponse(PacketCoding.CreateControlPacket(ConnectionClose()))
|
//sendResponse(PacketCoding.CreateControlPacket(ConnectionClose()))
|
||||||
}
|
}
|
||||||
|
|
||||||
def sendResponse(cont : PlanetSidePacketContainer) = {
|
def sendResponse(cont : PlanetSidePacketContainer) = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue