diff --git a/pslogin/src/main/scala/LoginSessionActor.scala b/pslogin/src/main/scala/LoginSessionActor.scala
index 9010e0437..6260563c7 100644
--- a/pslogin/src/main/scala/LoginSessionActor.scala
+++ b/pslogin/src/main/scala/LoginSessionActor.scala
@@ -10,6 +10,7 @@ import scodec.bits._
import MDCContextAware.Implicits._
import com.github.mauricio.async.db.{Connection, QueryResult, RowData}
import com.github.mauricio.async.db.mysql.exceptions.MySQLException
+import net.psforever.objects.DefaultCancellable
import net.psforever.types.PlanetSideEmpire
import scala.concurrent.{Await, Future}
@@ -25,7 +26,7 @@ class LoginSessionActor extends Actor with MDCContextAware {
var leftRef : ActorRef = ActorRef.noSender
var rightRef : ActorRef = ActorRef.noSender
- var updateServerListTask : Cancellable = LoginSessionActor.DefaultCancellable
+ var updateServerListTask : Cancellable = DefaultCancellable.obj
override def postStop() = {
if(updateServerListTask != null)
@@ -54,59 +55,22 @@ class LoginSessionActor extends Actor with MDCContextAware {
def Started : Receive = {
case UpdateServerList() =>
updateServerList()
- case ctrl @ ControlPacket(_, _) =>
- 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 =>
+ case ControlPacket(_, ctrl) =>
handleControlPkt(ctrl)
- case game : PlanetSideGamePacket =>
+ case GamePacket(_, _, game) =>
handleGamePkt(game)
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) = {
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
/// 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")
-
val serverTick = Math.abs(System.nanoTime().toInt) // limit the size to prevent encoding error
- sendResponse(PacketCoding.CreateControlPacket(ControlSyncResp(diff, serverTick,
- fa, fb, fb, fa)))
- case MultiPacket(packets) =>
+ sendResponse(PacketCoding.CreateControlPacket(ControlSyncResp(diff, serverTick, fa, fb, fb, fa)))
- /// 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 =>
log.error(s"Unhandled ControlPacket $default")
}
@@ -119,7 +83,6 @@ class LoginSessionActor extends Actor with MDCContextAware {
// TESTING CODE FOR ACCOUNT LOOKUP
def accountLookup(username : String, password : String) : Boolean = {
val connection: Connection = DatabaseConnector.getAccountsConnection
-
Await.result(connection.connect, 5 seconds)
// create account
@@ -135,13 +98,13 @@ class LoginSessionActor extends Actor with MDCContextAware {
case Some(resultSet) =>
val row : RowData = resultSet.head
row(0)
- case None => -1
- }
- )
+ case None =>
+ -1
+ })
try {
// XXX: remove awaits
- val result = Await.result( mapResult, 5 seconds )
+ Await.result( mapResult, 5 seconds )
return true
} catch {
case e : MySQLException =>
@@ -151,7 +114,6 @@ class LoginSessionActor extends Actor with MDCContextAware {
} finally {
connection.disconnect
}
-
false
}
@@ -190,22 +152,25 @@ class LoginSessionActor extends Actor with MDCContextAware {
log.info(s"Failed login to account $username")
sendResponse(PacketCoding.CreateGamePacket(0, response))
}
+
case ConnectToWorldRequestMessage(name, _, _, _, _, _, _) =>
log.info(s"Connect to world request for '$name'")
-
val response = ConnectToWorldMessage(serverName, serverAddress.getHostString, serverAddress.getPort)
sendResponse(PacketCoding.CreateGamePacket(0, response))
sendResponse(DropSession(sessionId, "user transferring to world"))
- case default => log.debug(s"Unhandled GamePacket $pkt")
+
+ case _ =>
+ log.debug(s"Unhandled GamePacket $pkt")
}
def updateServerList() = {
val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ",
Vector(
- WorldInformation(serverName, WorldStatus.Up, ServerType.Beta,
- Vector(WorldConnectionInfo(serverAddress)), PlanetSideEmpire.VS)
- ))
-
+ WorldInformation(
+ serverName, WorldStatus.Up, ServerType.Beta, Vector(WorldConnectionInfo(serverAddress)), PlanetSideEmpire.VS
+ )
+ )
+ )
sendResponse(PacketCoding.CreateGamePacket(0, msg))
}
@@ -216,22 +181,13 @@ class LoginSessionActor extends Actor with MDCContextAware {
def sendResponse(cont : Any) = {
log.trace("LOGIN SEND: " + cont)
-
MDC("sessionId") = sessionId.toString
rightRef !> cont
}
def sendRawResponse(pkt : ByteVector) = {
log.trace("LOGIN SEND RAW: " + pkt)
-
MDC("sessionId") = sessionId.toString
rightRef !> RawPacket(pkt)
}
}
-
-object LoginSessionActor {
- final val DefaultCancellable = new Cancellable() {
- def isCancelled : Boolean = true
- def cancel : Boolean = true
- }
-}
diff --git a/pslogin/src/main/scala/PacketCodingActor.scala b/pslogin/src/main/scala/PacketCodingActor.scala
index 9d597ba67..59a8ab284 100644
--- a/pslogin/src/main/scala/PacketCodingActor.scala
+++ b/pslogin/src/main/scala/PacketCodingActor.scala
@@ -5,7 +5,7 @@ import scodec.Attempt.{Failure, Successful}
import scodec.bits._
import org.log4s.MDC
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:
@@ -30,13 +30,14 @@ import net.psforever.packet.control.{HandleGamePacket, SlottedMetaPacket}
*/
class PacketCodingActor extends Actor with MDCContextAware {
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 rightRef : ActorRef = ActorRef.noSender
private[this] val log = org.log4s.getLogger
override def postStop() = {
- subslot = 0 //in case this `Actor` restarts
+ subslotOutbound = 0 //in case this `Actor` restarts
super.postStop()
}
@@ -68,12 +69,13 @@ class PacketCodingActor extends Actor with MDCContextAware {
mtuLimit(msg)
}
else {//from network, to LSA, WSA, etc. - decode
- PacketCoding.unmarshalPayload(0, msg) match { //TODO is it safe for this to always be 0?
- case Successful(packet) =>
- sendResponseRight(packet)
- case Failure(ex) =>
- log.info(s"Failed to marshal a packet: $ex")
- }
+ UnmarshalInnerPacket(msg, "a packet")
+// PacketCoding.unmarshalPayload(0, msg) match { //TODO is it safe for this to always be 0?
+// case Successful(packet) =>
+// handlePacketContainer(packet) //sendResponseRight
+// case Failure(ex) =>
+// log.info(s"Failed to marshal a packet: $ex")
+// }
}
//known elevated packet type
case ctrl @ ControlPacket(_, packet) =>
@@ -88,7 +90,7 @@ class PacketCodingActor extends Actor with MDCContextAware {
else { //deprecated; ControlPackets should not be coming from this direction
log.warn(s"DEPRECATED CONTROL PACKET SEND: $ctrl")
MDC("sessionId") = sessionId.toString
- sendResponseRight(ctrl)
+ handlePacketContainer(ctrl) //sendResponseRight
}
//known elevated packet type
case game @ GamePacket(_, _, packet) =>
@@ -123,15 +125,15 @@ class PacketCodingActor extends Actor with MDCContextAware {
/**
* Retrieve the current subslot number.
* 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 = {
- if(subslot == 65536) { //TODO what is the actual wrap number?
- subslot = 0
- subslot
+ if(subslotOutbound == 65536) { //TODO what is the actual wrap number?
+ subslotOutbound = 0
+ subslotOutbound
} else {
- val curr = subslot
- subslot += 1
+ val curr = subslotOutbound
+ subslotOutbound += 1
curr
}
}
@@ -177,7 +179,7 @@ class PacketCodingActor extends Actor with MDCContextAware {
PacketCoding.EncodePacket(pkt.packet) match {
case Successful(bdata) =>
sendResponseLeft(bdata.toByteVector)
- case f @ Failure(_) =>
+ case f : Failure =>
log.error(s"$f")
}
})
@@ -193,6 +195,72 @@ class PacketCodingActor extends Actor with MDCContextAware {
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.
+ *
+ * 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.
* @param cont the packet
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index a7c9d8f3d..b19360b8f 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -137,10 +137,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
galaxy = endpoint
log.info("ID: " + sessionId + " Got galaxy service " + endpoint)
- case ctrl @ ControlPacket(_, _) =>
- handlePktContainer(ctrl)
- case game @ GamePacket(_, _, _) =>
- handlePktContainer(game)
+ case ControlPacket(_, ctrl) =>
+ handleControlPkt(ctrl)
+ case GamePacket(_, _, pkt) =>
+ handleGamePkt(pkt)
// temporary hack to keep the client from disconnecting
case PokeClient() =>
sendResponse(KeepAliveMessage())
@@ -1091,61 +1091,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
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) = {
pkt match {
- case SlottedMetaPacket(slot, subslot, innerPacket) =>
- 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) =>
+ case sync @ ControlSync(diff, _, _, _, _, _, fa, fb) =>
log.debug(s"SYNC: $sync")
val serverTick = Math.abs(System.nanoTime().toInt) // limit the size to prevent encoding error
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(_) =>
log.info("Good bye")