diff --git a/common/src/main/scala/psforever/net/VNLWorldStatusMessage.scala b/common/src/main/scala/psforever/net/VNLWorldStatusMessage.scala index 770b4aa29..10eee77ab 100644 --- a/common/src/main/scala/psforever/net/VNLWorldStatusMessage.scala +++ b/common/src/main/scala/psforever/net/VNLWorldStatusMessage.scala @@ -1,5 +1,8 @@ // Copyright (c) 2016 PSForever.net to present package psforever.net + +import java.net.{InetAddress, InetSocketAddress} + import scodec._ import scodec.bits._ import scodec.codecs._ @@ -24,8 +27,12 @@ object EmpireNeed extends Enumeration { implicit val codec = PacketHelpers.createEnumerationCodec(this, uint2L) } +final case class WorldConnectionInfo(address : InetSocketAddress) + final case class WorldInformation(name : String, status : WorldStatus.Value, - serverType : ServerType.Value, empireNeed : EmpireNeed.Value) + serverType : ServerType.Value, + connections : Vector[WorldConnectionInfo], + empireNeed : EmpireNeed.Value) final case class VNLWorldStatusMessage(welcomeMessage : String, worlds : Vector[WorldInformation]) extends PlanetSideGamePacket { @@ -74,6 +81,28 @@ object VNLWorldStatusMessage extends Marshallable[VNLWorldStatusMessage] { ("status1" | uint8L)).xmap(to, from) } + implicit val connectionCodec : Codec[WorldConnectionInfo] = { + + type DecodeStruct = ByteVector :: Int :: HNil + type EncodeStruct = InetSocketAddress :: HNil + + def decode(a : DecodeStruct) : EncodeStruct = a match { + case ipBytes :: port :: HNil => + val addr = new InetSocketAddress(InetAddress.getByAddress(ipBytes.reverse.toArray), port) + addr :: HNil + } + + def encode(a : EncodeStruct) : DecodeStruct = a match { + case addr :: HNil => + val ip = addr.getAddress.getAddress + val port = addr.getPort + + ByteVector(ip).reverse :: port :: HNil + } + + (bytes(4) :: uint16L).xmap(decode, encode).as[WorldConnectionInfo] + } + implicit val codec : Codec[VNLWorldStatusMessage] = ( ("welcome_message" | PacketHelpers.encodedWideString) :: ("worlds" | vectorOfN(uint8L, ( @@ -81,7 +110,8 @@ object VNLWorldStatusMessage extends Marshallable[VNLWorldStatusMessage] { // XXX: this needs to be byte aligned, but not sure how to do this ("world_name" | PacketHelpers.encodedString) :: ( ("status_and_type" | statusCodec) :+ - ("unknown" | constant(hex"01459e25403775")) :+ + // TODO: limit the size of this vector to 11 as the client will fail on any more + ("connections" | vectorOfN(uint8L, connectionCodec)) :+ ("empire_need" | EmpireNeed.codec) ) ).as[WorldInformation] diff --git a/common/src/test/scala/GamePacketTest.scala b/common/src/test/scala/GamePacketTest.scala index 49d94c45b..2cde4e693 100644 --- a/common/src/test/scala/GamePacketTest.scala +++ b/common/src/test/scala/GamePacketTest.scala @@ -1,4 +1,6 @@ // Copyright (c) 2016 PSForever.net to present +import java.net.{InetAddress, InetSocketAddress} + import org.specs2.mutable._ import psforever.net._ import scodec.bits._ @@ -17,9 +19,15 @@ class GamePacketTest extends Specification { case VNLWorldStatusMessage(message, worlds) => worlds.length mustEqual 1 message mustEqual "Welcome to PlanetSide! " - worlds{0}.name mustEqual "gemini" - worlds{0}.empireNeed mustEqual EmpireNeed.NC - worlds{0}.status mustEqual WorldStatus.Up + val world = worlds{0} + + world.name mustEqual "gemini" + world.empireNeed mustEqual EmpireNeed.NC + world.status mustEqual WorldStatus.Up + + world.connections.length mustEqual 1 + world.connections{0}.address.getPort mustEqual 30007 + world.connections{0}.address.getAddress.toString mustEqual "/64.37.158.69" case default => true mustEqual false } @@ -27,7 +35,14 @@ class GamePacketTest extends Specification { "encode" in { val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ", - Vector(WorldInformation("gemini", WorldStatus.Up, ServerType.Beta, EmpireNeed.NC))) + Vector( + WorldInformation("gemini", WorldStatus.Up, ServerType.Beta, + Vector( + WorldConnectionInfo(new InetSocketAddress(InetAddress.getByName("64.37.158.69"), 30007)) + ), EmpireNeed.NC + ) + ) + ) //0100 04 00 01459e2540377540 val pkt = PacketCoding.EncodePacket(msg).require.toByteVector @@ -38,8 +53,8 @@ class GamePacketTest extends Specification { "encode and decode multiple worlds" in { val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ", Vector( - WorldInformation("PSForever1", WorldStatus.Up, ServerType.Released, EmpireNeed.NC), - WorldInformation("PSForever2", WorldStatus.Down, ServerType.Beta, EmpireNeed.TR) + WorldInformation("PSForever1", WorldStatus.Up, ServerType.Released, Vector(), EmpireNeed.NC), + WorldInformation("PSForever2", WorldStatus.Down, ServerType.Beta, Vector(), EmpireNeed.TR) )) val pkt = PacketCoding.EncodePacket(msg).require.toByteVector diff --git a/pslogin/src/main/scala/LoginSessionActor.scala b/pslogin/src/main/scala/LoginSessionActor.scala index 63b3387dd..e05be0413 100644 --- a/pslogin/src/main/scala/LoginSessionActor.scala +++ b/pslogin/src/main/scala/LoginSessionActor.scala @@ -209,7 +209,7 @@ class LoginSessionActor(session : LoginSession) extends Actor with ActorLogging val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ", Vector( - WorldInformation("gemini", WorldStatus.Up, ServerType.Released, EmpireNeed.NC) + WorldInformation("gemini", WorldStatus.Up, ServerType.Released, Vector(), EmpireNeed.NC) )) sendResponse(PacketCoding.encryptPacket(cryptoState.get, PacketCoding.CreateGamePacket(4,