From cdf240cf6611dbf0e70b96f3c0a267d018baaaaf Mon Sep 17 00:00:00 2001 From: Chord Date: Wed, 4 May 2016 23:03:30 -0400 Subject: [PATCH] Handle meta packet and return ack. server scroller The scroller isn't usable in production as the client will deselect the server listing each time the list is updated. This prevents you from selecting a server. --- .../packet/control/SlottedMetaAck.scala | 35 +++++ .../src/main/scala/CryptoSessionActor.scala | 3 +- .../src/main/scala/LoginSessionActor.scala | 122 ++++++++++++++---- 3 files changed, 131 insertions(+), 29 deletions(-) create mode 100644 common/src/main/scala/net/psforever/packet/control/SlottedMetaAck.scala 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 000000000..2d7f76ef4 --- /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 da390dc4f..5f76c4f7c 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 adffa7155..a7bbdc3c7 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()))