diff --git a/common/src/main/scala/net/psforever/packet/control/SlottedMetaAck.scala b/common/src/main/scala/net/psforever/packet/control/SlottedMetaAck.scala new file mode 100644 index 00000000..2d7f76ef --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/control/SlottedMetaAck.scala @@ -0,0 +1,35 @@ +// Copyright (c) 2016 PSForever.net to present +package net.psforever.packet.control + +import net.psforever.packet.{ControlPacketOpcode, Marshallable, PlanetSideControlPacket} +import scodec.Codec +import scodec.bits.{BitVector, ByteOrdering, ByteVector} +import scodec.codecs._ + +final case class SlottedMetaAck(slot : Int, subslot : Int) + extends PlanetSideControlPacket { + type Packet = SlottedMetaAck + + assert(slot >= 0 && slot <= 7, s"Slot number ($slot) is out of range") + + def opcode = { + val base = ControlPacketOpcode.RelatedB0.id + ControlPacketOpcode(base + slot % 4) + } + + // 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 = SlottedMetaAck.encode(this).map(vect => vect.drop(8)) +} + +object SlottedMetaAck extends Marshallable[SlottedMetaAck] { + implicit val codec : Codec[SlottedMetaAck] = ( + ("slot" | uint8L.xmap[Int](a => a - ControlPacketOpcode.RelatedB0.id, a=>a) ) :: + ("subslot" | uint16) + ).as[SlottedMetaAck] + + def decodeWithOpcode(slot : ControlPacketOpcode.Value)(bits : BitVector) = { + decode(ControlPacketOpcode.codec.encode(slot).require ++ bits) + } +} \ No newline at end of file diff --git a/pslogin/src/main/scala/CryptoSessionActor.scala b/pslogin/src/main/scala/CryptoSessionActor.scala index da390dc4..5f76c4f7 100644 --- a/pslogin/src/main/scala/CryptoSessionActor.scala +++ b/pslogin/src/main/scala/CryptoSessionActor.scala @@ -255,7 +255,8 @@ class CryptoSessionActor extends Actor with MDCContextAware { def handleEstablishedPacket(from : ActorRef, cont : PlanetSidePacketContainer) = { // we are processing a packet we decrypted if(from == self) { - rightRef ! cont + import MDCContextAware.Implicits._ + rightRef !> cont } else if(from == rightRef) { // processing a completed packet from the right. encrypt val packet = PacketCoding.encryptPacket(cryptoState.get, cont).require sendResponse(packet) diff --git a/pslogin/src/main/scala/LoginSessionActor.scala b/pslogin/src/main/scala/LoginSessionActor.scala index adffa715..a7bbdc3c 100644 --- a/pslogin/src/main/scala/LoginSessionActor.scala +++ b/pslogin/src/main/scala/LoginSessionActor.scala @@ -2,15 +2,19 @@ import java.net.{InetAddress, InetSocketAddress} import akka.actor.{Actor, ActorRef, MDCContextAware} -import net.psforever.packet._ +import net.psforever.packet.{PlanetSideGamePacket, _} import net.psforever.packet.control._ import net.psforever.packet.game._ import scodec.Attempt.{Failure, Successful} import scodec.bits._ +import scala.util.Random + class LoginSessionActor extends Actor with MDCContextAware { private[this] val log = org.log4s.getLogger + private case class UpdateServerList() + var leftRef : ActorRef = ActorRef.noSender var rightRef : ActorRef = ActorRef.noSender @@ -28,27 +32,108 @@ class LoginSessionActor extends Actor with MDCContextAware { } def Started : Receive = { + case UpdateServerList() => + updateServerList case ctrl @ ControlPacket(_, _) => - handlePkt(ctrl) + handlePktContainer(ctrl) case game @ GamePacket(_, _, _) => - handlePkt(game) + handlePktContainer(game) case default => failWithError(s"Invalid packet class received: $default") } - def handlePkt(pkt : PlanetSidePacketContainer) : Unit = pkt match { + 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 class received: $default") + case default => failWithError(s"Invalid packet container class received: $default") } def handleControlPkt(pkt : PlanetSideControlPacket) = { pkt match { - case meta @ SlottedMetaPacket(slot, subslot, innerPacket) => + case SlottedMetaPacket(slot, subslot, innerPacket) => + sendResponse(PacketCoding.CreateControlPacket(SlottedMetaAck(slot, subslot))) + PacketCoding.DecodePacket(innerPacket) match { - case Successful(p) => - log.trace("RECV[INNER]: " + p) + case Failure(e) => + log.error(s"Failed to decode inner packet of SlottedMetaPacket: $e") + case Successful(v) => + handlePkt(v) + } + 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") + } + } + + 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, 0, false) + + sendResponse(PacketCoding.CreateGamePacket(0, response)) + + import scala.concurrent.duration._ + import scala.concurrent.ExecutionContext.Implicits.global + context.system.scheduler.schedule(0 seconds, 250 milliseconds, self, UpdateServerList()) + + case default => log.debug(s"Unhandled GamePacket ${pkt}") + } + + val scrollerWindow = 20 + val scrollerText = "PSForever_The_next_generation_of_PlanetSide_Hey_what_a_neat_scroller!" + var scrollerOffset = 0 + + def updateServerList = { + val start = scrollerOffset % scrollerText.length + var end = (scrollerOffset+scrollerWindow) % scrollerText.length + + var finalName = "" + if(end < start) + finalName = scrollerText.substring(start, scrollerText.length) + ";" + scrollerText.substring(0, end) + else + finalName = scrollerText.substring(start, end) + + scrollerOffset += 1 + + //println(finalName) + val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ", + Vector( + WorldInformation(finalName, WorldStatus.Up, ServerType.Development, + Vector(WorldConnectionInfo(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 51000))), EmpireNeed.TR) + )) + + sendResponse(PacketCoding.CreateGamePacket(0, msg)) + } + + /* + val packet = LoginRespMessage("AAAABBBBCCCCDDDD", hex"00000000 18FABE0C 00000000 00000000", @@ -65,26 +150,7 @@ class LoginSessionActor extends Actor with MDCContextAware { )) sendResponse(PacketCoding.CreateGamePacket(0, msg)) - 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") - } - } - - def handleGamePkt(pkt : PlanetSideGamePacket) = { - log.debug(s"Unhandled GamePacket ${pkt}") - } - + */ def failWithError(error : String) = { log.error(error) //sendResponse(PacketCoding.CreateControlPacket(ConnectionClose()))