mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-19 18:14:44 +00:00
combined initial friend and formal list of friends into one list
This commit is contained in:
parent
58d7d922e4
commit
8cb5165c8b
|
|
@ -209,13 +209,130 @@ object PacketHelpers {
|
|||
* <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 non-negative before further processing.
|
||||
* @param size the known size of the `List`
|
||||
* It casts to a `Long` and passes onto an overloaded method.
|
||||
* @param size the known 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 sizedList[A](size : Int, codec : Codec[A]) : Codec[List[A]] = listOfN(provide(if(size < 0) 0 else size), codec)
|
||||
def listOfNSized[A](size : Int, codec : Codec[A]) : Codec[List[A]] = listOfNSized(if(size < 0) 0L else size.asInstanceOf[Long], 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 `Long` and assures that the parameter is non-negative before further processing.
|
||||
* @param size the known 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, provide
|
||||
* @return a codec that works on a List of A but excludes the size from the encoding
|
||||
*/
|
||||
def listOfNSized[A](size : Long, codec : Codec[A]) : Codec[List[A]] = listOfNAligned(provide(if(size < 0) 0 else size), 0, codec)
|
||||
|
||||
/**
|
||||
* Encode and decode a byte-aligned `List`.<br>
|
||||
* <br>
|
||||
* This function is copied almost verbatim from its source, but swapping the normal `ListCodec` for a new `AlignedListCodec`.
|
||||
* It also changes the type of the list length `Codec` from `Int` to `Long`.
|
||||
* Due to type erasure, this method can not be overloaded for both `Codec[Int]` and `Codec[Long]`.
|
||||
* The compiler would resolve both internally into type `Codec[T]` and their function definitions would be identical.
|
||||
* For the purposes of use, `longL(n)` will cast to an `Int` for the same acceptable values of `n` as in `uintL(n)`.
|
||||
* @param countCodec the codec that represents the prefixed size of the `List`
|
||||
* @param alignment the number of bits padded between the `List` size and the `List` contents
|
||||
* @param valueCodec a codec that describes each of the contents of the `List`
|
||||
* @tparam A the type of the `List` contents
|
||||
* @see codec\package.scala, listOfN
|
||||
* @return a codec that works on a List of A
|
||||
*/
|
||||
def listOfNAligned[A](countCodec : Codec[Long], alignment : Int, valueCodec : Codec[A]) : Codec[List[A]] = {
|
||||
countCodec.
|
||||
flatZip {
|
||||
count =>
|
||||
new AlignedListCodec(countCodec, valueCodec, alignment, Some(count))
|
||||
}.
|
||||
narrow[List[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"listOfN($countCodec, $valueCodec)")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The greater `Codec` class that encodes and decodes a byte-aligned `List`.<br>
|
||||
* <br>
|
||||
* This class is copied almost verbatim from its source, with two major modifications.
|
||||
* First, heavy modifications to its `encode` process account for the alignment value.
|
||||
* Second, the length field is parsed as a `Codec[Long]` value and type conversion is accounted for at several points.
|
||||
* @param countCodec the codec that represents the prefixed size of the `List`
|
||||
* @param valueCodec a codec that describes each of the contents of the `List`
|
||||
* @param alignment the number of bits padded between the `List` size and the `List` contents (on successful)
|
||||
* @param limit the number of elements in the `List`
|
||||
* @tparam A the type of the `List` contents
|
||||
* @see ListCodec.scala
|
||||
*/
|
||||
private class AlignedListCodec[A](countCodec : Codec[Long], valueCodec: Codec[A], alignment : Int, limit: Option[Long] = None) extends Codec[List[A]] {
|
||||
/**
|
||||
* Convert a `List` of elements into a byte-aligned `BitVector`.<br>
|
||||
* <br>
|
||||
* Bit padding after the encoded size of the `List` is only added if the `alignment` value is greater than zero and the initial encoding process was successful.
|
||||
* The padding is rather heavy-handed and a completely different `BitVector` is returned if successful.
|
||||
* @param list the `List` to be encoded
|
||||
* @return the `BitVector` encoding, if successful
|
||||
*/
|
||||
override def encode(list : List[A]) : Attempt[BitVector] = {
|
||||
var solve : Attempt[BitVector] = Encoder.encodeSeq(valueCodec)(list)
|
||||
if(alignment > 0) {
|
||||
solve match {
|
||||
case Attempt.Successful(vector) =>
|
||||
val countCodecSize : Long = countCodec.sizeBound.lowerBound
|
||||
solve = Attempt.successful(vector.take(countCodecSize) ++ BitVector.fill(alignment)(false) ++ vector.drop(countCodecSize))
|
||||
case _ =>
|
||||
solve = Attempt.failure(Err("failed to create a list"))
|
||||
}
|
||||
}
|
||||
solve
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a byte-aligned `BitVector` into a `List` of elements.
|
||||
* @param buffer the encoded bits in the `List`, preceded by the alignment bits
|
||||
* @return the decoded `List`
|
||||
*/
|
||||
override def decode(buffer: BitVector) = {
|
||||
val lim = Option( if(limit.isDefined) limit.get.asInstanceOf[Int] else 0 ) //TODO potentially unsafe size conversion
|
||||
Decoder.decodeCollect[List, A](valueCodec, lim)(buffer.drop(alignment))
|
||||
}
|
||||
|
||||
/**
|
||||
* The size of the encoded `List`.
|
||||
* @return the size as calculated by the size of each element for each element
|
||||
*/
|
||||
override def sizeBound = limit match {
|
||||
case None =>
|
||||
SizeBound.unknown
|
||||
case Some(lim : Long) =>
|
||||
valueCodec.sizeBound * lim
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a `String` representation of this `List`.
|
||||
* Unchanged from original.
|
||||
* @return the `String` representation
|
||||
*/
|
||||
override def toString = s"list($valueCodec)"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ package net.psforever.packet.game
|
|||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* An entry in the list of players known to and tracked by this player.
|
||||
|
|
@ -34,16 +35,12 @@ final case class Friend(name : String,
|
|||
* @param unk1 na; always 0?
|
||||
* @param unk2 na; always `true`?
|
||||
* @param unk3 na; always `true`?
|
||||
* @param number_of_friends the number of `Friend` entries handled by this packet; max is 15 per packet
|
||||
* @param friend the first `Friend` entry
|
||||
* @param friends all the other `Friend` entries
|
||||
* @param friends a list of `Friend`s
|
||||
*/
|
||||
final case class FriendsResponse(action : Int,
|
||||
unk1 : Int,
|
||||
unk2 : Boolean,
|
||||
unk3 : Boolean,
|
||||
number_of_friends : Int,
|
||||
friend : Option[Friend] = None,
|
||||
friends : List[Friend] = Nil)
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = FriendsResponse
|
||||
|
|
@ -75,7 +72,23 @@ object FriendsResponse extends Marshallable[FriendsResponse] {
|
|||
("unk3" | bool) ::
|
||||
(("number_of_friends" | uint4L) >>:~ { len =>
|
||||
conditional(len > 0, "friend" | Friend.codec) ::
|
||||
("friends" | PacketHelpers.sizedList(len-1, Friend.codec_list)) //List of 'Friend(String, Boolean)'s without a size field when encoded
|
||||
("friends" | PacketHelpers.listOfNSized(len-1, Friend.codec_list))
|
||||
})
|
||||
).as[FriendsResponse]
|
||||
).xmap[FriendsResponse] (
|
||||
{
|
||||
case act :: u1 :: u2 :: u3 :: num :: friend1 :: friends :: HNil =>
|
||||
val friendList : List[Friend] = if(friend1.isDefined) { friend1.get :: friends } else { friends }
|
||||
FriendsResponse(act, u1, u2, u3, friendList)
|
||||
},
|
||||
{
|
||||
case FriendsResponse(act, u1, u2, u3, friends) =>
|
||||
var friend1 : Option[Friend] = None
|
||||
var friendList : List[Friend] = Nil
|
||||
if(friends.nonEmpty) {
|
||||
friend1 = Some(friends.head)
|
||||
friendList = friends.drop(1)
|
||||
}
|
||||
act :: u1 :: u2 :: u3 :: friends.size :: friend1 :: friendList :: HNil
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -837,16 +837,14 @@ class GamePacketTest extends Specification {
|
|||
|
||||
"decode (one friend)" in {
|
||||
PacketCoding.DecodePacket(stringOneFriend).require match {
|
||||
case FriendsResponse(unk1, unk2, unk3, unk4, number_of_friends, friend, list) =>
|
||||
unk1 mustEqual 3
|
||||
case FriendsResponse(action, unk2, unk3, unk4, list) =>
|
||||
action mustEqual 3
|
||||
unk2 mustEqual 0
|
||||
unk3 mustEqual true
|
||||
unk4 mustEqual true
|
||||
number_of_friends mustEqual 1
|
||||
friend.isDefined mustEqual true
|
||||
friend.get.name mustEqual "KurtHectic-G"
|
||||
friend.get.online mustEqual false
|
||||
list.size mustEqual 0
|
||||
list.size mustEqual 1
|
||||
list.head.name mustEqual "KurtHectic-G"
|
||||
list.head.online mustEqual false
|
||||
case default =>
|
||||
ko
|
||||
}
|
||||
|
|
@ -854,24 +852,22 @@ class GamePacketTest extends Specification {
|
|||
|
||||
"decode (multiple friends)" in {
|
||||
PacketCoding.DecodePacket(stringManyFriends).require match {
|
||||
case FriendsResponse(unk1, unk2, unk3, unk4, number_of_friends, friend, list) =>
|
||||
unk1 mustEqual 0
|
||||
case FriendsResponse(action, unk2, unk3, unk4, list) =>
|
||||
action mustEqual 0
|
||||
unk2 mustEqual 0
|
||||
unk3 mustEqual true
|
||||
unk4 mustEqual true
|
||||
number_of_friends mustEqual 5
|
||||
friend.isDefined mustEqual true
|
||||
friend.get.name mustEqual "Angello-W"
|
||||
friend.get.online mustEqual false
|
||||
list.size mustEqual 4
|
||||
list.head.name mustEqual "thephattphrogg"
|
||||
list.size mustEqual 5
|
||||
list.head.name mustEqual "Angello-W"
|
||||
list.head.online mustEqual false
|
||||
list(1).name mustEqual "Kimpossible12"
|
||||
list(1).name mustEqual "thephattphrogg"
|
||||
list(1).online mustEqual false
|
||||
list(2).name mustEqual "Zearthling"
|
||||
list(2).name mustEqual "Kimpossible12"
|
||||
list(2).online mustEqual false
|
||||
list(3).name mustEqual "KurtHectic-G"
|
||||
list(3).name mustEqual "Zearthling"
|
||||
list(3).online mustEqual false
|
||||
list(4).name mustEqual "KurtHectic-G"
|
||||
list(4).online mustEqual false
|
||||
case default =>
|
||||
ko
|
||||
}
|
||||
|
|
@ -879,13 +875,11 @@ class GamePacketTest extends Specification {
|
|||
|
||||
"decode (short)" in {
|
||||
PacketCoding.DecodePacket(stringShort).require match {
|
||||
case FriendsResponse(unk1, unk2, unk3, unk4, number_of_friends, friend, list) =>
|
||||
unk1 mustEqual 4
|
||||
case FriendsResponse(action, unk2, unk3, unk4, list) =>
|
||||
action mustEqual 4
|
||||
unk2 mustEqual 0
|
||||
unk3 mustEqual true
|
||||
unk4 mustEqual true
|
||||
number_of_friends mustEqual 0
|
||||
friend.isDefined mustEqual false
|
||||
list.size mustEqual 0
|
||||
case default =>
|
||||
ko
|
||||
|
|
@ -893,24 +887,25 @@ class GamePacketTest extends Specification {
|
|||
}
|
||||
|
||||
"encode (one friend)" in {
|
||||
val msg = FriendsResponse(3, 0, true, true, 1, Option(Friend("KurtHectic-G", false)))
|
||||
val msg = FriendsResponse(3, 0, true, true, Friend("KurtHectic-G", false) :: Nil)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual stringOneFriend
|
||||
}
|
||||
|
||||
"encode (multiple friends)" in {
|
||||
val msg = FriendsResponse(0, 0, true, true, 5, Option(Friend("Angello-W", false)), Friend("thephattphrogg", false) ::
|
||||
Friend("Kimpossible12", false) ::
|
||||
Friend("Zearthling", false) ::
|
||||
Friend("KurtHectic-G", false) :: Nil)
|
||||
val msg = FriendsResponse(0, 0, true, true, 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
|
||||
}
|
||||
|
||||
"encode (short)" in {
|
||||
val msg = FriendsResponse(4, 0, true, true, 0)
|
||||
val msg = FriendsResponse(4, 0, true, true)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual stringShort
|
||||
|
|
|
|||
Loading…
Reference in a new issue