Working login to TR Sancuary!

Packet: SetCurrentAvatarMessage

Lots of hacks to get here, but wow, what a milestone.
This commit is contained in:
Chord 2016-07-15 01:45:10 -04:00
parent e0db8ec2b7
commit 5fa0610208
10 changed files with 76 additions and 13 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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) ::

View file

@ -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

View file

@ -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

View file

@ -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]
}

View file

@ -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
}
}
}
}

View file

@ -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)

View file

@ -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}")
}

View file

@ -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)
}