final changes to the Stroke classes for this iteration of BattleplanMessage; comments and clarification; tests (made-up, admittedly, but they work)

This commit is contained in:
FateJH 2017-04-30 00:01:24 -04:00
parent ef1a869449
commit 0e81b21d11
2 changed files with 332 additions and 179 deletions

View file

@ -9,28 +9,28 @@ import shapeless.{::, HNil}
/**
* A `Codec` for the actions that each layer of the diagram performs.
* `Action1`, `Action2`, `Action5`, `Action6`, and `Action7` have additional `DiagramStroke` input data.
* `Style`, `Vertex`, `Action5`, `DrawString`, and `Action7` have additional `DiagramStroke` input data.
*/
object DiagramActionCode extends Enumeration {
type Type = Value
val Action0,
Action1,
Style,
Vertex,
Action3,
Action4,
Action5,
Action6,
DrawString,
Action7,
Action8,
Action9,
ActionA,
ActionB,
ActionC,
ActionD,
ActionE,
ActionF
= Value //TODO replace these with descriptive wording
ActionC, //clear?
ActionD, //opposite of clear?
StartDrawing,
StopDrawing
= Value //TODO replace all these with descriptive words
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint4L)
}
@ -41,46 +41,61 @@ object DiagramActionCode extends Enumeration {
sealed trait DiagramStroke
/**
* na
* @param unk1 na
* @param unk2 na
* Set style properties for the line segemnt(s) to be drawn.
* Color and thickness can not vary within a given line and will only apply to the subsequent line segments.
* Attempting to list a change in between coordinate points will invalidate that segment.
* @param thickness the line width in pixels;
* 0.0f - 16.0f;
* 3.0f is about normal and 0.0f is smaller than the map grid lines
* @param color the color of the line;
* 0 is gray (default);
* 1 is red;
* 2 is green;
* 3 is blue
*/
final case class StrokeOne(unk1 : Float,
unk2 : Int) extends DiagramStroke
final case class Style(thickness : Float,
color : Int) extends DiagramStroke
/**
* Mark coordinates on the tactical map.
* Indicate coordinates on the tactical map.
* Any adjacent sets of coordinates will be connected with a line segment.
* @param x the x-coordinate of this point
* @param y the y-coordinate of this point
*/
final case class StrokeTwo(x : Float,
y : Float) extends DiagramStroke
final case class Vertex(x : Float,
y : Float) extends DiagramStroke
/**
* na
* @param unk1 na
* @param unk2 na
* @param unk3 na
* @param unk4 na
* @param x the x-coordinate of this point
* @param y the y-coordinate of this point
* @param unk na;
* 1024.0f - 0.0f
*/
final case class StrokeFive(unk1 : Float,
unk2 : Float,
unk3 : Float,
unk4 : Int) extends DiagramStroke
final case class StrokeFive(x : Float,
y : Float,
unk : Float) extends DiagramStroke
/**
* na
* @param unk1 na
* @param unk2 na
* @param unk3 na
* @param unk4 na
* @param unk5 na
* Draw a string message on the tactical map.
* String messages have their own color designation and will not inherit line properties.
* @param x the x-coordinate marking the bottom center of this message's text
* @param y the y-coordinate marking the bottom center of this message's text
* @param color the color of the message;
* 0 is gray (default);
* 1 is red;
* 2 is green;
* 3 is blue
* @param channel the available "slots" in which to display messages on the map;
* a maximum of 16 channels/messages (0-15) are available per player;
* no two messages may inhabit the same channel
* @param message the text to display
*/
final case class StrokeSix(unk1 : Float,
unk2 : Float,
unk3 : Int,
unk4 : Int,
unk5 : String) extends DiagramStroke
final case class DrawString(x : Float,
y : Float,
color : Int,
channel : Int,
message : String) extends DiagramStroke
/**
* na
@ -89,21 +104,51 @@ final case class StrokeSix(unk1 : Float,
final case class StrokeSeven(unk : Int) extends DiagramStroke
/**
* na
* A particular instruction in the rendering of this battleplan's diagram entry.
* @param action the behavior of this stroke;
* a hint to the kind of stroke data stored, if at all, and how to use it or incorporate prior data
* @param stroke the data
* @param stroke the data;
* defaults to `None`
*/
final case class BattleDiagramAction(action : DiagramActionCode.Value,
stroke : Option[DiagramStroke] = None)
/**
* na
* Share drawn images and words on the tactical map among a group of players.<br>
* <br>
* Each packet usually contains a small portion of an image, herein called a "diagram."
* `BattleplanMessage` packets are accumulative towards a full diagram.
* Moreover, rather than the `player_name`, each diagram is associated on a client by the `char_id` field.
* Only squad leaders and platoon leaders can draw on the map and share with other players in their squad or platoon.<br>
* <br>
* To start drawing, a would-be artist must have all clients who will receive their diagrams acknowledge a `StartDrawing` action.
* The `char_id` with this `StartDrawing` will associate all diagrams submitted with the same `char_id`'s portfolio.
* Multiple portfolio definitions may exist on a client at a given time and each will manage their own diagrams.
* When a given portfolio submits a `StopDrawing` action that is received, the previous diagrams associated with it will be cleared.
* That `char_id` will no longer accept diagrams on that client.
* Other portfolios will continue to accept diagrams as initialized.
* When no portfolios are being accepted, the "Toggle -> Battleplan" button on that client's tactical map will be disabled.
* When there is at least one portfolio accepted, the "Battleplan" button will be functional and can be toggled.<br>
* <br>
* To construct line segments, chain `StrokeTwo` diagrams in the given packet entry.
* Each defined point will act like a successive vertex in a chain of segments.
* Any non-vertex entry in between entries, e.g., a change of line color, will break the chain of line segments.
* For example:<br>
* RED-A-B-C will construct red lines segments A-B and B-C.<br>
* RED-A-B-GREEN-C will only construct a red line segement A-B.<br>
* RED-A-B-GREEN-C-D will construct a red line segement A-B and a green line segment C-D.<br>
* (Default line color, if none is declared specifically, is gray.)<br>
* <br>
* To construct a message, define a point to act as the center baseline for the text.
* The message will be written above and outwards from that point.
* Messages do not carry properties over from line segments - they set their own color and do not have line thickness.
* Any single portfolio may have only fifteen messages written to the tactical map at a time.
* @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
* @param zone_id on which continent the battle plan will be overlaid;
* can identify as "no zone" 0 when performing instructions not specific to drawing
* @param diagrams a list of the itemized actions that will construct this plan or are used to modify the plan
*/
final case class BattleplanMessage(char_id : Long,
player_name : String,
@ -118,12 +163,12 @@ final case class BattleplanMessage(char_id : Long,
object BattleDiagramAction {
/**
* Create a `BattleDiagramAction` object containing `StrokeOne` data.
* @param unk1 na
* @param unk2 na
* @param thickness the line width in pixels
* @param color the color of the line
* @return a `BattleDiagramAction` object
*/
def stroke1(unk1 : Float, unk2 : Int) : BattleDiagramAction =
BattleDiagramAction(DiagramActionCode.Action1, Some(StrokeOne(unk1, unk2)))
def style(thickness : Float, color : Int) : BattleDiagramAction =
BattleDiagramAction(DiagramActionCode.Style, Some(Style(thickness, color)))
/**
* Create a `BattleDiagramAction` object containing `StrokeTwo` vertex data.
@ -132,30 +177,28 @@ object BattleDiagramAction {
* @return a `BattleDiagramAction` object
*/
def vertex(x : Float, y : Float) : BattleDiagramAction =
BattleDiagramAction(DiagramActionCode.Vertex, Some(StrokeTwo(x, y)))
BattleDiagramAction(DiagramActionCode.Vertex, Some(Vertex(x, y)))
/**
* Create a `BattleDiagramAction` object containing `StrokeFive` data.
* @param unk1 na
* @param unk2 na
* @param unk3 na
* @param unk4 na
* @param x the x-coordinate of this point
* @param y the y-coordinate of this point
* @param unk na
* @return a `BattleDiagramAction` object
*/
def stroke5(unk1 : Float, unk2 : Float, unk3 : Float, unk4 : Int) : BattleDiagramAction =
BattleDiagramAction(DiagramActionCode.Action5, Some(StrokeFive(unk1, unk2, unk3, unk4)))
def stroke5(x : Float, y : Float, unk : Float) : BattleDiagramAction =
BattleDiagramAction(DiagramActionCode.Action5, Some(StrokeFive(x, y, unk)))
/**
* Create a `BattleDiagramAction` object containing `StrokeSix` data.
* @param unk1 na
* @param unk2 na
* @param unk3 na
* @param unk4 na
* @param unk5 na
* @return a `BattleDiagramAction` object
* @param x the x-coordinate marking the bottom center of this message's text
* @param y the y-coordinate marking the bottom center of this message's text
* @param color the color of the message
* @param channel the available "slots" in which to display messages on the map
* @param message the text to display
*/
def stroke6(unk1 : Float, unk2 : Float, unk3 : Int, unk4 : Int, unk5 : String) : BattleDiagramAction =
BattleDiagramAction(DiagramActionCode.Action6, Some(StrokeSix(unk1, unk2, unk3, unk4, unk5)))
def drawString(x : Float, y : Float, color : Int, channel : Int, message : String) : BattleDiagramAction =
BattleDiagramAction(DiagramActionCode.DrawString, Some(DrawString(x, y, color, channel, message)))
/**
* Create a `BattleDiagramAction` object containing `StrokeSeven` data.
@ -171,58 +214,61 @@ object BattleplanMessage extends Marshallable[BattleplanMessage] {
/**
* 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` 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.
* This hidden object is arranged like a linked list.
* During the decoding process, it is converted into an accessible formal `List`.
* 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 shall be used.
* @param diagram the contained object that maintains the data
* @param next the next `BattleDiagramChain`, if any
* @see scala.collection.mutable.LinkedList&#60;E&#62;
* @see java.util.LinkedList&#60;E&#62;
*/
private final case class BattleDiagramChain(diagram : BattleDiagramAction,
next : Option[BattleDiagramChain])
/**
* Parse data into a `StrokeOne` object.
* Parse data into a `Style` object.
*/
private val plan1_codec : Codec[StrokeOne] = ( //size: 8; pad: +0
("unk1" | newcodecs.q_float(0.0, 16.0, 5)) ::
("unk2" | uintL(3))
).as[StrokeOne]
private val plan1_codec : Codec[Style] = ( //size: 8 (12)
("thickness" | newcodecs.q_float(16.0, 0.0, 5)) ::
("color" | uintL(3))
).as[Style]
/**
* Parse data into a `StrokeTwo` object.
* Parse data into a `Vertex` object.
*/
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[StrokeTwo]
private val plan2_codec : Codec[Vertex] = ( //size: 22 (26)
("x" | newcodecs.q_float(-4096.0, 12288.0, 11)) ::
("y" | newcodecs.q_float(-4096.0, 12288.0, 11))
).as[Vertex]
/**
* Parse data into a `StrokeFive` object.
*/
private val plan5_codec : Codec[StrokeFive] = ( //size: 44; pad: +4
private val plan5_codec : Codec[StrokeFive] = ( //size: 33 (37)
("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))
("unk3" | newcodecs.q_float(1024.0, 0.0, 11))
).as[StrokeFive]
/**
* Parse data into a `StrokeSix` object.
* @param pad the current padding for the `String` entry
* Parse data into a `DrawString` object.<br>
* If we are on a byte boundary upon starting this entry, our message is padded by `5u` (always).
* If we are not on a byte boundary, we must use our current offset and this size (`31u + 4u`) to calculate the padding value.
* @param padOffset the current padding value for the `String` entry
*/
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[StrokeSix]
private def plan6_codec(padOffset : Int) : Codec[DrawString] = ( //size: irrelevant, pad value resets
("x" | newcodecs.q_float(-4096.0, 12288.0, 11)) ::
("y" | newcodecs.q_float(-4096.0, 12288.0, 11)) ::
("color" | uintL(3)) ::
("font_size" | uintL(6)) ::
("message" | PacketHelpers.encodedWideStringAligned( if(padOffset % 8 == 0) { 5 } else { 8 - (padOffset + 35) % 8 } ))
).as[DrawString]
/**
* Parse data into a `StrokeSeven` object.
*/
private val plan7_codec : Codec[StrokeSeven] = ("unk" | uintL(6)).as[StrokeSeven] // size: 6; pad: +2
private val plan7_codec : Codec[StrokeSeven] = ("unk" | uintL(6)).as[StrokeSeven] // size: 6 (10)
/**
* Switch between different patterns to create a `BattleDiagramAction` for the following data.
@ -232,10 +278,10 @@ object BattleplanMessage extends Marshallable[BattleplanMessage] {
* @return a `BattleDiagramAction` object
*/
private def diagram_codec(plan : DiagramActionCode.Value, pad : Int) : Codec[BattleDiagramAction] = (
conditional(plan == DiagramActionCode.Action1, plan1_codec) ::
conditional(plan == DiagramActionCode.Style, plan1_codec) ::
conditional(plan == DiagramActionCode.Vertex, plan2_codec) ::
conditional(plan == DiagramActionCode.Action5, plan5_codec) ::
conditional(plan == DiagramActionCode.Action6, plan6_codec(pad)) ::
conditional(plan == DiagramActionCode.DrawString, plan6_codec(pad)) ::
conditional(plan == DiagramActionCode.Action7, plan7_codec)
).exmap[BattleDiagramAction] (
{
@ -261,17 +307,17 @@ object BattleplanMessage extends Marshallable[BattleplanMessage] {
Attempt.failure(Err(s"too many strokes for action $plan"))
},
{
case BattleDiagramAction(DiagramActionCode.Action1, Some(stroke)) =>
Attempt.successful(Some(stroke.asInstanceOf[StrokeOne]) :: None :: None :: None :: None :: HNil)
case BattleDiagramAction(DiagramActionCode.Style, Some(stroke)) =>
Attempt.successful(Some(stroke.asInstanceOf[Style]) :: None :: None :: None :: None :: HNil)
case BattleDiagramAction(DiagramActionCode.Vertex, Some(stroke)) =>
Attempt.successful(None :: Some(stroke.asInstanceOf[StrokeTwo]) :: None :: None :: None :: HNil)
Attempt.successful(None :: Some(stroke.asInstanceOf[Vertex]) :: None :: None :: None :: HNil)
case BattleDiagramAction(DiagramActionCode.Action5, Some(stroke)) =>
Attempt.successful(None :: None :: Some(stroke.asInstanceOf[StrokeFive]) :: None :: None :: HNil)
case BattleDiagramAction(DiagramActionCode.Action6, Some(stroke)) =>
Attempt.successful(None :: None :: None :: Some(stroke.asInstanceOf[StrokeSix]) :: None :: HNil)
case BattleDiagramAction(DiagramActionCode.DrawString, Some(stroke)) =>
Attempt.successful(None :: None :: None :: Some(stroke.asInstanceOf[DrawString]) :: None :: HNil)
case BattleDiagramAction(DiagramActionCode.Action7, Some(stroke)) =>
Attempt.successful(None :: None :: None :: None :: Some(stroke.asInstanceOf[StrokeSeven]) :: HNil)
@ -285,20 +331,20 @@ object BattleplanMessage extends Marshallable[BattleplanMessage] {
)
/**
* Parse what was originally an encoded `List` of elements as a linked list of elements.
* Maintain a `String` padding value that applies an appropriate offset value.
* Parse diagram instructions as a linked list.
* Maintain a `String` padding value that applies an appropriate offset value regardless of where in the elements it is required.
* @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
* @param padOffset 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 `BattleDiagramChain` segments
*/
private def parse_diagrams_codec(remaining : Int, pad : Int = 0) : Codec[BattleDiagramChain] = (
private def parse_diagrams_codec(remaining : Int, padOffset : Int = 0) : Codec[BattleDiagramChain] = (
DiagramActionCode.codec >>:~ { plan =>
("diagram" | diagram_codec(plan, pad)) ::
("diagram" | diagram_codec(plan, padOffset)) ::
conditional(remaining > 1,
"next" | parse_diagrams_codec(
remaining - 1,
pad + (if(plan == DiagramActionCode.Vertex || plan == DiagramActionCode.Action7) { 2 } else if(plan == DiagramActionCode.Action5) { 4 } else if(plan == DiagramActionCode.Action6) { -pad } else { 0 })
padOffset + (if(plan == DiagramActionCode.DrawString) { -padOffset } else if(plan == DiagramActionCode.Action5) { 37 } else if(plan == DiagramActionCode.Vertex) { 26 } else if(plan == DiagramActionCode.Style) { 12 } else if(plan == DiagramActionCode.Action7) { 10 } else { 4 })
)
)
}).exmap[BattleDiagramChain] (
@ -319,16 +365,18 @@ object BattleplanMessage extends Marshallable[BattleplanMessage] {
* @param list a `List` of extracted `BattleDiagrams`;
* technically, the output
*/
private def rollDiagramLayers(element : BattleDiagramChain, list : ListBuffer[BattleDiagramAction]) : Unit = {
list += element.diagram
if(element.next.isDefined)
rollDiagramLayers(element.next.get, list) //tail call optimization
private def rollDiagramLayers(element : Option[BattleDiagramChain], list : ListBuffer[BattleDiagramAction]) : Unit = {
if(element.isEmpty)
return
list += element.get.diagram
rollDiagramLayers(element.get.next, list) //tail call optimization
}
/**
* 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 `BattleDiagramChain` objects;
* defaults to `None`, so does not need to be defined during the initial pass;
* technically, the output
* @return a linked list of `BattleDiagramChain` objects
*/
@ -351,7 +399,7 @@ object BattleplanMessage extends Marshallable[BattleplanMessage] {
case char_id :: player :: zone_id :: _ :: diagramLayers :: HNil =>
val list : ListBuffer[BattleDiagramAction] = new ListBuffer()
if(diagramLayers.isDefined)
rollDiagramLayers(diagramLayers.get, list)
rollDiagramLayers(diagramLayers, list)
Attempt.successful(BattleplanMessage(char_id, player, zone_id, list.toList))
},
{

View file

@ -7,26 +7,44 @@ import net.psforever.packet.game._
import scodec.bits._
class BattleplanMessageTest extends Specification {
val string = hex"b3 3a197902 94 59006500740041006e006f0074006800650072004600610069006c0075007200650041006c007400 0000 01 e0"
val string_line = hex"b3 85647702 8c4f0075007400730074006100620075006c006f0075007300 0a00 20 2aba2b4aae8bd2aba334aae8dd2aca3b4ab28fd2aca414ab29152aca474ab292d2ada4d4ab69452ada534ab695d2ada594ab696d2ada5d4ab697d2ada614ab698d2ada654ab699d2ada694ab69ad2aea6d4aba9bd2aea714aba9cd2aea754aba9dd2aea794aba9ed"
val string_start = hex"b3 3a197902 94 59006500740041006e006f0074006800650072004600610069006c0075007200650041006c007400 0000 01 e0"
val string_stop = hex"b3 3a197902 94 59006500740041006e006f0074006800650072004600610069006c0075007200650041006c007400 0000 01 f0"
val string_line = hex"b3 85647702 8c 4f0075007400730074006100620075006c006f0075007300 0a00 20 2aba2b4aae8bd2aba334aae8dd2aca3b4ab28fd2aca414ab29152aca474ab292d2ada4d4ab69452ada534ab695d2ada594ab696d2ada5d4ab697d2ada614ab698d2ada654ab699d2ada694ab69ad2aea6d4aba9bd2aea714aba9cd2aea754aba9dd2aea794aba9ed"
val string_style = hex"b3856477028c4f0075007400730074006100620075006c006f00750073000a00031d22aba2f4aae8cd"
val string_message = hex"b3 85647702 8c 4f0075007400730074006100620075006c006f0075007300 0a00 01 6aba2b5011c0480065006c006c006f00200041007500720061007800690073002100"
//0xb3856477028c4f0075007400730074006100620075006c006f00750073000a000130
"decode (short)" in {
PacketCoding.DecodePacket(string).require match {
"decode (start)" in {
PacketCoding.DecodePacket(string_start).require match {
case BattleplanMessage(char_id, player_name, zone_id, diagrams) =>
char_id mustEqual 41490746
player_name mustEqual "YetAnotherFailureAlt"
zone_id mustEqual PlanetSideGUID(0)
diagrams.size mustEqual 1
//0
diagrams.head.action mustEqual DiagramActionCode.ActionE
diagrams.head.action mustEqual DiagramActionCode.StartDrawing
diagrams.head.stroke.isDefined mustEqual false
case _ =>
ko
}
}
"decode (line)" in {
"decode (end)" in {
PacketCoding.DecodePacket(string_stop).require match {
case BattleplanMessage(char_id, player_name, zone_id, diagrams) =>
char_id mustEqual 41490746
player_name mustEqual "YetAnotherFailureAlt"
zone_id mustEqual PlanetSideGUID(0)
diagrams.size mustEqual 1
//0
diagrams.head.action mustEqual DiagramActionCode.StopDrawing
diagrams.head.stroke.isDefined mustEqual false
case _ =>
ko
}
}
"decode (stop)" in {
PacketCoding.DecodePacket(string_line).require match {
case BattleplanMessage(char_id, player_name, zone_id, diagrams) =>
char_id mustEqual 41378949
@ -36,149 +54,209 @@ class BattleplanMessageTest extends Specification {
//0
diagrams.head.action mustEqual DiagramActionCode.Vertex
diagrams.head.stroke.isDefined mustEqual true
diagrams.head.stroke.get.isInstanceOf[StrokeTwo] mustEqual true
diagrams.head.stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7512.0f
diagrams.head.stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6312.0f
diagrams.head.stroke.get.isInstanceOf[Vertex] mustEqual true
diagrams.head.stroke.get.asInstanceOf[Vertex].x mustEqual 7512.0f
diagrams.head.stroke.get.asInstanceOf[Vertex].y mustEqual 6312.0f
//1
diagrams(1).action mustEqual DiagramActionCode.Vertex
diagrams(1).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7512.0f
diagrams(1).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6328.0f
diagrams(1).stroke.get.asInstanceOf[Vertex].x mustEqual 7512.0f
diagrams(1).stroke.get.asInstanceOf[Vertex].y mustEqual 6328.0f
//2
diagrams(2).action mustEqual DiagramActionCode.Vertex
diagrams(2).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7512.0f
diagrams(2).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6344.0f
diagrams(2).stroke.get.asInstanceOf[Vertex].x mustEqual 7512.0f
diagrams(2).stroke.get.asInstanceOf[Vertex].y mustEqual 6344.0f
//3
diagrams(3).action mustEqual DiagramActionCode.Vertex
diagrams(3).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7512.0f
diagrams(3).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6360.0f
diagrams(3).stroke.get.asInstanceOf[Vertex].x mustEqual 7512.0f
diagrams(3).stroke.get.asInstanceOf[Vertex].y mustEqual 6360.0f
//4
diagrams(4).action mustEqual DiagramActionCode.Vertex
diagrams(4).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7520.0f
diagrams(4).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6376.0f
diagrams(4).stroke.get.asInstanceOf[Vertex].x mustEqual 7520.0f
diagrams(4).stroke.get.asInstanceOf[Vertex].y mustEqual 6376.0f
//5
diagrams(5).action mustEqual DiagramActionCode.Vertex
diagrams(5).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7520.0f
diagrams(5).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6392.0f
diagrams(5).stroke.get.asInstanceOf[Vertex].x mustEqual 7520.0f
diagrams(5).stroke.get.asInstanceOf[Vertex].y mustEqual 6392.0f
//6
diagrams(6).action mustEqual DiagramActionCode.Vertex
diagrams(6).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7520.0f
diagrams(6).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6400.0f
diagrams(6).stroke.get.asInstanceOf[Vertex].x mustEqual 7520.0f
diagrams(6).stroke.get.asInstanceOf[Vertex].y mustEqual 6400.0f
//7
diagrams(7).action mustEqual DiagramActionCode.Vertex
diagrams(7).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7520.0f
diagrams(7).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6416.0f
diagrams(7).stroke.get.asInstanceOf[Vertex].x mustEqual 7520.0f
diagrams(7).stroke.get.asInstanceOf[Vertex].y mustEqual 6416.0f
//8
diagrams(8).action mustEqual DiagramActionCode.Vertex
diagrams(8).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7520.0f
diagrams(8).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6424.0f
diagrams(8).stroke.get.asInstanceOf[Vertex].x mustEqual 7520.0f
diagrams(8).stroke.get.asInstanceOf[Vertex].y mustEqual 6424.0f
//9
diagrams(9).action mustEqual DiagramActionCode.Vertex
diagrams(9).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7520.0f
diagrams(9).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6440.0f
diagrams(9).stroke.get.asInstanceOf[Vertex].x mustEqual 7520.0f
diagrams(9).stroke.get.asInstanceOf[Vertex].y mustEqual 6440.0f
//10
diagrams(10).action mustEqual DiagramActionCode.Vertex
diagrams(10).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7528.0f
diagrams(10).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6448.0f
diagrams(10).stroke.get.asInstanceOf[Vertex].x mustEqual 7528.0f
diagrams(10).stroke.get.asInstanceOf[Vertex].y mustEqual 6448.0f
//11
diagrams(11).action mustEqual DiagramActionCode.Vertex
diagrams(11).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7528.0f
diagrams(11).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6464.0f
diagrams(11).stroke.get.asInstanceOf[Vertex].x mustEqual 7528.0f
diagrams(11).stroke.get.asInstanceOf[Vertex].y mustEqual 6464.0f
//12
diagrams(12).action mustEqual DiagramActionCode.Vertex
diagrams(12).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7528.0f
diagrams(12).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6472.0f
diagrams(12).stroke.get.asInstanceOf[Vertex].x mustEqual 7528.0f
diagrams(12).stroke.get.asInstanceOf[Vertex].y mustEqual 6472.0f
//13
diagrams(13).action mustEqual DiagramActionCode.Vertex
diagrams(13).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7528.0f
diagrams(13).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6488.0f
diagrams(13).stroke.get.asInstanceOf[Vertex].x mustEqual 7528.0f
diagrams(13).stroke.get.asInstanceOf[Vertex].y mustEqual 6488.0f
//14
diagrams(14).action mustEqual DiagramActionCode.Vertex
diagrams(14).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7528.0f
diagrams(14).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6496.0f
diagrams(14).stroke.get.asInstanceOf[Vertex].x mustEqual 7528.0f
diagrams(14).stroke.get.asInstanceOf[Vertex].y mustEqual 6496.0f
//15
diagrams(15).action mustEqual DiagramActionCode.Vertex
diagrams(15).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7528.0f
diagrams(15).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6504.0f
diagrams(15).stroke.get.asInstanceOf[Vertex].x mustEqual 7528.0f
diagrams(15).stroke.get.asInstanceOf[Vertex].y mustEqual 6504.0f
//16
diagrams(16).action mustEqual DiagramActionCode.Vertex
diagrams(16).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7528.0f
diagrams(16).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6512.0f
diagrams(16).stroke.get.asInstanceOf[Vertex].x mustEqual 7528.0f
diagrams(16).stroke.get.asInstanceOf[Vertex].y mustEqual 6512.0f
//17
diagrams(17).action mustEqual DiagramActionCode.Vertex
diagrams(17).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7528.0f
diagrams(17).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6520.0f
diagrams(17).stroke.get.asInstanceOf[Vertex].x mustEqual 7528.0f
diagrams(17).stroke.get.asInstanceOf[Vertex].y mustEqual 6520.0f
//18
diagrams(18).action mustEqual DiagramActionCode.Vertex
diagrams(18).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7528.0f
diagrams(18).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6528.0f
diagrams(18).stroke.get.asInstanceOf[Vertex].x mustEqual 7528.0f
diagrams(18).stroke.get.asInstanceOf[Vertex].y mustEqual 6528.0f
//19
diagrams(19).action mustEqual DiagramActionCode.Vertex
diagrams(19).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7528.0f
diagrams(19).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6536.0f
diagrams(19).stroke.get.asInstanceOf[Vertex].x mustEqual 7528.0f
diagrams(19).stroke.get.asInstanceOf[Vertex].y mustEqual 6536.0f
//20
diagrams(20).action mustEqual DiagramActionCode.Vertex
diagrams(20).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7528.0f
diagrams(20).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6544.0f
diagrams(20).stroke.get.asInstanceOf[Vertex].x mustEqual 7528.0f
diagrams(20).stroke.get.asInstanceOf[Vertex].y mustEqual 6544.0f
//21
diagrams(21).action mustEqual DiagramActionCode.Vertex
diagrams(21).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7528.0f
diagrams(21).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6552.0f
diagrams(21).stroke.get.asInstanceOf[Vertex].x mustEqual 7528.0f
diagrams(21).stroke.get.asInstanceOf[Vertex].y mustEqual 6552.0f
//22
diagrams(22).action mustEqual DiagramActionCode.Vertex
diagrams(22).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7528.0f
diagrams(22).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6560.0f
diagrams(22).stroke.get.asInstanceOf[Vertex].x mustEqual 7528.0f
diagrams(22).stroke.get.asInstanceOf[Vertex].y mustEqual 6560.0f
//23
diagrams(23).action mustEqual DiagramActionCode.Vertex
diagrams(23).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7528.0f
diagrams(23).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6568.0f
diagrams(23).stroke.get.asInstanceOf[Vertex].x mustEqual 7528.0f
diagrams(23).stroke.get.asInstanceOf[Vertex].y mustEqual 6568.0f
//24
diagrams(24).action mustEqual DiagramActionCode.Vertex
diagrams(24).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7536.0f
diagrams(24).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6576.0f
diagrams(24).stroke.get.asInstanceOf[Vertex].x mustEqual 7536.0f
diagrams(24).stroke.get.asInstanceOf[Vertex].y mustEqual 6576.0f
//25
diagrams(25).action mustEqual DiagramActionCode.Vertex
diagrams(25).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7536.0f
diagrams(25).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6584.0f
diagrams(25).stroke.get.asInstanceOf[Vertex].x mustEqual 7536.0f
diagrams(25).stroke.get.asInstanceOf[Vertex].y mustEqual 6584.0f
//26
diagrams(26).action mustEqual DiagramActionCode.Vertex
diagrams(26).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7536.0f
diagrams(26).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6592.0f
diagrams(26).stroke.get.asInstanceOf[Vertex].x mustEqual 7536.0f
diagrams(26).stroke.get.asInstanceOf[Vertex].y mustEqual 6592.0f
//27
diagrams(27).action mustEqual DiagramActionCode.Vertex
diagrams(27).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7536.0f
diagrams(27).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6600.0f
diagrams(27).stroke.get.asInstanceOf[Vertex].x mustEqual 7536.0f
diagrams(27).stroke.get.asInstanceOf[Vertex].y mustEqual 6600.0f
//28
diagrams(28).action mustEqual DiagramActionCode.Vertex
diagrams(28).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7536.0f
diagrams(28).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6608.0f
diagrams(28).stroke.get.asInstanceOf[Vertex].x mustEqual 7536.0f
diagrams(28).stroke.get.asInstanceOf[Vertex].y mustEqual 6608.0f
//29
diagrams(29).action mustEqual DiagramActionCode.Vertex
diagrams(29).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7536.0f
diagrams(29).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6616.0f
diagrams(29).stroke.get.asInstanceOf[Vertex].x mustEqual 7536.0f
diagrams(29).stroke.get.asInstanceOf[Vertex].y mustEqual 6616.0f
//30
diagrams(30).action mustEqual DiagramActionCode.Vertex
diagrams(30).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7536.0f
diagrams(30).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6624.0f
diagrams(30).stroke.get.asInstanceOf[Vertex].x mustEqual 7536.0f
diagrams(30).stroke.get.asInstanceOf[Vertex].y mustEqual 6624.0f
//31
diagrams(31).action mustEqual DiagramActionCode.Vertex
diagrams(31).stroke.get.asInstanceOf[StrokeTwo].x mustEqual 7536.0f
diagrams(31).stroke.get.asInstanceOf[StrokeTwo].y mustEqual 6632.0f
diagrams(31).stroke.get.asInstanceOf[Vertex].x mustEqual 7536.0f
diagrams(31).stroke.get.asInstanceOf[Vertex].y mustEqual 6632.0f
case _ =>
ko
}
}
"encode (short)" in {
"decode (style)" in {
PacketCoding.DecodePacket(string_style).require match {
case BattleplanMessage(char_id, player_name, zone_id, diagrams) =>
char_id mustEqual 41378949
player_name mustEqual "Outstabulous"
zone_id mustEqual PlanetSideGUID(10)
diagrams.size mustEqual 3
//0
diagrams.head.action mustEqual DiagramActionCode.Style
diagrams.head.stroke.isDefined mustEqual true
diagrams.head.stroke.get.isInstanceOf[Style] mustEqual true
diagrams.head.stroke.get.asInstanceOf[Style].thickness mustEqual 3.0f
diagrams.head.stroke.get.asInstanceOf[Style].color mustEqual 2
//1
diagrams(1).action mustEqual DiagramActionCode.Vertex
diagrams(1).stroke.get.asInstanceOf[Vertex].x mustEqual 7512.0f
diagrams(1).stroke.get.asInstanceOf[Vertex].y mustEqual 6328.0f
//2
diagrams(2).action mustEqual DiagramActionCode.Vertex
diagrams(2).stroke.get.asInstanceOf[Vertex].x mustEqual 7512.0f
diagrams(2).stroke.get.asInstanceOf[Vertex].y mustEqual 6344.0f
case _ =>
ko
}
}
"decode (message)" in {
PacketCoding.DecodePacket(string_message).require match {
case BattleplanMessage(char_id, player_name, zone_id, diagrams) =>
char_id mustEqual 41378949
player_name mustEqual "Outstabulous"
zone_id mustEqual PlanetSideGUID(10)
diagrams.size mustEqual 1
//0
diagrams.head.action mustEqual DiagramActionCode.DrawString
diagrams.head.stroke.isDefined mustEqual true
diagrams.head.stroke.get.isInstanceOf[DrawString] mustEqual true
diagrams.head.stroke.get.asInstanceOf[DrawString].x mustEqual 7512.0f
diagrams.head.stroke.get.asInstanceOf[DrawString].y mustEqual 6312.0f
diagrams.head.stroke.get.asInstanceOf[DrawString].color mustEqual 2
diagrams.head.stroke.get.asInstanceOf[DrawString].channel mustEqual 0
diagrams.head.stroke.get.asInstanceOf[DrawString].message mustEqual "Hello Auraxis!"
case _ =>
ko
}
}
"encode (start)" in {
val msg = BattleplanMessage(
41490746,
"YetAnotherFailureAlt",
PlanetSideGUID(0),
BattleDiagramAction(DiagramActionCode.ActionE) ::
BattleDiagramAction(DiagramActionCode.StartDrawing) ::
Nil
)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
pkt mustEqual string_start
}
"encode (stop)" in {
val msg = BattleplanMessage(
41490746,
"YetAnotherFailureAlt",
PlanetSideGUID(0),
BattleDiagramAction(DiagramActionCode.StopDrawing) ::
Nil
)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_stop
}
"encode (line)" in {
@ -224,4 +302,31 @@ class BattleplanMessageTest extends Specification {
pkt mustEqual string_line
}
"encode (style)" in {
val msg = BattleplanMessage(
41378949,
"Outstabulous",
PlanetSideGUID(10),
BattleDiagramAction.style(3.0f, 2) ::
BattleDiagramAction.vertex(7512.0f, 6328.0f) ::
BattleDiagramAction.vertex(7512.0f, 6344.0f) ::
Nil
)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_style
}
"encode (message)" in {
val msg = BattleplanMessage(
41378949,
"Outstabulous",
PlanetSideGUID(10),
BattleDiagramAction.drawString(7512.0f, 6312.0f, 2, 0, "Hello Auraxis!") :: Nil
)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_message
}
}