diff --git a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala index a62be626..9a6e9c74 100644 --- a/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala +++ b/common/src/main/scala/net/psforever/packet/GamePacketOpcode.scala @@ -349,7 +349,7 @@ object GamePacketOpcode extends Enumeration { case ObjectCreateMessage_Duplicate => noDecoder(opcode) case ObjectCreateMessage => game.ObjectCreateMessage.decode case ObjectDeleteMessage => game.ObjectDeleteMessage.decode - case PingMsg => noDecoder(opcode) + case PingMsg => game.PingMsg.decode case VehicleStateMessage => noDecoder(opcode) case FrameVehicleStateMessage => noDecoder(opcode) case GenericObjectStateMsg => game.GenericObjectStateMsg.decode diff --git a/common/src/main/scala/net/psforever/packet/game/PingMsg.scala b/common/src/main/scala/net/psforever/packet/game/PingMsg.scala new file mode 100644 index 00000000..74632a87 --- /dev/null +++ b/common/src/main/scala/net/psforever/packet/game/PingMsg.scala @@ -0,0 +1,24 @@ +// 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._ + +/** Sent periodically by the PlanetSide client when connected to the Login server. Not encrypted + * + * @param unk1 + * @param unk2 + */ +final case class PingMsg(unk1 : Long, unk2 : Long) extends PlanetSideGamePacket { + type Packet = PingMsg + def opcode = GamePacketOpcode.PingMsg + def encode = PingMsg.encode(this) +} + +object PingMsg extends Marshallable[PingMsg] { + implicit val codec : Codec[PingMsg] = ( + ("unk1" | uint32L) :: + ("unk2" | uint32L) + ).as[PingMsg] +} \ No newline at end of file diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index 5d4f30a5..8d760450 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -150,10 +150,12 @@ class GamePacketTest extends Specification { "decode" in { PacketCoding.DecodePacket(packet2).require match { - case obj @ ObjectCreateMessage(len, cls, guid, parent) => + case obj @ ObjectCreateMessage(len, cls, guid, parent, rest) => + val manualRest = packet2.bits.drop(32 + 1 + 0xb + 16) len === 29719 cls === 121 guid === 2497 + rest === manualRest parent === None case default => ko @@ -741,5 +743,24 @@ class GamePacketTest extends Specification { pkt_hitobj mustEqual string_hitobj } } + + "PingMsg" should { + val packet = hex"1a 00000000 b0360000" + + "decode" in { + PacketCoding.DecodePacket(packet).require match { + case PingMsg(unk1, unk2) => + unk1 === 0 + unk2 === 14000 + case default => + ko + } + } + + "encode" in { + val msg = PingMsg(0, 14000) + PacketCoding.EncodePacket(msg).require.toByteVector === packet + } + } } } diff --git a/pslogin/src/main/scala/CryptoSessionActor.scala b/pslogin/src/main/scala/CryptoSessionActor.scala index c42df2ed..9fe3b8b5 100644 --- a/pslogin/src/main/scala/CryptoSessionActor.scala +++ b/pslogin/src/main/scala/CryptoSessionActor.scala @@ -13,6 +13,7 @@ import java.security.SecureRandom import net.psforever.packet.control.{ClientStart, ServerStart} import net.psforever.packet.crypto._ +import net.psforever.packet.game.PingMsg /** * Actor that stores crypto state for a connection, appropriately encrypts and decrypts packets, @@ -64,7 +65,6 @@ 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 + s", msg ${msg.toString}") case Successful(p) => log.trace("Initializing -> NewClient") @@ -76,6 +76,21 @@ class CryptoSessionActor extends Actor with MDCContextAware { case default => log.error(s"Unexpected packet type ${p} in state NewClient") } + case Failure(e) => + // There is a special case where no crypto is being used. + // The only packet coming through looks like PingMsg + + PacketCoding.DecodePacket(msg) match { + case Successful(packet) => + packet match { + case ping @ PingMsg(unk1, unk2) => + // TODO: figure out how to get ping to show up on the planetside client + //sendResponse(PingMsg(unk2, unk1)) + case default => log.error(s"Unexpected non-crypto packet type ${packet} in state NewClient") + } + case Failure(e) => + log.error("Could not decode packet: " + e + s" in state NewClient") + } } case default => log.error(s"Invalid message '$default' received in state NewClient") } @@ -302,6 +317,21 @@ class CryptoSessionActor extends Actor with MDCContextAware { } } + def sendResponse(pkt : PlanetSideGamePacket) : ByteVector = { + log.trace("CRYPTO SEND GAME: " + pkt) + val pktEncoded = PacketCoding.EncodePacket(pkt) + + pktEncoded match { + case Failure(e) => + log.error(s"Failed to encode packet ${pkt.getClass.getName} when sending response") + ByteVector.empty + case Successful(v) => + val bytes = v.toByteVector + leftRef ! ResponsePacket(bytes) + bytes + } + } + def getRandBytes(amount : Int) : ByteVector = { val array = Array.ofDim[Byte](amount) random.nextBytes(array)