diff --git a/common/src/main/scala/net/psforever/packet/game/BattleplanMessage.scala b/common/src/main/scala/net/psforever/packet/game/BattleplanMessage.scala index 277eb103..fbcbc4da 100644 --- a/common/src/main/scala/net/psforever/packet/game/BattleplanMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/BattleplanMessage.scala @@ -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.
+ * An intermediary object intended to temporarily store `BattleDiagramAction` objects.
*
* 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) } ) } diff --git a/common/src/test/scala/game/BattleplanMessageTest.scala b/common/src/test/scala/game/BattleplanMessageTest.scala index 2eb171bd..860ee0dd 100644 --- a/common/src/test/scala/game/BattleplanMessageTest.scala +++ b/common/src/test/scala/game/BattleplanMessageTest.scala @@ -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 diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 56b00e01..cf620c3d 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -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())