incoming control container packet unwinding has been shifted from LSA and WSA to PCA

This commit is contained in:
FateJH 2018-03-12 19:18:09 -04:00
parent 71173c171b
commit adb7738268
3 changed files with 109 additions and 134 deletions

View file

@ -10,6 +10,7 @@ import scodec.bits._
import MDCContextAware.Implicits._ import MDCContextAware.Implicits._
import com.github.mauricio.async.db.{Connection, QueryResult, RowData} import com.github.mauricio.async.db.{Connection, QueryResult, RowData}
import com.github.mauricio.async.db.mysql.exceptions.MySQLException import com.github.mauricio.async.db.mysql.exceptions.MySQLException
import net.psforever.objects.DefaultCancellable
import net.psforever.types.PlanetSideEmpire import net.psforever.types.PlanetSideEmpire
import scala.concurrent.{Await, Future} import scala.concurrent.{Await, Future}
@ -25,7 +26,7 @@ class LoginSessionActor extends Actor with MDCContextAware {
var leftRef : ActorRef = ActorRef.noSender var leftRef : ActorRef = ActorRef.noSender
var rightRef : ActorRef = ActorRef.noSender var rightRef : ActorRef = ActorRef.noSender
var updateServerListTask : Cancellable = LoginSessionActor.DefaultCancellable var updateServerListTask : Cancellable = DefaultCancellable.obj
override def postStop() = { override def postStop() = {
if(updateServerListTask != null) if(updateServerListTask != null)
@ -54,59 +55,22 @@ class LoginSessionActor extends Actor with MDCContextAware {
def Started : Receive = { def Started : Receive = {
case UpdateServerList() => case UpdateServerList() =>
updateServerList() updateServerList()
case ctrl @ ControlPacket(_, _) => case ControlPacket(_, ctrl) =>
handlePktContainer(ctrl)
case game @ GamePacket(_, _, _) =>
handlePktContainer(game)
case default => failWithError(s"Invalid packet class received: $default")
}
def handlePkt(pkt : PlanetSidePacket) : Unit = pkt match {
case ctrl : PlanetSideControlPacket =>
handleControlPkt(ctrl) handleControlPkt(ctrl)
case game : PlanetSideGamePacket => case GamePacket(_, _, game) =>
handleGamePkt(game) handleGamePkt(game)
case default => failWithError(s"Invalid packet class received: $default") case default => failWithError(s"Invalid packet class received: $default")
} }
def handlePktContainer(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 container class received: $default")
}
def handleControlPkt(pkt : PlanetSideControlPacket) = { def handleControlPkt(pkt : PlanetSideControlPacket) = {
pkt match { pkt match {
case SlottedMetaPacket(slot, subslot, innerPacket) =>
// Meta packets are like TCP packets - then need to be ACKed to the client
sendResponse(PacketCoding.CreateControlPacket(SlottedMetaAck(slot, subslot)))
// Decode the inner packet and handle it or error
PacketCoding.DecodePacket(innerPacket).fold({
error => log.error(s"Failed to decode inner packet of SlottedMetaPacket: $error")
}, {
handlePkt(_)
})
/// TODO: figure out what this is what what it does for the PS client /// TODO: figure out what this is what what it does for the PS client
/// I believe it has something to do with reliable packet transmission and resending /// I believe it has something to do with reliable packet transmission and resending
case sync @ ControlSync(diff, unk, f1, f2, f3, f4, fa, fb) => case sync @ ControlSync(diff, _, _, _, _, _, fa, fb) =>
log.trace(s"SYNC: $sync") log.trace(s"SYNC: $sync")
val serverTick = Math.abs(System.nanoTime().toInt) // limit the size to prevent encoding error val serverTick = Math.abs(System.nanoTime().toInt) // limit the size to prevent encoding error
sendResponse(PacketCoding.CreateControlPacket(ControlSyncResp(diff, serverTick, sendResponse(PacketCoding.CreateControlPacket(ControlSyncResp(diff, serverTick, fa, fb, fb, fa)))
fa, fb, fb, fa)))
case MultiPacket(packets) =>
/// Extract out each of the subpackets in the MultiPacket and handle them or raise a packet error
packets.foreach { pkt =>
PacketCoding.DecodePacket(pkt).fold({ error =>
log.error(s"Failed to decode inner packet of MultiPacket: $error")
}, {
handlePkt(_)
})
}
case default => case default =>
log.error(s"Unhandled ControlPacket $default") log.error(s"Unhandled ControlPacket $default")
} }
@ -119,7 +83,6 @@ class LoginSessionActor extends Actor with MDCContextAware {
// TESTING CODE FOR ACCOUNT LOOKUP // TESTING CODE FOR ACCOUNT LOOKUP
def accountLookup(username : String, password : String) : Boolean = { def accountLookup(username : String, password : String) : Boolean = {
val connection: Connection = DatabaseConnector.getAccountsConnection val connection: Connection = DatabaseConnector.getAccountsConnection
Await.result(connection.connect, 5 seconds) Await.result(connection.connect, 5 seconds)
// create account // create account
@ -135,13 +98,13 @@ class LoginSessionActor extends Actor with MDCContextAware {
case Some(resultSet) => case Some(resultSet) =>
val row : RowData = resultSet.head val row : RowData = resultSet.head
row(0) row(0)
case None => -1 case None =>
} -1
) })
try { try {
// XXX: remove awaits // XXX: remove awaits
val result = Await.result( mapResult, 5 seconds ) Await.result( mapResult, 5 seconds )
return true return true
} catch { } catch {
case e : MySQLException => case e : MySQLException =>
@ -151,7 +114,6 @@ class LoginSessionActor extends Actor with MDCContextAware {
} finally { } finally {
connection.disconnect connection.disconnect
} }
false false
} }
@ -190,22 +152,25 @@ class LoginSessionActor extends Actor with MDCContextAware {
log.info(s"Failed login to account $username") log.info(s"Failed login to account $username")
sendResponse(PacketCoding.CreateGamePacket(0, response)) sendResponse(PacketCoding.CreateGamePacket(0, response))
} }
case ConnectToWorldRequestMessage(name, _, _, _, _, _, _) => case ConnectToWorldRequestMessage(name, _, _, _, _, _, _) =>
log.info(s"Connect to world request for '$name'") log.info(s"Connect to world request for '$name'")
val response = ConnectToWorldMessage(serverName, serverAddress.getHostString, serverAddress.getPort) val response = ConnectToWorldMessage(serverName, serverAddress.getHostString, serverAddress.getPort)
sendResponse(PacketCoding.CreateGamePacket(0, response)) sendResponse(PacketCoding.CreateGamePacket(0, response))
sendResponse(DropSession(sessionId, "user transferring to world")) sendResponse(DropSession(sessionId, "user transferring to world"))
case default => log.debug(s"Unhandled GamePacket $pkt")
case _ =>
log.debug(s"Unhandled GamePacket $pkt")
} }
def updateServerList() = { def updateServerList() = {
val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ", val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ",
Vector( Vector(
WorldInformation(serverName, WorldStatus.Up, ServerType.Beta, WorldInformation(
Vector(WorldConnectionInfo(serverAddress)), PlanetSideEmpire.VS) serverName, WorldStatus.Up, ServerType.Beta, Vector(WorldConnectionInfo(serverAddress)), PlanetSideEmpire.VS
)) )
)
)
sendResponse(PacketCoding.CreateGamePacket(0, msg)) sendResponse(PacketCoding.CreateGamePacket(0, msg))
} }
@ -216,22 +181,13 @@ class LoginSessionActor extends Actor with MDCContextAware {
def sendResponse(cont : Any) = { def sendResponse(cont : Any) = {
log.trace("LOGIN SEND: " + cont) log.trace("LOGIN SEND: " + cont)
MDC("sessionId") = sessionId.toString MDC("sessionId") = sessionId.toString
rightRef !> cont rightRef !> cont
} }
def sendRawResponse(pkt : ByteVector) = { def sendRawResponse(pkt : ByteVector) = {
log.trace("LOGIN SEND RAW: " + pkt) log.trace("LOGIN SEND RAW: " + pkt)
MDC("sessionId") = sessionId.toString MDC("sessionId") = sessionId.toString
rightRef !> RawPacket(pkt) rightRef !> RawPacket(pkt)
} }
} }
object LoginSessionActor {
final val DefaultCancellable = new Cancellable() {
def isCancelled : Boolean = true
def cancel : Boolean = true
}
}

View file

@ -5,7 +5,7 @@ import scodec.Attempt.{Failure, Successful}
import scodec.bits._ import scodec.bits._
import org.log4s.MDC import org.log4s.MDC
import MDCContextAware.Implicits._ import MDCContextAware.Implicits._
import net.psforever.packet.control.{HandleGamePacket, SlottedMetaPacket} import net.psforever.packet.control._
/** /**
* In between the network side and the higher functioning side of the simulation: * In between the network side and the higher functioning side of the simulation:
@ -30,13 +30,14 @@ import net.psforever.packet.control.{HandleGamePacket, SlottedMetaPacket}
*/ */
class PacketCodingActor extends Actor with MDCContextAware { class PacketCodingActor extends Actor with MDCContextAware {
private var sessionId : Long = 0 private var sessionId : Long = 0
private var subslot : Int = 0 private var subslotOutbound : Int = 0
private var subslotInbound : Int = 0
private var leftRef : ActorRef = ActorRef.noSender private var leftRef : ActorRef = ActorRef.noSender
private var rightRef : ActorRef = ActorRef.noSender private var rightRef : ActorRef = ActorRef.noSender
private[this] val log = org.log4s.getLogger private[this] val log = org.log4s.getLogger
override def postStop() = { override def postStop() = {
subslot = 0 //in case this `Actor` restarts subslotOutbound = 0 //in case this `Actor` restarts
super.postStop() super.postStop()
} }
@ -68,12 +69,13 @@ class PacketCodingActor extends Actor with MDCContextAware {
mtuLimit(msg) mtuLimit(msg)
} }
else {//from network, to LSA, WSA, etc. - decode else {//from network, to LSA, WSA, etc. - decode
PacketCoding.unmarshalPayload(0, msg) match { //TODO is it safe for this to always be 0? UnmarshalInnerPacket(msg, "a packet")
case Successful(packet) => // PacketCoding.unmarshalPayload(0, msg) match { //TODO is it safe for this to always be 0?
sendResponseRight(packet) // case Successful(packet) =>
case Failure(ex) => // handlePacketContainer(packet) //sendResponseRight
log.info(s"Failed to marshal a packet: $ex") // case Failure(ex) =>
} // log.info(s"Failed to marshal a packet: $ex")
// }
} }
//known elevated packet type //known elevated packet type
case ctrl @ ControlPacket(_, packet) => case ctrl @ ControlPacket(_, packet) =>
@ -88,7 +90,7 @@ class PacketCodingActor extends Actor with MDCContextAware {
else { //deprecated; ControlPackets should not be coming from this direction else { //deprecated; ControlPackets should not be coming from this direction
log.warn(s"DEPRECATED CONTROL PACKET SEND: $ctrl") log.warn(s"DEPRECATED CONTROL PACKET SEND: $ctrl")
MDC("sessionId") = sessionId.toString MDC("sessionId") = sessionId.toString
sendResponseRight(ctrl) handlePacketContainer(ctrl) //sendResponseRight
} }
//known elevated packet type //known elevated packet type
case game @ GamePacket(_, _, packet) => case game @ GamePacket(_, _, packet) =>
@ -123,15 +125,15 @@ class PacketCodingActor extends Actor with MDCContextAware {
/** /**
* Retrieve the current subslot number. * Retrieve the current subslot number.
* Increment the `subslot` for the next time it is needed. * Increment the `subslot` for the next time it is needed.
* @return a 16u number starting at 0 * @return a `16u` number starting at 0
*/ */
def Subslot : Int = { def Subslot : Int = {
if(subslot == 65536) { //TODO what is the actual wrap number? if(subslotOutbound == 65536) { //TODO what is the actual wrap number?
subslot = 0 subslotOutbound = 0
subslot subslotOutbound
} else { } else {
val curr = subslot val curr = subslotOutbound
subslot += 1 subslotOutbound += 1
curr curr
} }
} }
@ -177,7 +179,7 @@ class PacketCodingActor extends Actor with MDCContextAware {
PacketCoding.EncodePacket(pkt.packet) match { PacketCoding.EncodePacket(pkt.packet) match {
case Successful(bdata) => case Successful(bdata) =>
sendResponseLeft(bdata.toByteVector) sendResponseLeft(bdata.toByteVector)
case f @ Failure(_) => case f : Failure =>
log.error(s"$f") log.error(s"$f")
} }
}) })
@ -193,6 +195,72 @@ class PacketCodingActor extends Actor with MDCContextAware {
leftRef !> RawPacket(cont) leftRef !> RawPacket(cont)
} }
/**
* Transform data into a container packet and re-submit that container to the process that handles the packet.
* @param data the packet data
* @param description an explanation of the input `data`
*/
def UnmarshalInnerPacket(data : ByteVector, description : String) : Unit = {
PacketCoding.unmarshalPayload(0, data) match { //TODO is it safe for this to always be 0?
case Successful(packet) =>
handlePacketContainer(packet)
case Failure(ex) =>
log.info(s"Failed to unmarshal $description: $ex")
}
}
/**
* Sort and redirect a container packet bound for the server by type of contents.
* `GamePacket` objects can just onwards without issue.
* `ControlPacket` objects may need to be dequeued.
* All other container types are invalid.
* @param container the container packet
*/
def handlePacketContainer(container : PlanetSidePacketContainer) : Unit = {
container match {
case _ : GamePacket =>
sendResponseRight(container)
case ControlPacket(_, ctrlPkt) =>
handleControlPacket(container, ctrlPkt)
case default =>
log.warn(s"Invalid packet container class received: ${default.getClass.getName}") //do not spill contents in log
}
}
/**
* Process a control packet or determine that it does not need to be processed at this level.
* Primarily, if the packet is of a type that contains another packet that needs be be unmarshalled,
* that/those packet must be unwound.<br>
* <br>
* The subslot information is used to identify these nested packets after arriving at their destination,
* to establish order for sequential packets and relation between divided packets.
* @param container the original container packet
* @param packet the packet that was extracted from the container
*/
def handleControlPacket(container : PlanetSidePacketContainer, packet : PlanetSideControlPacket) = {
packet match {
case SlottedMetaPacket(slot, subslot, innerPacket) =>
subslotInbound = subslot
self.tell(PacketCoding.CreateControlPacket(SlottedMetaAck(slot, subslot)), rightRef) //will go towards network
UnmarshalInnerPacket(innerPacket, "the inner packet of a SlottedMetaPacket")
case MultiPacket(packets) =>
packets.foreach { UnmarshalInnerPacket(_, "the inner packet of a MultiPacket") }
case MultiPacketEx(packets) =>
packets.foreach { UnmarshalInnerPacket(_, "the inner packet of a MultiPacketEx") }
case RelatedA0(subslot) =>
log.error(s"bad subslot data - $subslot; potential disarray")
case RelatedB0(subslot) =>
log.trace(s"good control packet received - subslot data $subslot")
case _ =>
sendResponseRight(container)
}
}
/** /**
* Decoded packet going towards the simulation. * Decoded packet going towards the simulation.
* @param cont the packet * @param cont the packet

View file

@ -137,10 +137,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
galaxy = endpoint galaxy = endpoint
log.info("ID: " + sessionId + " Got galaxy service " + endpoint) log.info("ID: " + sessionId + " Got galaxy service " + endpoint)
case ctrl @ ControlPacket(_, _) => case ControlPacket(_, ctrl) =>
handlePktContainer(ctrl) handleControlPkt(ctrl)
case game @ GamePacket(_, _, _) => case GamePacket(_, _, pkt) =>
handlePktContainer(game) handleGamePkt(pkt)
// temporary hack to keep the client from disconnecting // temporary hack to keep the client from disconnecting
case PokeClient() => case PokeClient() =>
sendResponse(KeepAliveMessage()) sendResponse(KeepAliveMessage())
@ -1091,61 +1091,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.warn(s"Invalid packet class received: $default") log.warn(s"Invalid packet class received: $default")
} }
def handlePkt(pkt : PlanetSidePacket) : Unit = pkt match {
case ctrl : PlanetSideControlPacket =>
handleControlPkt(ctrl)
case game : PlanetSideGamePacket =>
handleGamePkt(game)
case default => log.error(s"Invalid packet class received: $default")
}
def handlePktContainer(pkt : PlanetSidePacketContainer) : Unit = pkt match {
case ctrl @ ControlPacket(opcode, ctrlPkt) =>
handleControlPkt(ctrlPkt)
case game @ GamePacket(opcode, seq, gamePkt) =>
handleGamePkt(gamePkt)
case default => log.warn(s"Invalid packet container class received: $default")
}
def handleControlPkt(pkt : PlanetSideControlPacket) = { def handleControlPkt(pkt : PlanetSideControlPacket) = {
pkt match { pkt match {
case SlottedMetaPacket(slot, subslot, innerPacket) => case sync @ ControlSync(diff, _, _, _, _, _, fa, fb) =>
sendResponse(SlottedMetaAck(slot, subslot))
PacketCoding.DecodePacket(innerPacket) match {
case Failure(e) =>
log.error(s"Failed to decode inner packet of SlottedMetaPacket: $e")
case Successful(v) =>
handlePkt(v)
}
case sync @ ControlSync(diff, unk, f1, f2, f3, f4, fa, fb) =>
log.debug(s"SYNC: $sync") log.debug(s"SYNC: $sync")
val serverTick = Math.abs(System.nanoTime().toInt) // limit the size to prevent encoding error val serverTick = Math.abs(System.nanoTime().toInt) // limit the size to prevent encoding error
sendResponse(ControlSyncResp(diff, serverTick, fa, fb, fb, fa)) sendResponse(ControlSyncResp(diff, serverTick, fa, fb, fb, fa))
case MultiPacket(packets) =>
packets.foreach { pkt =>
PacketCoding.DecodePacket(pkt) match {
case Failure(e) =>
log.error(s"Failed to decode inner packet of MultiPacket: $e")
case Successful(v) =>
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 RelatedA0(subslot) =>
log.error(s"Client not ready for last control packet with subslot $subslot; potential system disarray")
case RelatedB0(subslot) =>
log.trace(s"Good control packet received $subslot")
case TeardownConnection(_) => case TeardownConnection(_) =>
log.info("Good bye") log.info("Good bye")