PSF-BotServer/pslogin/src/main/scala/LoginSessionActor.scala

165 lines
5.5 KiB
Scala

// Copyright (c) 2016 PSForever.net to present
import java.net.{InetAddress, InetSocketAddress}
import akka.actor.{Actor, ActorRef, Cancellable, MDCContextAware}
import net.psforever.packet.{PlanetSideGamePacket, _}
import net.psforever.packet.control._
import net.psforever.packet.game._
import org.log4s.MDC
import scodec.Attempt.{Failure, Successful}
import scodec.bits._
import MDCContextAware.Implicits._
import net.psforever.types.PlanetSideEmpire
import scala.concurrent.duration._
import scala.util.Random
class LoginSessionActor extends Actor with MDCContextAware {
private[this] val log = org.log4s.getLogger
import scala.concurrent.ExecutionContext.Implicits.global
private case class UpdateServerList()
var sessionId : Long = 0
var leftRef : ActorRef = ActorRef.noSender
var rightRef : ActorRef = ActorRef.noSender
var updateServerListTask : Cancellable = null
override def postStop() = {
if(updateServerListTask != null)
updateServerListTask.cancel()
}
def receive = Initializing
def Initializing : Receive = {
case HelloFriend(sessionId, right) =>
this.sessionId = sessionId
leftRef = sender()
rightRef = right.asInstanceOf[ActorRef]
context.become(Started)
case _ =>
log.error("Unknown message")
context.stop(self)
}
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 =>
handleControlPkt(ctrl)
case game : PlanetSideGamePacket =>
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) =>
sendResponse(PacketCoding.CreateControlPacket(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.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) =>
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 default =>
log.debug(s"Unhandled ControlPacket $default")
}
}
// TODO: move to global configuration or database lookup
val serverName = "PSForever"
val serverAddress = new InetSocketAddress(LoginConfig.serverIpAddress.getHostAddress, 51001)
def handleGamePkt(pkt : PlanetSideGamePacket) = pkt match {
case LoginMessage(majorVersion, minorVersion, buildDate, username,
password, token, revision) =>
val clientVersion = s"Client Version: ${majorVersion}.${minorVersion}.${revision}, ${buildDate}"
if(token.isDefined)
log.info(s"New login UN:$username Token:${token.get}. ${clientVersion}")
else
log.info(s"New login UN:$username PW:$password. ${clientVersion}")
val newToken = token.getOrElse("THISISMYTOKENYES")
val response = LoginRespMessage(newToken, hex"00000000 18FABE0C 00000000 00000000",
0, 1, 2, 685276011, username, 10001)
sendResponse(PacketCoding.CreateGamePacket(0, response))
updateServerListTask = context.system.scheduler.schedule(0 seconds, 2 seconds, self, UpdateServerList())
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}")
}
def updateServerList() = {
val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ",
Vector(
WorldInformation(serverName, WorldStatus.Up, ServerType.Released,
Vector(WorldConnectionInfo(serverAddress)), PlanetSideEmpire.VS)
))
sendResponse(PacketCoding.CreateGamePacket(0, msg))
}
def failWithError(error : String) = {
log.error(error)
//sendResponse(PacketCoding.CreateControlPacket(ConnectionClose()))
}
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)
}
}