mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +00:00
Refine VNL multi-world with new vector codec
This commit is contained in:
parent
37ad423820
commit
a54ee2f0b7
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) 2019 PSForever
|
||||
package net.psforever.newcodecs
|
||||
|
||||
import scodec._
|
||||
import scodec.bits.BitVector
|
||||
|
||||
final class PrefixedVectorCodec[A](firstCodec: Codec[A], codec: Codec[A], limit: Option[Int] = None) extends Codec[Vector[A]] {
|
||||
|
||||
def sizeBound = limit match {
|
||||
case None => SizeBound.unknown
|
||||
case Some(lim) => codec.sizeBound * lim.toLong
|
||||
}
|
||||
|
||||
def encode(vector: Vector[A]) = Encoder.encodeSeq(firstCodec)(vector.slice(0,1)).map { bits =>
|
||||
if (vector.length > 1)
|
||||
bits ++ (Encoder.encodeSeq(codec)(vector.tail) getOrElse BitVector.empty)
|
||||
else
|
||||
bits
|
||||
}
|
||||
|
||||
def decode(buffer: BitVector) : scodec.Attempt[scodec.DecodeResult[Vector[A]]] = {
|
||||
Decoder.decodeCollect[Vector, A](firstCodec, Some(1))(buffer) match {
|
||||
case Attempt.Successful(firstValue) =>
|
||||
Decoder.decodeCollect[Vector, A](codec, limit map { _ - 1 })(firstValue.remainder) match {
|
||||
case Attempt.Successful(secondValue) =>
|
||||
Attempt.successful(DecodeResult(firstValue.value ++ secondValue.value, secondValue.remainder))
|
||||
case Attempt.Failure(e) => Attempt.failure(e)
|
||||
}
|
||||
case Attempt.Failure(e) => Attempt.failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
override def toString = s"vector($codec)"
|
||||
}
|
||||
|
|
@ -3,14 +3,22 @@ package net.psforever.newcodecs
|
|||
|
||||
import scodec.Attempt
|
||||
import scodec.Attempt.{Failure, Successful}
|
||||
import scodec.Codec
|
||||
import scodec._
|
||||
import scodec.bits.BitVector
|
||||
|
||||
package object newcodecs {
|
||||
|
||||
def q_double(min: Double, max: Double, bits: Int): Codec[Double] = new QuantizedDoubleCodec(min, max, bits)
|
||||
|
||||
def q_float(min : Double, max : Double, bits : Int): Codec[Float] = q_double(min, max, bits).narrow(v => Attempt.successful(v.toFloat), _.toDouble)
|
||||
|
||||
def binary_choice[A](choice: Boolean, codec_true: => Codec[A], codec_false: => Codec[A]): Codec[A] = new BinaryChoiceCodec(choice, codec_true, codec_false)
|
||||
|
||||
def prefixedVectorOfN[A](countCodec: Codec[Int], firstValueCodec: Codec[A], valueCodec: Codec[A]): Codec[Vector[A]] =
|
||||
countCodec.
|
||||
flatZip { count => new PrefixedVectorCodec(firstValueCodec, valueCodec, Some(count)) }.
|
||||
narrow[Vector[A]]({ case (cnt, xs) =>
|
||||
if (xs.size == cnt) Attempt.successful(xs)
|
||||
else Attempt.failure(Err(s"Insufficient number of elements: decoded ${xs.size} but should have decoded $cnt"))
|
||||
}, xs => (xs.size, xs)).
|
||||
withToString(s"vectorOfN($countCodec, $valueCodec)")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import java.net.{InetAddress, InetSocketAddress}
|
|||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
import net.psforever.newcodecs.newcodecs._
|
||||
import scodec._
|
||||
import scodec.bits._
|
||||
import scodec.codecs._
|
||||
|
|
@ -30,8 +31,7 @@ final case class WorldInformation(name : String, status : WorldStatus.Value,
|
|||
connections : Vector[WorldConnectionInfo],
|
||||
empireNeed : PlanetSideEmpire.Value)
|
||||
|
||||
final case class VNLWorldStatusMessage(welcomeMessage : String, world_count : Int, world : WorldInformation,
|
||||
other_worlds : Vector[WorldInformation] = Vector())
|
||||
final case class VNLWorldStatusMessage(welcomeMessage : String, worlds : Vector[WorldInformation])
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = VNLWorldStatusMessage
|
||||
def opcode = GamePacketOpcode.VNLWorldStatusMessage
|
||||
|
|
@ -119,9 +119,7 @@ object VNLWorldStatusMessage extends Marshallable[VNLWorldStatusMessage] {
|
|||
)).as[WorldInformation]
|
||||
|
||||
implicit val codec : Codec[VNLWorldStatusMessage] = (
|
||||
("welcome_message" | PacketHelpers.encodedWideString) ::
|
||||
(("num_worlds" | uint8L) flatPrepend { num_worlds =>
|
||||
("primary_world" | world_codec) ::
|
||||
("extra_worlds" | vectorOfN(provide(num_worlds-1), world_codec_aligned)
|
||||
)})).as[VNLWorldStatusMessage]
|
||||
("welcome_message" | PacketHelpers.encodedWideString) ::
|
||||
("worlds" | prefixedVectorOfN(uint8L, world_codec, world_codec_aligned))
|
||||
).as[VNLWorldStatusMessage]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,12 @@ class VNLWorldStatusMessageTest extends Specification {
|
|||
|
||||
"decode" in {
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case VNLWorldStatusMessage(message, _, world, extra_worlds) =>
|
||||
extra_worlds.length mustEqual 0
|
||||
case VNLWorldStatusMessage(message, worlds) =>
|
||||
message mustEqual "Welcome to PlanetSide! "
|
||||
|
||||
worlds.length mustEqual 1
|
||||
|
||||
val world = worlds(0)
|
||||
world.name mustEqual "gemini"
|
||||
world.empireNeed mustEqual PlanetSideEmpire.NC
|
||||
world.status mustEqual WorldStatus.Up
|
||||
|
|
@ -36,13 +39,14 @@ class VNLWorldStatusMessageTest extends Specification {
|
|||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ", 1,
|
||||
WorldInformation("gemini", WorldStatus.Up, ServerType.Released,
|
||||
val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ",
|
||||
Vector(WorldInformation("gemini", WorldStatus.Up, ServerType.Released,
|
||||
Vector(
|
||||
WorldConnectionInfo(new InetSocketAddress(InetAddress.getByName("64.37.158.69"), 30007))
|
||||
), PlanetSideEmpire.NC
|
||||
)
|
||||
))
|
||||
)
|
||||
|
||||
//0100 04 00 01459e2540377540
|
||||
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
|
@ -50,18 +54,56 @@ class VNLWorldStatusMessageTest extends Specification {
|
|||
pkt mustEqual string
|
||||
}
|
||||
|
||||
"encode and decode empty messages" in {
|
||||
val string = hex"0584410041004100410000"
|
||||
val empty_msg = VNLWorldStatusMessage("AAAA", Vector())
|
||||
val empty_pkt = PacketCoding.EncodePacket(empty_msg).require.toByteVector
|
||||
|
||||
empty_pkt mustEqual string
|
||||
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case VNLWorldStatusMessage(message, worlds) =>
|
||||
message mustEqual "AAAA"
|
||||
worlds.length mustEqual 0
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode and decode multiple worlds" in {
|
||||
val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ", 2,
|
||||
WorldInformation("ABCDABCD1", WorldStatus.Up, ServerType.Released, Vector(), PlanetSideEmpire.NC),
|
||||
var string = hex" 0597570065006c0063006f006d006500200074006f00200050006c0061006e0065007400530069006400650021002000028941424344414243443101000300006240414243444142434432000002020000"
|
||||
|
||||
val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ",
|
||||
Vector(
|
||||
WorldInformation("ABCDABCD1", WorldStatus.Up, ServerType.Released, Vector(), PlanetSideEmpire.NC),
|
||||
WorldInformation("ABCDABCD2", WorldStatus.Down, ServerType.Beta, Vector(), PlanetSideEmpire.TR)
|
||||
))
|
||||
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
println(pkt)
|
||||
pkt mustEqual string
|
||||
|
||||
// TODO: actually test something
|
||||
ok
|
||||
string = hex" 0597570065006c0063006f006d006500200074006f00200050006c0061006e0065007400530069006400650021002000028941424344414243443101000300006240414243444142434432000002020000"
|
||||
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case VNLWorldStatusMessage(message, worlds) =>
|
||||
message mustEqual "Welcome to PlanetSide! "
|
||||
|
||||
worlds.length mustEqual 2
|
||||
|
||||
worlds(0).name mustEqual "ABCDABCD1"
|
||||
worlds(0).empireNeed mustEqual PlanetSideEmpire.NC
|
||||
worlds(0).status mustEqual WorldStatus.Up
|
||||
worlds(0).serverType mustEqual ServerType.Released
|
||||
worlds(0).connections.length mustEqual 0
|
||||
|
||||
worlds(1).name mustEqual "ABCDABCD2"
|
||||
worlds(1).empireNeed mustEqual PlanetSideEmpire.TR
|
||||
worlds(1).status mustEqual WorldStatus.Down
|
||||
worlds(1).serverType mustEqual ServerType.Beta
|
||||
worlds(1).connections.length mustEqual 0
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -164,13 +164,10 @@ class LoginSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
|
||||
def updateServerList() = {
|
||||
val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ", 2,
|
||||
WorldInformation(
|
||||
serverName, WorldStatus.Up, ServerType.Beta, Vector(WorldConnectionInfo(serverAddress)), PlanetSideEmpire.VS
|
||||
),
|
||||
val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ",
|
||||
Vector(
|
||||
WorldInformation(
|
||||
serverName + "A", WorldStatus.Up, ServerType.Beta, Vector(WorldConnectionInfo(serverAddress)), PlanetSideEmpire.TR
|
||||
serverName, WorldStatus.Up, ServerType.Beta, Vector(WorldConnectionInfo(serverAddress)), PlanetSideEmpire.VS
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue