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 scodec.{DecodeResult, Err, Codec, Attempt}
import scodec.Attempt.Successful
import scodec.{Attempt, Codec, DecodeResult, Err}
import scodec.bits._
import scodec.codecs._
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.
* Padding may also exist between the length field and the beginning of the contents.
* @param lenSize a codec that defines the bit size that encodes the length
* @param adjustment the optional alignment for padding; defaults to 0
* @return the `String` `Codec`
* Codec that encodes/decodes a list of `n` elements, where `n` is known at compile time.<br>
* <br>
* This function is copied almost verbatim from its source, with exception of swapping the parameter that is normally a `Nat` `literal`.
* The modified function takes a normal unsigned `Integer` and assures that the parameter is a non-negative before further processing.
* @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] =
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)
def sizedList[A](size : Int, codec : Codec[A]) : Codec[List[A]] = listOfN(provide(if(size < 0) 0 else size), codec)
}

View file

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

View file

@ -831,25 +831,64 @@ class GamePacketTest extends Specification {
}
"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 {
PacketCoding.DecodePacket(string).require match {
case FriendsResponse(player_guid, friend, unk) =>
player_guid mustEqual PlanetSideGUID(35937)
friend.length mustEqual 12
friend mustEqual "KurtHectic-G"
unk mustEqual false
"decode (one friend)" in {
PacketCoding.DecodePacket(stringOneFriend).require match {
case FriendsResponse(unk1, unk2, unk3, unk4, number_of_friends, friend, list) =>
unk1 mustEqual 3
unk2 mustEqual 0
unk3 mustEqual true
unk4 mustEqual true
number_of_friends mustEqual 1
friend.name mustEqual "KurtHectic-G"
friend.online mustEqual false
list.size mustEqual 0
case default =>
ko
}
}
"encode" in {
val msg = FriendsResponse(PlanetSideGUID(35937), "KurtHectic-G", false)
"decode (multiple friends)" in {
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
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
}
}