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. 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) ![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 UnknownMessage21 => noDecoder(opcode)
case BindPlayerMessage => noDecoder(opcode) case BindPlayerMessage => noDecoder(opcode)
case ObjectCreateMessage_Duplicate => noDecoder(opcode) case ObjectCreateMessage_Duplicate => noDecoder(opcode)
case ObjectCreateMessage => noDecoder(opcode) case ObjectCreateMessage => game.ObjectCreateMessage.decode
case ObjectDeleteMessage => noDecoder(opcode) case ObjectDeleteMessage => noDecoder(opcode)
case PingMsg => noDecoder(opcode) case PingMsg => noDecoder(opcode)
case VehicleStateMessage => noDecoder(opcode) case VehicleStateMessage => noDecoder(opcode)
@ -379,7 +379,7 @@ object GamePacketOpcode extends Enumeration {
case LoadMapMessage => noDecoder(opcode) case LoadMapMessage => noDecoder(opcode)
// OPCODE 50 // OPCODE 50
case SetCurrentAvatarMessage => noDecoder(opcode) case SetCurrentAvatarMessage => game.SetCurrentAvatarMessage.decode
case ObjectHeldMessage => noDecoder(opcode) case ObjectHeldMessage => noDecoder(opcode)
case WeaponFireMessage => noDecoder(opcode) case WeaponFireMessage => noDecoder(opcode)
case AvatarJumpMessage => noDecoder(opcode) case AvatarJumpMessage => noDecoder(opcode)

View file

@ -39,7 +39,7 @@ final case class CharacterInfoMessage(zoneId : PlanetSideZoneID,
object CharacterInfoMessage extends Marshallable[CharacterInfoMessage] { object CharacterInfoMessage extends Marshallable[CharacterInfoMessage] {
implicit val codec : Codec[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) :: ("zoneId" | PlanetSideZoneID.codec) ::
("charId" | uint32L) :: ("charId" | uint32L) ::
("charGUID" | PlanetSideGUID.codec) :: ("charGUID" | PlanetSideGUID.codec) ::

View file

@ -4,9 +4,6 @@ import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, Plan
import scodec.Codec import scodec.Codec
import scodec.codecs._ import scodec.codecs._
/**
* Created by Root on 5/19/2016.
*/
final case class KeepAliveMessage(code : Int) extends PlanetSideGamePacket { final case class KeepAliveMessage(code : Int) extends PlanetSideGamePacket {
type Packet = KeepAliveMessage type Packet = KeepAliveMessage
def opcode = GamePacketOpcode.KeepAliveMessage def opcode = GamePacketOpcode.KeepAliveMessage

View file

@ -11,7 +11,8 @@ case class ObjectCreateMessageParent(guid : Int, slot : Int)
case class ObjectCreateMessage(streamLength : Long, case class ObjectCreateMessage(streamLength : Long,
objectClass : Int, objectClass : Int,
guid : Int, guid : Int,
parentInfo : Option[ObjectCreateMessageParent]) parentInfo : Option[ObjectCreateMessageParent]
)
extends PlanetSideGamePacket { extends PlanetSideGamePacket {
def opcode = GamePacketOpcode.ObjectCreateMessage 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 (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 = { def NewClient : Receive = {
case RawPacket(msg) => case RawPacket(msg) =>
PacketCoding.UnmarshalPacket(msg) match { 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) => case Successful(p) =>
//println("RECV: " + p) //println("RECV: " + p)

View file

@ -90,7 +90,7 @@ class LoginSessionActor extends Actor with MDCContextAware {
} }
val serverName = "PSForever" 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 { def handleGamePkt(pkt : PlanetSideGamePacket) = pkt match {
case LoginMessage(majorVersion, minorVersion, buildDate, username, case LoginMessage(majorVersion, minorVersion, buildDate, username,
@ -112,7 +112,7 @@ class LoginSessionActor extends Actor with MDCContextAware {
case ConnectToWorldRequestMessage(name, _, _, _, _, _, _) => case ConnectToWorldRequestMessage(name, _, _, _, _, _, _) =>
log.info(s"Connect to world request for '${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)) sendResponse(PacketCoding.CreateGamePacket(0, response))
case default => log.debug(s"Unhandled GamePacket ${pkt}") case default => log.debug(s"Unhandled GamePacket ${pkt}")
} }

View file

@ -11,7 +11,7 @@ import scodec.bits._
class WorldSessionActor extends Actor with MDCContextAware { class WorldSessionActor extends Actor with MDCContextAware {
private[this] val log = org.log4s.getLogger private[this] val log = org.log4s.getLogger
private case class UpdateServerList() private case class PokeClient()
var leftRef : ActorRef = ActorRef.noSender var leftRef : ActorRef = ActorRef.noSender
var rightRef : ActorRef = ActorRef.noSender var rightRef : ActorRef = ActorRef.noSender
@ -34,6 +34,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
handlePktContainer(ctrl) handlePktContainer(ctrl)
case game @ GamePacket(_, _, _) => case game @ GamePacket(_, _, _) =>
handlePktContainer(game) 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") 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 { def handleGamePkt(pkt : PlanetSideGamePacket) = pkt match {
case ConnectToWorldRequestMessage(server, token, majorVersion, minorVersion, revision, buildDate, unk) => 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}") 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, 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) => case msg @ CharacterRequestMessage(charId, action) =>
log.info("Handling " + msg) log.info("Handling " + msg)
@ -108,6 +120,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
case CharacterRequestAction.Delete => case CharacterRequestAction.Delete =>
sendResponse(PacketCoding.CreateGamePacket(0, ActionResultMessage(false, Some(1)))) sendResponse(PacketCoding.CreateGamePacket(0, ActionResultMessage(false, Some(1))))
case CharacterRequestAction.Select => 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 => case default =>
log.error("Unsupported " + default + " in " + msg) log.error("Unsupported " + default + " in " + msg)
} }