renamed classes in BattleplanMessage to more appropriate names; added an Enumeration class for action numbers; queued up a bunch of test data for later

This commit is contained in:
FateJH 2017-04-28 09:11:15 -04:00
parent 7d158eba1a
commit 331406a849
3 changed files with 185 additions and 137 deletions

View file

@ -8,25 +8,53 @@ import scodec.codecs._
import shapeless.{::, HNil}
/**
* A common ancestor of all the different "sheets" used to keep track of the data.
* A `Codec` for the actions that each layer of the diagram performs.
* `Action1`, `Action2`, `Action5`, `Action6`, and `Action7` have additional `DiagramStroke` input data.
*/
sealed trait DiagramSheet
object DiagramActionCode extends Enumeration {
type Type = Value
val Action0,
Action1,
Action2,
Action3,
Action4,
Action5,
Action6,
Action7,
Action8,
Action9,
ActionA,
ActionB,
ActionC,
ActionD,
ActionE,
ActionF
= Value //TODO replace these with descriptive wording
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint4L)
}
/**
* A common ancestor of all the different "strokes" used to keep track of the data.
*/
sealed trait DiagramStroke
/**
* na
* @param unk1 na
* @param unk2 na
*/
final case class SheetOne(unk1 : Float,
unk2 : Int) extends DiagramSheet
final case class StrokeOne(unk1 : Float,
unk2 : Int) extends DiagramStroke
/**
* na
* @param unk1 na
* @param unk2 na
*/
final case class SheetTwo(unk1 : Float,
unk2 : Float) extends DiagramSheet
final case class StrokeTwo(unk1 : Float,
unk2 : Float) extends DiagramStroke
/**
* na
@ -35,10 +63,10 @@ final case class SheetTwo(unk1 : Float,
* @param unk3 na
* @param unk4 na
*/
final case class SheetFive(unk1 : Float,
unk2 : Float,
unk3 : Float,
unk4 : Int) extends DiagramSheet
final case class StrokeFive(unk1 : Float,
unk2 : Float,
unk3 : Float,
unk4 : Int) extends DiagramStroke
/**
* na
@ -48,37 +76,39 @@ final case class SheetFive(unk1 : Float,
* @param unk4 na
* @param unk5 na
*/
final case class SheetSix(unk1 : Float,
unk2 : Float,
unk3 : Int,
unk4 : Int,
unk5 : String) extends DiagramSheet
final case class StrokeSix(unk1 : Float,
unk2 : Float,
unk3 : Int,
unk4 : Int,
unk5 : String) extends DiagramStroke
/**
* na
* @param unk na
*/
final case class SheetSeven(unk : Int) extends DiagramSheet
final case class StrokeSeven(unk : Int) extends DiagramStroke
/**
* na
* @param pageNum a hint to kind of data stored
* @param sheet the data
* @param action the behavior of this stroke;
* a hint to kind of stroke data stored, if at all, and how to use it or incorporate prior data
* @param stroke the data
*/
final case class BattleDiagram(pageNum : Int,
sheet : Option[DiagramSheet] = None)
final case class BattleDiagramAction(action : DiagramActionCode.Value,
stroke : Option[DiagramStroke] = None)
/**
* na
* @param unk1 na
* @param mastermind the player who contributed this battle plan
* @param unk2 na
* @param diagrams a list of the individual `BattleDiagram`s that compose this plan
* @param char_id na;
* same as in `CharacterInfoMessage`
* @param player_name the player who contributed this battle plan
* @param zone_id on which continent the battle plan will be overlaid
* @param diagrams a list of the individual actions that compose this plan
*/
final case class BattleplanMessage(unk1 : Long,
mastermind : String,
unk2 : Int,
diagrams : List[BattleDiagram])
final case class BattleplanMessage(char_id : Long,
player_name : String,
zone_id : Int,
diagrams : List[BattleDiagramAction])
extends PlanetSideGamePacket {
type Packet = BattleplanMessage
def opcode = GamePacketOpcode.BattleplanMessage
@ -87,170 +117,170 @@ final case class BattleplanMessage(unk1 : Long,
object BattelplanDiagram {
/**
* Create a `BattleDiagram` object containing `SheetOne` data.
* Create a `BattleDiagramAction` object containing `StrokeOne` data.
* @param unk1 na
* @param unk2 na
* @return a `BattleDiagram` object
* @return a `BattleDiagramAction` object
*/
def sheet1(unk1 : Float, unk2 : Int) : BattleDiagram =
BattleDiagram(1, Some(SheetOne(unk1, unk2)))
def stroke1(unk1 : Float, unk2 : Int) : BattleDiagramAction =
BattleDiagramAction(DiagramActionCode.Action1, Some(StrokeOne(unk1, unk2)))
/**
* Create a `BattleDiagram` object containing `SheetTwo` data.
* Create a `BattleDiagramAction` object containing `StrokeTwo` data.
* @param unk1 na
* @param unk2 na
* @return a `BattleDiagram` object
* @return a `BattleDiagramAction` object
*/
def sheet2(unk1 : Float, unk2 : Float) : BattleDiagram =
BattleDiagram(2, Some(SheetTwo(unk1, unk2)))
def stroke2(unk1 : Float, unk2 : Float) : BattleDiagramAction =
BattleDiagramAction(DiagramActionCode.Action2, Some(StrokeTwo(unk1, unk2)))
/**
* Create a `BattleDiagram` object containing `SheetFive` data.
* Create a `BattleDiagramAction` object containing `StrokeFive` data.
* @param unk1 na
* @param unk2 na
* @param unk3 na
* @param unk4 na
* @return a `BattleDiagram` object
* @return a `BattleDiagramAction` object
*/
def sheet5(unk1 : Float, unk2 : Float, unk3 : Float, unk4 : Int) : BattleDiagram =
BattleDiagram(5, Some(SheetFive(unk1, unk2, unk3, unk4)))
def stroke5(unk1 : Float, unk2 : Float, unk3 : Float, unk4 : Int) : BattleDiagramAction =
BattleDiagramAction(DiagramActionCode.Action5, Some(StrokeFive(unk1, unk2, unk3, unk4)))
/**
* Create a `BattleDiagram` object containing `SheetSix` data.
* Create a `BattleDiagramAction` object containing `StrokeSix` data.
* @param unk1 na
* @param unk2 na
* @param unk3 na
* @param unk4 na
* @param unk5 na
* @return a `BattleDiagram` object
* @return a `BattleDiagramAction` object
*/
def sheet6(unk1 : Float, unk2 : Float, unk3 : Int, unk4 : Int, unk5 : String) : BattleDiagram =
BattleDiagram(6, Some(SheetSix(unk1, unk2, unk3, unk4, unk5)))
def stroke6(unk1 : Float, unk2 : Float, unk3 : Int, unk4 : Int, unk5 : String) : BattleDiagramAction =
BattleDiagramAction(DiagramActionCode.Action6, Some(StrokeSix(unk1, unk2, unk3, unk4, unk5)))
/**
* Create a `BattleDiagram` object containing `SheetSeven` data.
* Create a `BattleDiagramAction` object containing `StrokeSeven` data.
* @param unk na
* @return a `BattleDiagram` object
* @return a `BattleDiagramAction` object
*/
def sheet7(unk : Int) : BattleDiagram =
BattleDiagram(7, Some(SheetSeven(unk)))
def stroke7(unk : Int) : BattleDiagramAction =
BattleDiagramAction(DiagramActionCode.Action7, Some(StrokeSeven(unk)))
}
object BattleplanMessage extends Marshallable[BattleplanMessage] {
/**
* An intermediary object intended to temporarily store `BattleDiagram` objects.<br>
* An intermediary object intended to temporarily store `BattleDiagramAction` objects.<br>
* <br>
* This hidden object is arranged like a linked list;
* but, later, it is converted into an accessible formal `List` of `BattleDiagram` objects during decoding;
* but, later, it is converted into an accessible formal `List` during decoding;
* likewise, during the encoding process, the `List` is transformed back into a linked list structure.
* `Scala`'s own linked list `Collection` is deprecated, without substitution, so this custom one must be used.
* @param diagram the contained `BattleDiagram` with the sheet that maintains the data
* @param next the next `BattleDiagramLayer`, if any, arranging into a linked list
* @param diagram the contained object that maintains the data
* @param next the next `BattleDiagramChain`, if any
*/
private final case class BattleDiagramLayer(diagram : BattleDiagram,
next : Option[BattleDiagramLayer])
private final case class BattleDiagramChain(diagram : BattleDiagramAction,
next : Option[BattleDiagramChain])
/**
* Parse data into a `SheetOne` object.
* Parse data into a `StrokeOne` object.
*/
private val plan1_codec : Codec[SheetOne] = ( //size: 8; pad: +0
private val plan1_codec : Codec[StrokeOne] = ( //size: 8; pad: +0
("unk1" | newcodecs.q_float(0.0, 16.0, 5)) ::
("unk2" | uintL(3))
).as[SheetOne]
).as[StrokeOne]
/**
* Parse data into a `SheetTwo` object.
* Parse data into a `StrokeTwo` object.
*/
private val plan2_codec : Codec[SheetTwo] = ( //size: 22; pad: +2
private val plan2_codec : Codec[StrokeTwo] = ( //size: 22; pad: +2
("unk1" | newcodecs.q_float(-4096.0, 12288.0, 11)) ::
("unk2" | newcodecs.q_float(-4096.0, 12288.0, 11))
).as[SheetTwo]
).as[StrokeTwo]
/**
* Parse data into a `SheetFive` object.
* Parse data into a `StrokeFive` object.
*/
private val plan5_codec : Codec[SheetFive] = ( //size: 44; pad: +4
private val plan5_codec : Codec[StrokeFive] = ( //size: 44; pad: +4
("unk1" | newcodecs.q_float(-4096.0, 12288.0, 11)) ::
("unk2" | newcodecs.q_float(-4096.0, 12288.0, 11)) ::
("unk3" | newcodecs.q_float(1024.0, 0.0, 11)) ::
("unk4" | uintL(11))
).as[SheetFive]
).as[StrokeFive]
/**
* Parse data into a `SheetSix` object.
* Parse data into a `StrokeSix` object.
* @param pad the current padding for the `String` entry
*/
private def plan6_codec(pad : Int) : Codec[SheetSix] = ( //size: 31 + string.length.field + string.length * 16 + padding; pad: value resets
private def plan6_codec(pad : Int) : Codec[StrokeSix] = ( //size: 31 + string.length.field + string.length * 16 + padding; pad: value resets
("unk1" | newcodecs.q_float(-4096.0, 12288.0, 11)) ::
("unk2" | newcodecs.q_float(-4096.0, 12288.0, 11)) ::
("unk3" | uintL(3)) ::
("unk4" | uintL(6)) ::
("unk5" | PacketHelpers.encodedWideStringAligned( (pad + 1) % 8 ))
).as[SheetSix]
).as[StrokeSix]
/**
* Parse data into a `SheetSeven` object.
* Parse data into a `StrokeSeven` object.
*/
private val plan7_codec : Codec[SheetSeven] = ("unk" | uintL(6)).as[SheetSeven] // size: 6; pad: +2
private val plan7_codec : Codec[StrokeSeven] = ("unk" | uintL(6)).as[StrokeSeven] // size: 6; pad: +2
/**
* Switch between different patterns to create a `BattleDiagram` for the following data.
* Switch between different patterns to create a `BattleDiagramAction` for the following data.
* @param plan a hint to help parse the following data
* @param pad the current padding for any `String` entry stored within the parsed elements;
* when `plan == 6`, `plan6_codec` utilizes this value
* @return a `BattleDiagram` object
* @return a `BattleDiagramAction` object
*/
private def diagram_codec(plan : Int, pad : Int) : Codec[BattleDiagram] = (
conditional(plan == 1, plan1_codec) ::
conditional(plan == 2, plan2_codec) ::
conditional(plan == 5, plan5_codec) ::
conditional(plan == 6, plan6_codec(pad)) ::
conditional(plan == 7, plan7_codec)
).exmap[BattleDiagram] (
private def diagram_codec(plan : DiagramActionCode.Value, pad : Int) : Codec[BattleDiagramAction] = (
conditional(plan == DiagramActionCode.Action1, plan1_codec) ::
conditional(plan == DiagramActionCode.Action2, plan2_codec) ::
conditional(plan == DiagramActionCode.Action5, plan5_codec) ::
conditional(plan == DiagramActionCode.Action6, plan6_codec(pad)) ::
conditional(plan == DiagramActionCode.Action7, plan7_codec)
).exmap[BattleDiagramAction] (
{
case Some(sheet) :: None :: None :: None :: None :: HNil =>
Attempt.successful(BattleDiagram(plan, Some(sheet)))
case Some(stroke) :: None :: None :: None :: None :: HNil =>
Attempt.successful(BattleDiagramAction(plan, Some(stroke)))
case None :: Some(sheet) :: None :: None :: None :: HNil =>
Attempt.successful(BattleDiagram(plan, Some(sheet)))
case None :: Some(stroke) :: None :: None :: None :: HNil =>
Attempt.successful(BattleDiagramAction(plan, Some(stroke)))
case None :: None :: Some(sheet) :: None :: None :: HNil =>
Attempt.successful(BattleDiagram(plan, Some(sheet)))
case None :: None :: Some(stroke) :: None :: None :: HNil =>
Attempt.successful(BattleDiagramAction(plan, Some(stroke)))
case None :: None :: None :: Some(sheet) :: None :: HNil =>
Attempt.successful(BattleDiagram(plan, Some(sheet)))
case None :: None :: None :: Some(stroke) :: None :: HNil =>
Attempt.successful(BattleDiagramAction(plan, Some(stroke)))
case None :: None :: None :: None :: Some(sheet) :: HNil =>
Attempt.successful(BattleDiagram(plan, Some(sheet)))
case None :: None :: None :: None :: Some(stroke) :: HNil =>
Attempt.successful(BattleDiagramAction(plan, Some(stroke)))
case None :: None :: None :: None :: None :: HNil =>
Attempt.successful(BattleDiagram(plan, None))
Attempt.successful(BattleDiagramAction(plan, None))
case _:: _ :: _ :: _ :: _ :: HNil =>
Attempt.failure(Err(s"too many sheets at once for $plan"))
Attempt.failure(Err(s"too many strokes for action $plan"))
},
{
case BattleDiagram(1, Some(sheet)) =>
Attempt.successful(Some(sheet.asInstanceOf[SheetOne]) :: None :: None :: None :: None :: HNil)
case BattleDiagramAction(DiagramActionCode.Action1, Some(stroke)) =>
Attempt.successful(Some(stroke.asInstanceOf[StrokeOne]) :: None :: None :: None :: None :: HNil)
case BattleDiagram(2, Some(sheet)) =>
Attempt.successful(None :: Some(sheet.asInstanceOf[SheetTwo]) :: None :: None :: None :: HNil)
case BattleDiagramAction(DiagramActionCode.Action2, Some(stroke)) =>
Attempt.successful(None :: Some(stroke.asInstanceOf[StrokeTwo]) :: None :: None :: None :: HNil)
case BattleDiagram(5, Some(sheet)) =>
Attempt.successful(None :: None :: Some(sheet.asInstanceOf[SheetFive]) :: None :: None :: HNil)
case BattleDiagramAction(DiagramActionCode.Action5, Some(stroke)) =>
Attempt.successful(None :: None :: Some(stroke.asInstanceOf[StrokeFive]) :: None :: None :: HNil)
case BattleDiagram(6, Some(sheet)) =>
Attempt.successful(None :: None :: None :: Some(sheet.asInstanceOf[SheetSix]) :: None :: HNil)
case BattleDiagramAction(DiagramActionCode.Action6, Some(stroke)) =>
Attempt.successful(None :: None :: None :: Some(stroke.asInstanceOf[StrokeSix]) :: None :: HNil)
case BattleDiagram(7, Some(sheet)) =>
Attempt.successful(None :: None :: None :: None :: Some(sheet.asInstanceOf[SheetSeven]) :: HNil)
case BattleDiagramAction(DiagramActionCode.Action7, Some(stroke)) =>
Attempt.successful(None :: None :: None :: None :: Some(stroke.asInstanceOf[StrokeSeven]) :: HNil)
case BattleDiagram(_, None) =>
case BattleDiagramAction(_, None) =>
Attempt.successful(None :: None :: None :: None :: None :: HNil)
case BattleDiagram(n, _) =>
Attempt.failure(Err(s"unhandled sheet number $n"))
case BattleDiagramAction(n, _) =>
Attempt.failure(Err(s"unhandled stroke action number $n"))
}
)
@ -260,74 +290,74 @@ object BattleplanMessage extends Marshallable[BattleplanMessage] {
* @param remaining the number of elements remaining to parse
* @param pad the current padding for any `String` entry stored within the parsed elements;
* different elements add different padding offset to this field on subsequent passes
* @return a `Codec` for `BattleDiagramLayer` objects
* @return a `Codec` for `BattleDiagramChain` segments
*/
private def parse_diagrams_codec(remaining : Int, pad : Int = 0) : Codec[BattleDiagramLayer] = (
uint4L >>:~ { plan =>
private def parse_diagrams_codec(remaining : Int, pad : Int = 0) : Codec[BattleDiagramChain] = (
DiagramActionCode.codec >>:~ { plan =>
("diagram" | diagram_codec(plan, pad)) ::
conditional(remaining > 1,
"next" | parse_diagrams_codec(
remaining - 1,
pad + (if(plan == 2 || plan == 7) { 2 } else if(plan == 5) { 4 } else if(plan == 6) { -pad } else { 0 })
pad + (if(plan == DiagramActionCode.Action2 || plan == DiagramActionCode.Action7) { 2 } else if(plan == DiagramActionCode.Action5) { 4 } else if(plan == DiagramActionCode.Action6) { -pad } else { 0 })
)
)
}).exmap[BattleDiagramLayer] (
}).exmap[BattleDiagramChain] (
{
case _ :: diagram :: next :: HNil =>
Attempt.successful(BattleDiagramLayer(diagram, next))
Attempt.successful(BattleDiagramChain(diagram, next))
},
{
case BattleDiagramLayer(BattleDiagram(num, sheet), next) =>
Attempt.successful(num :: BattleDiagram(num, sheet) :: next :: HNil)
case BattleDiagramChain(BattleDiagramAction(num, stroke), next) =>
Attempt.successful(num :: BattleDiagramAction(num, stroke) :: next :: HNil)
}
)
import scala.collection.mutable.ListBuffer
/**
* Transform a linked list of `BattleDiagramLayer` into a `List` of `BattleDiagram` objects.
* @param element the current link in a chain of `BattleDiagramLayer` objects
* Transform a linked list of `BattleDiagramChain` into a `List` of `BattleDiagramAction` objects.
* @param element the current link in a chain of `BattleDiagramChain` objects
* @param list a `List` of extracted `BattleDiagrams`;
* technically, the output
*/
private def rollDiagramLayers(element : BattleDiagramLayer, list : ListBuffer[BattleDiagram]) : Unit = {
private def rollDiagramLayers(element : BattleDiagramChain, list : ListBuffer[BattleDiagramAction]) : Unit = {
list += element.diagram
if(element.next.isDefined)
rollDiagramLayers(element.next.get, list) //tail call optimization
}
/**
* Transform a `List` of `BattleDiagram` objects into a linked list of `BattleDiagramLayer` objects.
* Transform a `List` of `BattleDiagramAction` objects into a linked list of `BattleDiagramChain` objects.
* @param revIter a reverse `List` `Iterator` for a `List` of `BattleDiagrams`
* @param layers the current head of a chain of `BattleDiagramLayer` objects;
* @param layers the current head of a chain of `BattleDiagramChain` objects;
* technically, the output
* @return a linked list of `BattleDiagramLayer` objects
* @return a linked list of `BattleDiagramChain` objects
*/
private def unrollDiagramLayers(revIter : Iterator[BattleDiagram], layers : Option[BattleDiagramLayer] = None) : Option[BattleDiagramLayer] = {
private def unrollDiagramLayers(revIter : Iterator[BattleDiagramAction], layers : Option[BattleDiagramChain] = None) : Option[BattleDiagramChain] = {
if(!revIter.hasNext)
return layers
val elem : BattleDiagram = revIter.next
unrollDiagramLayers(revIter, Some(BattleDiagramLayer(elem, layers))) //tail call optimization
val elem : BattleDiagramAction = revIter.next
unrollDiagramLayers(revIter, Some(BattleDiagramChain(elem, layers))) //tail call optimization
}
implicit val codec : Codec[BattleplanMessage] = (
("unk1" | uint32L) ::
("mastermind" | PacketHelpers.encodedWideString) ::
("unk2" | uint16L) ::
("char_id" | uint32L) ::
("player_name" | PacketHelpers.encodedWideString) ::
("zone_id" | uint16L) ::
(uint8L >>:~ { count =>
conditional(count > 0, "diagrams" | parse_diagrams_codec(count)).hlist
})
).exmap[BattleplanMessage] (
{
case unk1 :: unk2 :: unk3 :: _ :: diagramLayers :: HNil =>
val list : ListBuffer[BattleDiagram] = new ListBuffer()
case char_id :: player :: zone_id :: _ :: diagramLayers :: HNil =>
val list : ListBuffer[BattleDiagramAction] = new ListBuffer()
if(diagramLayers.isDefined)
rollDiagramLayers(diagramLayers.get, list)
Attempt.successful(BattleplanMessage(unk1, unk2, unk3, list.toList))
Attempt.successful(BattleplanMessage(char_id, player, zone_id, list.toList))
},
{
case BattleplanMessage(unk1, unk2, unk3, diagrams) =>
val layersOpt = unrollDiagramLayers(diagrams.reverseIterator)
Attempt.successful(unk1 :: unk2 :: unk3 :: diagrams.size :: layersOpt :: HNil)
case BattleplanMessage(char_id, player_name, zone_id, diagrams) =>
val layersOpt : Option[BattleDiagramChain] = unrollDiagramLayers(diagrams.reverseIterator)
Attempt.successful(char_id :: player_name :: zone_id :: diagrams.size :: layersOpt :: HNil)
}
)
}

View file

@ -8,17 +8,34 @@ import scodec.bits._
class BattleplanMessageTest extends Specification {
val string = hex"b3 3a197902 94 59006500740041006e006f0074006800650072004600610069006c0075007200650041006c007400 0000 01 e0"
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a00202aba2b4aae8bd2aba334aae8dd2aca3b4ab28fd2aca414ab29152aca474ab292d2ada4d4ab69452ada534ab695d2ada594ab696d2ada5d4ab697d2ada614ab698d2ada654ab699d2ada694ab69ad2aea6d4aba9bd2aea714aba9cd2aea754aba9dd2aea794aba9ed
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a002025ca4d497292525ca47497291525ca4149728f525ca3b49728dd25ca3549728cd25ca3149728bd25ca2d49728ad25ca29497289d25ca25497288d25ca21497287d25ca1d497286d25ca19497285d25ca15497284d25da11497683d25da0d497682d25da09497681d
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a002025da05497680d25da014976ffc25dbfd0976fec25dbf90976fdc25dbf50976fcc25dbf10976fbc25dbed0976fac25dbe90976f9425dbe30972f8425cbdd0972f6425cbd70972f4c25bbcf096ef2c25bbc9096ef1c25bbc5096ef0425bbbf096eef425bbbb096eee4
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a002027aa4349ea90527aa3f49ea8f527aa3b49ea8e527ba3749ee8d527ba3349ee8bd27ba2d49ee8ad27ca2949f289d27ca2549f288d27ca2149f287d27da1d49f686d27da1749fa85527ea1349fa84527ea0f49fe83527fa0b49fe82527fa0749fe81527fa0349fe805
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a00202a8bbb0aa6eec2a9bbd0aa6efc2a9bc10aa6f0c2a9bc50aa6f1c2a9bc90aa6f2c2a9bcf0aa6f442a9bd30aa6f542a9bd70aa6f642a9bdb0aa6f742a9bdf0aa6f842a9be30aa6f942a9be70aa6fa42a9beb0aa6fb42a9bef0aa6fcc2a9bf50aa6fdc2a9bf90aa6ff4
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a00201b127ba8949eea1d27ba8549eea0527aa7f49ea9f527aa7b49ea9e527aa7749ea9d527aa7349ea9c527aa6f49ea9ad27aa6949ea99d27aa6349ea98527aa5f49ea97527aa5b49ea96527aa5549ea94d27aa5149ea93d27aa4d49ea92d27aa4949ea91d27aa4540
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a001e27fbff09feff427fbfb09fafe427ebf709fafd427ebf309f6fcc27dbf109f6fbc27dbed09f2fac27cbe909f2f9c27cbe509eef8c27bbe109eef7c27abdd09eaf6c27abd909eaf5c27abd509e6f54279bd309e6f44279bcf09e6f34279bcb09e6f240
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a0020891b1260a8b4982a25260a874982a15260a834982a0525fa7f497e9f525fa7b497e9e525ea77497a9d525ea73497a9c525ea6f497a9b525ea6b497a9a525da67497699525da63497698525da5f497697525da5b497696525ca57497295525ca534
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a001a25abad096aeac25aba9096ae9c25aba5096ae8c25aba1096ae7c25ab9d096ae6c25ab99096ae5c25ab95096ae4c25ab91096ae3c25ab8d096ae2c25ab89096ae1c25ab85096ae0c25ab81096adfc25bb7d096edec0
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a0018241c8149071fd241c7d49071ed241c7949071dd241c7549031cd240c7149031bd240c6d49031ad240c69490319d240c65490318d240c61490317d240c5d490316d240c59490315d240c55490314d
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a000a2a9bff0aaa8052aaa074aaa82d2aaa0f4aaa85d2aaa1b4aaa87d2aaa234aae89d0
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a000a279bc709e6f14279bc309e6f04279bbf09e6ef4279bbb09e2eec278bb90c
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a000829ebb90a7eee42a0bb90a86eec2a2bbb0a96eec2a6bbb0a9eeec
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a00072aea7d4aba9fd2aea814abaa0d2aea854abaa1d2aea894
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a000625bbb7096eed425bbb3096eec425bbaf096aebc0
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a000225ca51497293d0
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a000130
"decode" in {
PacketCoding.DecodePacket(string).require match {
case BattleplanMessage(unk1, mastermind, unk2, diagrams) =>
unk1 mustEqual 41490746
mastermind mustEqual "YetAnotherFailureAlt"
unk2 mustEqual 0
case BattleplanMessage(char_id, player_name, zone_id, diagrams) =>
char_id mustEqual 41490746
player_name mustEqual "YetAnotherFailureAlt"
zone_id mustEqual 0
diagrams.size mustEqual 1
//h0
diagrams.head.pageNum mustEqual 14
diagrams.head.sheet.isDefined mustEqual false
diagrams.head.action mustEqual DiagramActionCode.ActionE
diagrams.head.stroke.isDefined mustEqual false
case _ =>
ko
}
@ -29,7 +46,7 @@ class BattleplanMessageTest extends Specification {
41490746,
"YetAnotherFailureAlt",
0,
BattleDiagram(14) ::
BattleDiagramAction(DiagramActionCode.ActionE) ::
Nil
)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector

View file

@ -179,7 +179,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.debug("Object: " + obj)
// LoadMapMessage 13714 in mossy .gcap
// XXX: hardcoded shit
sendResponse(PacketCoding.CreateGamePacket(0, LoadMapMessage("map13","home3",40100,25,true,3770441820L))) //VS Sanctuary
sendResponse(PacketCoding.CreateGamePacket(0, LoadMapMessage("map10","z10",40100,25,true,3770441820L))) //VS Sanctuary
sendResponse(PacketCoding.CreateGamePacket(0, ZonePopulationUpdateMessage(PlanetSideGUID(13), 414, 138, 0, 138, 0, 138, 0, 138, 0)))
sendResponse(PacketCoding.CreateGamePacket(0, objectHex))
@ -217,7 +217,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, SetCurrentAvatarMessage(guid,0,0)))
sendResponse(PacketCoding.CreateGamePacket(0, CreateShortcutMessage(guid, 1, 0, true, Shortcut.MEDKIT)))
sendResponse(PacketCoding.CreateGamePacket(0, ReplicationStreamMessage(5, Some(6), Vector(SquadListing(255))))) //clear squad list
sendRawResponse(hex"b3 3a197902 94 59006500740041006e006f0074006800650072004600610069006c0075007200650041006c007400 0000 01 e0")
sendRawResponse(hex"b3 3a197902 8c 4f0075007400730074006100620075006c006f0075007300 0a00202aba2b4aae8bd2aba334aae8dd2aca3b4ab28fd2aca414ab29152aca474ab292d2ada4d4ab69452ada534ab695d2ada594ab696d2ada5d4ab697d2ada614ab698d2ada654ab699d2ada694ab69ad2aea6d4aba9bd2aea714aba9cd2aea754aba9dd2aea794aba9ed")
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
clientKeepAlive = context.system.scheduler.schedule(0 seconds, 500 milliseconds, self, PokeClient())