solved encoding issues, but am not perfectly happy with the solution; tests pass for one friend name and many friend names; check new codec for sizedList

This commit is contained in:
FateJH 2016-10-11 15:20:05 -04:00
parent b444b4b503
commit 3e84e999fb
3 changed files with 94 additions and 34 deletions

View file

@ -3,7 +3,8 @@ package net.psforever.packet
import java.nio.charset.Charset import java.nio.charset.Charset
import scodec.{DecodeResult, Err, Codec, Attempt} import scodec.Attempt.Successful
import scodec.{Attempt, Codec, DecodeResult, Err}
import scodec.bits._ import scodec.bits._
import scodec.codecs._ import scodec.codecs._
import scodec._ import scodec._
@ -205,15 +206,17 @@ object PacketHelpers {
*/ */
/** /**
* Construct a `Codec` for reading `wchar_t` (wide character) `Strings` whose length field are constrained to specific bit size proportions. * Codec that encodes/decodes a list of `n` elements, where `n` is known at compile time.<br>
* Padding may also exist between the length field and the beginning of the contents. * <br>
* @param lenSize a codec that defines the bit size that encodes the length * This function is copied almost verbatim from its source, with exception of swapping the parameter that is normally a `Nat` `literal`.
* @param adjustment the optional alignment for padding; defaults to 0 * The modified function takes a normal unsigned `Integer` and assures that the parameter is a non-negative before further processing.
* @return the `String` `Codec` * @param size the fixed size of the `List`
* @param codec a codec that describes each of the contents of the `List`
* @tparam A the type of the `List` contents
* @see codec\package.scala, sizedList
* @see codec\package.scala, listOfN
* @see codec\package.scala, provides
* @return a codec that works on a List of A but excludes the size from the encoding
*/ */
def specSizeWideStringAligned(lenSize : Codec[Int], adjustment : Int = 0) : Codec[String] = def sizedList[A](size : Int, codec : Codec[A]) : Codec[List[A]] = listOfN(provide(if(size < 0) 0 else size), codec)
variableSizeBytes((lenSize <~ ignore(adjustment)).xmap(
insize => insize*2, // number of symbols -> number of bytes (decode)
outSize => outSize/2 // number of bytes -> number of symbols (encode)
), utf16)
} }

View file

@ -5,25 +5,43 @@ import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, Plan
import scodec.Codec import scodec.Codec
import scodec.codecs._ import scodec.codecs._
/** final case class Friend(name : String = "",
* na online : Boolean = false)
* @param player_guid the player
* @param friend the name of the friend final case class FriendsResponse(unk1 : Int,
* @param unk na unk2 : Int,
*/ unk3 : Boolean,
final case class FriendsResponse(player_guid : PlanetSideGUID, unk4 : Boolean,
friend : String, number_of_friends : Int,
unk : Boolean) friend : Friend,
friends : List[Friend] = Nil)
extends PlanetSideGamePacket { extends PlanetSideGamePacket {
type Packet = FriendsResponse type Packet = FriendsResponse
def opcode = GamePacketOpcode.FriendsResponse def opcode = GamePacketOpcode.FriendsResponse
def encode = FriendsReponse.encode(this) def encode = FriendsReponse.encode(this)
} }
object Friend extends Marshallable[Friend] {
implicit val codec : Codec[Friend] = (
("name" | PacketHelpers.encodedWideStringAligned(3)) ::
("online" | bool)
).as[Friend]
implicit val codec_list : Codec[Friend] = (
("name" | PacketHelpers.encodedWideStringAligned(7)) ::
("online" | bool)
).as[Friend]
}
object FriendsReponse extends Marshallable[FriendsResponse] { object FriendsReponse extends Marshallable[FriendsResponse] {
implicit val codec : Codec[FriendsResponse] = ( implicit val codec : Codec[FriendsResponse] = (
("player_guid" | PlanetSideGUID.codec) :: ("unk1" | uintL(3)) ::
("friend" | PacketHelpers.specSizeWideStringAligned(uint(5), 3)) :: ("unk2" | uintL(4)) ::
("unk" | bool) ("unk3" | bool) ::
("unk4" | bool) ::
(("number_of_friends" | uintL(4)) >>:~ { len =>
("friend" | Friend.codec) ::
("friends" | PacketHelpers.sizedList(len-1, Friend.codec_list))
})
).as[FriendsResponse] ).as[FriendsResponse]
} }

View file

@ -831,25 +831,64 @@ class GamePacketTest extends Specification {
} }
"FriendsResponse" should { "FriendsResponse" should {
val string = hex"73 618C 60 4B007500720074004800650063007400690063002D004700 00" val stringOneFriend = hex"73 61 8C 60 4B007500720074004800650063007400690063002D004700 00"
val stringManyFriends = hex"73 01 AC 48 4100 6E00 6700 6500 6C00 6C00 6F00 2D00 5700 47 00 7400 6800 6500 7000 6800 6100 7400 7400 7000 6800 7200 6F00 6700 6700 46 80 4B00 6900 6D00 7000 6F00 7300 7300 6900 6200 6C00 6500 3100 3200 45 00 5A00 6500 6100 7200 7400 6800 6C00 6900 6E00 6700 46 00 4B00 7500 7200 7400 4800 6500 6300 7400 6900 6300 2D00 4700 00"
"decode" in { "decode (one friend)" in {
PacketCoding.DecodePacket(string).require match { PacketCoding.DecodePacket(stringOneFriend).require match {
case FriendsResponse(player_guid, friend, unk) => case FriendsResponse(unk1, unk2, unk3, unk4, number_of_friends, friend, list) =>
player_guid mustEqual PlanetSideGUID(35937) unk1 mustEqual 3
friend.length mustEqual 12 unk2 mustEqual 0
friend mustEqual "KurtHectic-G" unk3 mustEqual true
unk mustEqual false unk4 mustEqual true
number_of_friends mustEqual 1
friend.name mustEqual "KurtHectic-G"
friend.online mustEqual false
list.size mustEqual 0
case default => case default =>
ko ko
} }
} }
"encode" in { "decode (multiple friends)" in {
val msg = FriendsResponse(PlanetSideGUID(35937), "KurtHectic-G", false) PacketCoding.DecodePacket(stringManyFriends).require match {
case FriendsResponse(unk1, unk2, unk3, unk4, number_of_friends, friend, list) =>
unk1 mustEqual 0
unk2 mustEqual 0
unk3 mustEqual true
unk4 mustEqual true
number_of_friends mustEqual 5
friend.name mustEqual "Angello-W"
friend.online mustEqual false
list.size mustEqual 4
list.head.name mustEqual "thephattphrogg"
list.head.online mustEqual false
list(1).name mustEqual "Kimpossible12"
list(1).online mustEqual false
list(2).name mustEqual "Zearthling"
list(2).online mustEqual false
list(3).name mustEqual "KurtHectic-G"
list(3).online mustEqual false
case default =>
ko
}
}
"encode (one friend)" in {
val msg = FriendsResponse(3, 0, true, true, 1, Friend("KurtHectic-G", false))
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string pkt mustEqual stringOneFriend
}
"encode (multiple friends)" in {
val msg = FriendsResponse(0, 0, true, true, 5, Friend("Angello-W", false), Friend("thephattphrogg", false) ::
Friend("Kimpossible12", false) ::
Friend("Zearthling", false) ::
Friend("KurtHectic-G", false) :: Nil)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual stringManyFriends
} }
} }