From 5fa06102084e7961b086c77e74f04d535151bece Mon Sep 17 00:00:00 2001 From: Chord Date: Fri, 15 Jul 2016 01:45:10 -0400 Subject: [PATCH] Working login to TR Sancuary! Packet: SetCurrentAvatarMessage Lots of hacks to get here, but wow, what a milestone. --- README.md | 2 +- .../psforever/packet/GamePacketOpcode.scala | 4 +-- .../packet/game/CharacterInfoMessage.scala | 2 +- .../packet/game/KeepAliveMessage.scala | 3 -- .../packet/game/ObjectCreateMessage.scala | 3 +- .../packet/game/SetCurrentAvatarMessage.scala | 20 +++++++++++++ common/src/test/scala/GamePacketTest.scala | 21 ++++++++++++++ .../src/main/scala/CryptoSessionActor.scala | 2 +- .../src/main/scala/LoginSessionActor.scala | 4 +-- .../src/main/scala/WorldSessionActor.scala | 28 +++++++++++++++++-- 10 files changed, 76 insertions(+), 13 deletions(-) create mode 100644 common/src/main/scala/net/psforever/packet/game/SetCurrentAvatarMessage.scala diff --git a/README.md b/README.md index 5592531a..de878b21 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PlanetSide Login Server +# PlanetSide Login Server [![Build Status](https://travis-ci.org/psforever/PSF-LoginServer.svg?branch=master)](https://travis-ci.org/psforever/PSF-LoginServer) This project contains the code to run and manage the login server role for PlanetSide 1. ![PSForever Login Server banner](https://i.imgur.com/EkbIv5x.png) diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index 3c45fffc..7f531b52 100644 --- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -347,7 +347,7 @@ object GamePacketOpcode extends Enumeration { case UnknownMessage21 => noDecoder(opcode) case BindPlayerMessage => noDecoder(opcode) case ObjectCreateMessage_Duplicate => noDecoder(opcode) - case ObjectCreateMessage => noDecoder(opcode) + case ObjectCreateMessage => game.ObjectCreateMessage.decode case ObjectDeleteMessage => noDecoder(opcode) case PingMsg => noDecoder(opcode) case VehicleStateMessage => noDecoder(opcode) @@ -379,7 +379,7 @@ object GamePacketOpcode extends Enumeration { case LoadMapMessage => noDecoder(opcode) // OPCODE 50 - case SetCurrentAvatarMessage => noDecoder(opcode) + case SetCurrentAvatarMessage => game.SetCurrentAvatarMessage.decode case ObjectHeldMessage => noDecoder(opcode) case WeaponFireMessage => noDecoder(opcode) case AvatarJumpMessage => noDecoder(opcode) diff --git a/common/src/main/scala/net/psforever/packet/game/CharacterInfoMessage.scala b/common/src/main/scala/net/psforever/packet/game/CharacterInfoMessage.scala index 82ea0f4b..c22ec666 100644 --- a/common/src/main/scala/net/psforever/packet/game/CharacterInfoMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/CharacterInfoMessage.scala @@ -39,7 +39,7 @@ final case class CharacterInfoMessage(zoneId : PlanetSideZoneID, object CharacterInfoMessage extends Marshallable[CharacterInfoMessage] { implicit val codec : Codec[CharacterInfoMessage] = ( - ("unknown" | uint32L).unit(0) :: + ("unknown" | uint32L).unit(0) :: // this type is set to unit as we dont know what it is yet ("zoneId" | PlanetSideZoneID.codec) :: ("charId" | uint32L) :: ("charGUID" | PlanetSideGUID.codec) :: diff --git a/common/src/main/scala/net/psforever/packet/game/KeepAliveMessage.scala b/common/src/main/scala/net/psforever/packet/game/KeepAliveMessage.scala index b279ad36..83d2a535 100644 --- a/common/src/main/scala/net/psforever/packet/game/KeepAliveMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/KeepAliveMessage.scala @@ -4,9 +4,6 @@ import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, Plan import scodec.Codec import scodec.codecs._ -/** - * Created by Root on 5/19/2016. - */ final case class KeepAliveMessage(code : Int) extends PlanetSideGamePacket { type Packet = KeepAliveMessage def opcode = GamePacketOpcode.KeepAliveMessage diff --git a/common/src/main/scala/net/psforever/packet/game/ObjectCreateMessage.scala b/common/src/main/scala/net/psforever/packet/game/ObjectCreateMessage.scala index 46896f29..4d3861e3 100644 --- a/common/src/main/scala/net/psforever/packet/game/ObjectCreateMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/ObjectCreateMessage.scala @@ -11,7 +11,8 @@ case class ObjectCreateMessageParent(guid : Int, slot : Int) case class ObjectCreateMessage(streamLength : Long, objectClass : Int, guid : Int, - parentInfo : Option[ObjectCreateMessageParent]) + parentInfo : Option[ObjectCreateMessageParent] + ) extends PlanetSideGamePacket { def opcode = GamePacketOpcode.ObjectCreateMessage diff --git a/common/src/main/scala/net/psforever/packet/game/SetCurrentAvatarMessage.scala b/common/src/main/scala/net/psforever/packet/game/SetCurrentAvatarMessage.scala new file mode 100644 index 00000000..7f229f52 --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/SetCurrentAvatarMessage.scala @@ -0,0 +1,20 @@ +// Copyright (c) 2016 PSForever.net to present +package net.psforever.packet.game + +import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket} +import scodec.Codec +import scodec.codecs._ + +final case class SetCurrentAvatarMessage(guid: PlanetSideGUID, unk1 : Int, unk2 : Int) extends PlanetSideGamePacket { + type Packet = SetCurrentAvatarMessage + def opcode = GamePacketOpcode.SetCurrentAvatarMessage + def encode = SetCurrentAvatarMessage.encode(this) +} + +object SetCurrentAvatarMessage extends Marshallable[SetCurrentAvatarMessage] { + implicit val codec: Codec[SetCurrentAvatarMessage] = ( + ("guid" | PlanetSideGUID.codec) :: + ("unk1" | uint(3)) :: + ("unk2" | uint(3)) + ).as[SetCurrentAvatarMessage] +} \ No newline at end of file diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index 0ec125d2..e897b895 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -142,5 +142,26 @@ class GamePacketTest extends Specification { (hex"1f".bits ++ bin"0" ++ hex"01000000".bits).toByteVector } } + + "ObjectCreateMessage" should { + val packet = hex"18 CF 13 00 00 BC 87 00 0A F0 16 C3 43 A1 30 90 00 02 C0 40 00 08 70 43 00 68 00 6F 00 72 00 64 00 54 00 52 00 82 65 1F F5 9E 80 80 00 00 00 00 00 3F FF C0 00 00 00 20 00 00 00 20 27 03 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FC CC 10 00 03 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 90 01 90 00 00 00 00 01 00 7E C8 00 C8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 C0 00 42 C5 46 86 C7 00 00 02 A0 00 00 12 60 78 70 65 5F 77 61 72 70 5F 67 61 74 65 5F 75 73 61 67 65 92 78 70 65 5F 69 6E 73 74 61 6E 74 5F 61 63 74 69 6F 6E 92 78 70 65 5F 73 61 6E 63 74 75 61 72 79 5F 68 65 6C 70 91 78 70 65 5F 62 61 74 74 6C 65 5F 72 61 6E 6B 5F 32 8E 78 70 65 5F 66 6F 72 6D 5F 73 71 75 61 64 8E 78 70 65 5F 74 68 5F 6E 6F 6E 73 61 6E 63 8B 78 70 65 5F 74 68 5F 61 6D 6D 6F 90 78 70 65 5F 74 68 5F 66 69 72 65 6D 6F 64 65 73 8F 75 73 65 64 5F 63 68 61 69 6E 62 6C 61 64 65 9A 76 69 73 69 74 65 64 5F 62 72 6F 61 64 63 61 73 74 5F 77 61 72 70 67 61 74 65 8E 76 69 73 69 74 65 64 5F 6C 6F 63 6B 65 72 8D 75 73 65 64 5F 70 75 6E 69 73 68 65 72 88 75 73 65 64 5F 72 65 6B 8D 75 73 65 64 5F 72 65 70 65 61 74 65 72 9F 76 69 73 69 74 65 64 5F 64 65 63 6F 6E 73 74 72 75 63 74 69 6F 6E 5F 74 65 72 6D 69 6E 61 6C 8F 75 73 65 64 5F 73 75 70 70 72 65 73 73 6F 72 96 76 69 73 69 74 65 64 5F 6F 72 64 65 72 5F 74 65 72 6D 69 6E 61 6C 85 6D 61 70 31 35 85 6D 61 70 31 34 85 6D 61 70 31 32 85 6D 61 70 30 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 0A 36 13 88 04 00 40 00 00 10 00 04 00 00 4D 6E 40 10 41 00 00 00 40 00 18 08 38 1C C0 20 32 00 00 07 80 15 E1 D0 02 10 20 00 00 08 00 03 01 07 13 A8 04 06 40 00 00 10 03 20 BB 00 42 E4 00 00 01 00 0E 07 70 08 6C 80 00 06 40 01 C0 F0 01 13 90 00 00 C8 00 38 1E 40 23 32 00 00 19 00 07 03 D0 05 0E 40 00 03 20 00 E8 7B 00 A4 C8 00 00 64 00 DA 4F 80 14 E1 00 00 00 40 00 18 08 38 1F 40 20 32 00 00 0A 00 08 " + val packet2 = hex"18 17 74 00 00 BC 8C 10 90 3B 45 C6 FA 94 00 9F F0 00 00 40 00 08 C0 44 00 69 00 66 00 66 00 45" + + "decode" in { + PacketCoding.DecodePacket(packet2).require match { + case obj @ ObjectCreateMessage(len, cls, guid, parent) => + len === 29719 + cls === 121 + guid === 2497 + parent === None + case default => + ko + } + } + + "encode" in { + ok + } + } } } diff --git a/pslogin/src/main/scala/CryptoSessionActor.scala b/pslogin/src/main/scala/CryptoSessionActor.scala index 21bef0d9..07f5201f 100644 --- a/pslogin/src/main/scala/CryptoSessionActor.scala +++ b/pslogin/src/main/scala/CryptoSessionActor.scala @@ -64,7 +64,7 @@ class CryptoSessionActor extends Actor with MDCContextAware { def NewClient : Receive = { case RawPacket(msg) => PacketCoding.UnmarshalPacket(msg) match { - case Failure(e) => log.error("Could not decode packet: " + e) + case Failure(e) => log.error("Could not decode packet: " + e + s", msg ${msg.toString}") case Successful(p) => //println("RECV: " + p) diff --git a/pslogin/src/main/scala/LoginSessionActor.scala b/pslogin/src/main/scala/LoginSessionActor.scala index 54ad1f4a..c8e96564 100644 --- a/pslogin/src/main/scala/LoginSessionActor.scala +++ b/pslogin/src/main/scala/LoginSessionActor.scala @@ -90,7 +90,7 @@ class LoginSessionActor extends Actor with MDCContextAware { } val serverName = "PSForever" - val serverAddress = new InetSocketAddress(InetAddress.getLocalHost, 51001) + val serverAddress = new InetSocketAddress("192.168.0.2", 51001) def handleGamePkt(pkt : PlanetSideGamePacket) = pkt match { case LoginMessage(majorVersion, minorVersion, buildDate, username, @@ -112,7 +112,7 @@ class LoginSessionActor extends Actor with MDCContextAware { case ConnectToWorldRequestMessage(name, _, _, _, _, _, _) => log.info(s"Connect to world request for '${name}'") - val response = ConnectToWorldMessage(serverName, serverAddress.getHostName, serverAddress.getPort) + val response = ConnectToWorldMessage(serverName, serverAddress.getHostString, serverAddress.getPort) sendResponse(PacketCoding.CreateGamePacket(0, response)) case default => log.debug(s"Unhandled GamePacket ${pkt}") } diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 9ad05b0b..dd31c9e8 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -11,7 +11,7 @@ import scodec.bits._ class WorldSessionActor extends Actor with MDCContextAware { private[this] val log = org.log4s.getLogger - private case class UpdateServerList() + private case class PokeClient() var leftRef : ActorRef = ActorRef.noSender var rightRef : ActorRef = ActorRef.noSender @@ -34,6 +34,9 @@ class WorldSessionActor extends Actor with MDCContextAware { handlePktContainer(ctrl) case game @ GamePacket(_, _, _) => handlePktContainer(game) + // temporary hack to keep the client from disconnecting + case PokeClient() => + sendResponse(PacketCoding.CreateGamePacket(0, KeepAliveMessage(0))) case default => failWithError(s"Invalid packet class received: $default") } @@ -92,6 +95,9 @@ class WorldSessionActor extends Actor with MDCContextAware { } } + // XXX: hard coded ObjectCreateMessage + val objectHex = hex"18 57 0C 00 00 BC 84 B0 06 C2 D7 65 53 5C A1 60 00 01 34 40 00 09 70 49 00 6C 00 6C 00 6C 00 49 00 49 00 49 00 6C 00 6C 00 6C 00 49 00 6C 00 49 00 6C 00 6C 00 49 00 6C 00 6C 00 6C 00 49 00 6C 00 6C 00 49 00 84 52 70 76 1E 80 80 00 00 00 00 00 3F FF C0 00 00 00 20 00 00 0F F6 A7 03 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FD 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 90 01 90 00 64 00 00 01 00 7E C8 00 C8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 C0 00 42 C5 46 86 C7 00 00 00 80 00 00 12 40 78 70 65 5F 73 61 6E 63 74 75 61 72 79 5F 68 65 6C 70 90 78 70 65 5F 74 68 5F 66 69 72 65 6D 6F 64 65 73 8B 75 73 65 64 5F 62 65 61 6D 65 72 85 6D 61 70 31 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 0A 23 02 60 04 04 40 00 00 10 00 06 02 08 14 D0 08 0C 80 00 02 00 02 6B 4E 00 82 88 00 00 02 00 00 C0 41 C0 9E 01 01 90 00 00 64 00 44 2A 00 10 91 00 00 00 40 00 18 08 38 94 40 20 32 00 00 00 80 19 05 48 02 17 20 00 00 08 00 70 29 80 43 64 00 00 32 00 0E 05 40 08 9C 80 00 06 40 01 C0 AA 01 19 90 00 00 C8 00 3A 15 80 28 72 00 00 19 00 04 0A B8 05 26 40 00 03 20 06 C2 58 00 A7 88 00 00 02 00 00 80 00 00 " + def handleGamePkt(pkt : PlanetSideGamePacket) = pkt match { case ConnectToWorldRequestMessage(server, token, majorVersion, minorVersion, revision, buildDate, unk) => @@ -99,8 +105,14 @@ class WorldSessionActor extends Actor with MDCContextAware { log.info(s"New world login to ${server} with Token:${token}. ${clientVersion}") + // ObjectCreateMessage + sendRawResponse(objectHex) + // XXX: hard coded message + sendRawResponse(hex"14 0F 00 00 00 10 27 00 00 C1 D8 7A 02 4B 00 26 5C B0 80 00 ") + + // NOTE: PlanetSideZoneID just chooses the background sendResponse(PacketCoding.CreateGamePacket(0, - CharacterInfoMessage(PlanetSideZoneID(0), 0, PlanetSideGUID(0), true, 0))) + CharacterInfoMessage(PlanetSideZoneID(1), 0, PlanetSideGUID(0), true, 0))) case msg @ CharacterRequestMessage(charId, action) => log.info("Handling " + msg) @@ -108,6 +120,18 @@ class WorldSessionActor extends Actor with MDCContextAware { case CharacterRequestAction.Delete => sendResponse(PacketCoding.CreateGamePacket(0, ActionResultMessage(false, Some(1)))) case CharacterRequestAction.Select => + PacketCoding.DecodeGamePacket(objectHex).require match { + case ObjectCreateMessage(len, cls, guid, _) => + // LoadMapMessage 13714 in mossy .gcap + // XXX: hardcoded shit + sendRawResponse(hex"31 85 6D 61 70 31 32 85 68 6F 6D 65 32 6C 9D 19 00 00 00 5F 40 B2 1C 80 ") + sendRawResponse(objectHex) + sendResponse(PacketCoding.CreateGamePacket(0, SetCurrentAvatarMessage(PlanetSideGUID(guid),0,0))) + + import scala.concurrent.duration._ + import scala.concurrent.ExecutionContext.Implicits.global + context.system.scheduler.schedule(0 seconds, 1000 milliseconds, self, PokeClient()) + } case default => log.error("Unsupported " + default + " in " + msg) }