diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala
index 094980fc7..10a332b52 100644
--- a/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/AvatarConverter.scala
@@ -3,47 +3,52 @@ package net.psforever.objects.definition.converter
import net.psforever.objects.{EquipmentSlot, Player}
import net.psforever.objects.equipment.Equipment
-import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, Cosmetics, DetailedCharacterData, DrawnSlot, ImplantEffects, ImplantEntry, InternalSlot, InventoryData, PlacementData, RibbonBars, UniformStyle}
+import net.psforever.packet.game.objectcreate._
import net.psforever.types.{GrenadeState, ImplantType}
import scala.annotation.tailrec
import scala.util.{Success, Try}
class AvatarConverter extends ObjectCreateConverter[Player]() {
- override def ConstructorData(obj : Player) : Try[CharacterData] = {
+ override def ConstructorData(obj : Player) : Try[PlayerData] = {
val MaxArmor = obj.MaxArmor
- Success(
- CharacterData(
+ Success(
+ PlayerData.apply(
+ PlacementData(obj.Position, obj.Orientation, obj.Velocity),
MakeAppearanceData(obj),
- 255 * obj.Health / obj.MaxHealth, //TODO not precise
- if(MaxArmor == 0) { 0 } else { 255 * obj.Armor / MaxArmor }, //TODO not precise
- DressBattleRank(obj),
- DressCommandRank(obj),
- recursiveMakeImplantEffects(obj.Implants.iterator),
- MakeCosmetics(obj.BEP),
+ CharacterData(
+ 255 * obj.Health / obj.MaxHealth, //TODO not precise
+ if(MaxArmor == 0) { 0 } else { 255 * obj.Armor / MaxArmor }, //TODO not precise
+ DressBattleRank(obj),
+ DressCommandRank(obj),
+ recursiveMakeImplantEffects(obj.Implants.iterator),
+ MakeCosmetics(obj.BEP)
+ ),
InventoryData(MakeHolsters(obj, BuildEquipment).sortBy(_.parentSlot)), //TODO is sorting necessary?
GetDrawnSlot(obj)
)
)
- //TODO tidy this mess up
}
- override def DetailedConstructorData(obj : Player) : Try[DetailedCharacterData] = {
+ override def DetailedConstructorData(obj : Player) : Try[DetailedPlayerData] = {
Success(
- DetailedCharacterData(
+ DetailedPlayerData.apply(
+ PlacementData(obj.Position, obj.Orientation, obj.Velocity),
MakeAppearanceData(obj),
- obj.BEP,
- obj.CEP,
- obj.MaxHealth,
- obj.Health,
- obj.Armor,
- obj.MaxStamina,
- obj.Stamina,
- obj.Certifications.toList.sortBy(_.id), //TODO is sorting necessary?
- MakeImplantEntries(obj),
- List.empty[String], //TODO fte list
- List.empty[String], //TODO tutorial list
- MakeCosmetics(obj.BEP),
+ DetailedCharacterData(
+ obj.BEP,
+ obj.CEP,
+ obj.MaxHealth,
+ obj.Health,
+ obj.Armor,
+ obj.MaxStamina,
+ obj.Stamina,
+ obj.Certifications.toList.sortBy(_.id), //TODO is sorting necessary?
+ MakeImplantEntries(obj),
+ List.empty[String], //TODO fte list
+ List.empty[String], //TODO tutorial list
+ MakeCosmetics(obj.BEP)
+ ),
InventoryData((MakeHolsters(obj, BuildDetailedEquipment) ++ MakeFifthSlot(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)),
GetDrawnSlot(obj)
)
@@ -55,9 +60,8 @@ class AvatarConverter extends ObjectCreateConverter[Player]() {
* @param obj the `Player` game object
* @return the resulting `CharacterAppearanceData`
*/
- private def MakeAppearanceData(obj : Player) : CharacterAppearanceData = {
+ private def MakeAppearanceData(obj : Player) : (Int)=>CharacterAppearanceData = {
CharacterAppearanceData(
- PlacementData(obj.Position, obj.Orientation, obj.Velocity),
BasicCharacterData(obj.Name, obj.Faction, obj.Sex, obj.Head, obj.Voice),
0,
false,
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/CharacterSelectConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/CharacterSelectConverter.scala
index 293c71ced..bb00d7dc3 100644
--- a/common/src/main/scala/net/psforever/objects/definition/converter/CharacterSelectConverter.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/CharacterSelectConverter.scala
@@ -3,7 +3,7 @@ package net.psforever.objects.definition.converter
import net.psforever.objects.{EquipmentSlot, Player}
import net.psforever.objects.equipment.Equipment
-import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, DetailedCharacterData, ImplantEntry, InternalSlot, InventoryData, PlacementData, RibbonBars}
+import net.psforever.packet.game.objectcreate._
import net.psforever.types.{GrenadeState, ImplantType}
import scala.annotation.tailrec
@@ -15,19 +15,22 @@ import scala.util.{Failure, Success, Try}
* Details that would not be apparent on that screen such as implants or certifications are ignored.
*/
class CharacterSelectConverter extends AvatarConverter {
- override def ConstructorData(obj : Player) : Try[CharacterData] = Failure(new Exception("CharacterSelectConverter should not be used to generate CharacterData"))
+ override def ConstructorData(obj : Player) : Try[PlayerData] = Failure(new Exception("CharacterSelectConverter should not be used to generate CharacterData"))
- override def DetailedConstructorData(obj : Player) : Try[DetailedCharacterData] = {
+ override def DetailedConstructorData(obj : Player) : Try[DetailedPlayerData] = {
Success(
- DetailedCharacterData(
+ DetailedPlayerData.apply(
+ PlacementData(0, 0, 0),
MakeAppearanceData(obj),
- obj.BEP,
- obj.CEP,
- 1, 1, 0, 1, 1,
- Nil,
- MakeImplantEntries(obj), //necessary for correct stream length
- Nil, Nil,
- MakeCosmetics(obj.BEP),
+ DetailedCharacterData(
+ obj.BEP,
+ obj.CEP,
+ 1, 1, 0, 1, 1,
+ Nil,
+ MakeImplantEntries(obj), //necessary for correct stream length
+ Nil, Nil,
+ MakeCosmetics(obj.BEP)
+ ),
InventoryData(recursiveMakeHolsters(obj.Holsters().iterator)),
GetDrawnSlot(obj)
)
@@ -40,9 +43,8 @@ class CharacterSelectConverter extends AvatarConverter {
* @see `AvatarConverter.MakeAppearanceData`
* @return the resulting `CharacterAppearanceData`
*/
- private def MakeAppearanceData(obj : Player) : CharacterAppearanceData = {
+ private def MakeAppearanceData(obj : Player) : (Int)=>CharacterAppearanceData = {
CharacterAppearanceData(
- PlacementData(0f, 0f, 0f),
BasicCharacterData(obj.Name, obj.Faction, obj.Sex, obj.Head, 1),
0,
false,
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/CorpseConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/CorpseConverter.scala
index 68b7df90d..b32a0a822 100644
--- a/common/src/main/scala/net/psforever/objects/definition/converter/CorpseConverter.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/CorpseConverter.scala
@@ -3,23 +3,26 @@ package net.psforever.objects.definition.converter
import net.psforever.objects.{EquipmentSlot, Player}
import net.psforever.objects.equipment.Equipment
-import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, DetailedCharacterData, DrawnSlot, InternalSlot, InventoryData, PlacementData, RibbonBars}
+import net.psforever.packet.game.objectcreate._
import net.psforever.types.{CharacterGender, GrenadeState, Vector3}
import scala.annotation.tailrec
import scala.util.{Failure, Success, Try}
class CorpseConverter extends AvatarConverter {
- override def ConstructorData(obj : Player) : Try[CharacterData] =
+ override def ConstructorData(obj : Player) : Try[PlayerData] =
Failure(new Exception("CorpseConverter should not be used to generate CharacterData"))
- override def DetailedConstructorData(obj : Player) : Try[DetailedCharacterData] = {
+ override def DetailedConstructorData(obj : Player) : Try[DetailedPlayerData] = {
Success(
- DetailedCharacterData(
+ DetailedPlayerData.apply(
+ PlacementData(obj.Position, Vector3(0,0, obj.Orientation.z)),
MakeAppearanceData(obj),
- 0, 0, 0, 0, 0, 0, 0,
- Nil, Nil, Nil, Nil,
- None,
+ DetailedCharacterData(
+ 0, 0, 0, 0, 0, 0, 0,
+ Nil, Nil, Nil, Nil,
+ None
+ ),
InventoryData((MakeHolsters(obj) ++ MakeInventory(obj)).sortBy(_.parentSlot)),
DrawnSlot.None
)
@@ -31,9 +34,8 @@ class CorpseConverter extends AvatarConverter {
* @param obj the `Player` game object
* @return the resulting `CharacterAppearanceData`
*/
- private def MakeAppearanceData(obj : Player) : CharacterAppearanceData = {
+ private def MakeAppearanceData(obj : Player) : (Int)=>CharacterAppearanceData = {
CharacterAppearanceData(
- PlacementData(obj.Position, Vector3(0,0, obj.Orientation.z)),
BasicCharacterData(obj.Name, obj.Faction, CharacterGender.Male, 0, 0),
0,
false,
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterAppearanceData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterAppearanceData.scala
index 92dd6e6b1..581b81a22 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterAppearanceData.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterAppearanceData.scala
@@ -23,6 +23,8 @@ import shapeless.{::, HNil}
* `5 - male_5 female_5`
* `6 - female_1 no voice`
* `7 - female_2 no voice`
+ * @see `PlanetSideEmpire`
+ * `CharacaterGender`
* @param name the unique name of the avatar;
* minimum of two characters
* @param faction the empire to which the avatar belongs
@@ -30,8 +32,6 @@ import shapeless.{::, HNil}
* @param head the avatar's face and hair;
* by row and column on the character creation screen, the high nibble is the row and the low nibble is the column
* @param voice the avatar's voice selection
- * @see `PlanetSideEmpire`
- * @see `CharacaterGender`
*/
final case class BasicCharacterData(name : String,
faction : PlanetSideEmpire.Value,
@@ -60,8 +60,12 @@ final case class BasicCharacterData(name : String,
*
* Exploration:
* How do I crouch?
- * @param pos the position of the character in the world environment (in three coordinates)
- * @param basic_appearance the player's cardinal appearance settings
+ * @see `CharacterData`
+ * `DetailedCharacterData`
+ * `ExoSuitType`
+ * `GrenadeState`
+ * `RibbonBars`
+ * @param app the player's cardinal appearance settings
* @param voice2 na;
* affects the frequency by which the character's voice is heard (somehow);
* commonly 3 for best results
@@ -86,16 +90,8 @@ final case class BasicCharacterData(name : String,
* @param charging_pose animation pose for both charging modules and BFR imprinting
* @param on_zipline player's model is changed into a faction-color ball of energy, as if on a zip line
* @param ribbons the four merit commendation ribbon medals
- * @see `CharacterData`
- * @see `DetailedCharacterData`
- * @see `PlacementData`
- * @see `ExoSuitType`
- * @see `GrenadeState`
- * @see `RibbonBars`
- * @see `http://wiki.planetsidesyndicate.com/index.php?title=Outfit_Logo` for a list of outfit decals
*/
-final case class CharacterAppearanceData(pos : PlacementData,
- basic_appearance : BasicCharacterData,
+final case class CharacterAppearanceData(app : BasicCharacterData,
voice2 : Int,
black_ops : Boolean,
jammered : Boolean,
@@ -110,22 +106,29 @@ final case class CharacterAppearanceData(pos : PlacementData,
is_cloaking : Boolean,
charging_pose : Boolean,
on_zipline : Boolean,
- ribbons : RibbonBars) extends StreamBitSize {
+ ribbons : RibbonBars)
+ (name_padding : Int) extends StreamBitSize {
override def bitsize : Long = {
//factor guard bool values into the base size, not its corresponding optional field
- val placementSize : Long = pos.bitsize
- val nameStringSize : Long = StreamBitSize.stringBitSize(basic_appearance.name, 16) + CharacterAppearanceData.namePadding(pos.vel)
+ val nameStringSize : Long = StreamBitSize.stringBitSize(app.name, 16) + CharacterAppearanceData.namePaddingRule(name_padding)
val outfitStringSize : Long = StreamBitSize.stringBitSize(outfit_name, 16) + CharacterAppearanceData.outfitNamePadding
val altModelSize = CharacterAppearanceData.altModelBit(this).getOrElse(0)
- 335L + placementSize + nameStringSize + outfitStringSize + altModelSize
+ 335L + nameStringSize + outfitStringSize + altModelSize
}
+
+
+ /**
+ * When a player is released-dead or attached to a zipline, their basic infantry model is replaced with a different one.
+ * @return the length of the variable field that exists when using alternate models
+ */
+ def altModelBit : Option[Int] = CharacterAppearanceData.altModelBit(this)
}
object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
/**
* When a player is released-dead or attached to a zipline, their basic infantry model is replaced with a different one.
- * In the former casde, a backpack.
+ * In the former case, a backpack.
* In the latter case, a ball of colored energy.
* In this state, the length of the stream of data is modified.
* @param app the appearance
@@ -143,14 +146,13 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
* The padding will always be a number 0-7.
* @return the pad length in bits
*/
- def namePadding(move : Option[_]) : Int = {
- if(move.isDefined) {
- 2
+ def namePaddingRule(pad : Int) : Int =
+ if(pad == 0) {
+ 1 //normal alignment padding for the string
}
else {
- 4
+ pad //custom padding value
}
- }
/**
* Get the padding of the outfit's name.
@@ -161,77 +163,78 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
6
}
- implicit val codec : Codec[CharacterAppearanceData] = (
- ("pos" | PlacementData.codec) >>:~ { pos =>
- ("faction" | PlanetSideEmpire.codec) ::
- ("black_ops" | bool) ::
- (("alt_model" | bool) >>:~ { alt_model => //modifies stream format (to display alternate player models)
+ def codec(name_padding : Int) : Codec[CharacterAppearanceData] = (
+ ("faction" | PlanetSideEmpire.codec) ::
+ ("black_ops" | bool) ::
+ (("alt_model" | bool) >>:~ { alt_model => //modifies stream format (to display alternate player models)
+ ignore(1) :: //unknown
+ ("jammered" | bool) ::
+ bool :: //crashes client
+ uint(16) :: //unknown, but usually 0
+ ("name" | PacketHelpers.encodedWideStringAligned( namePaddingRule(name_padding) )) ::
+ ("exosuit" | ExoSuitType.codec) ::
+ ignore(2) :: //unknown
+ ("sex" | CharacterGender.codec) ::
+ ("head" | uint8L) ::
+ ("voice" | uint(3)) ::
+ ("voice2" | uint2L) ::
+ ignore(78) :: //unknown
+ uint16L :: //usually either 0 or 65535
+ uint32L :: //for outfit_name (below) to be visible in-game, this value should be non-zero
+ ("outfit_name" | PacketHelpers.encodedWideStringAligned( outfitNamePadding )) ::
+ ("outfit_logo" | uint8L) ::
ignore(1) :: //unknown
- ("jammered" | bool) ::
- bool :: //crashes client
- uint(16) :: //unknown, but usually 0
- ("name" | PacketHelpers.encodedWideStringAligned( namePadding(pos.vel) )) ::
- ("exosuit" | ExoSuitType.codec) ::
- ignore(2) :: //unknown
- ("sex" | CharacterGender.codec) ::
- ("head" | uint8L) ::
- ("voice" | uint(3)) ::
- ("voice2" | uint2L) ::
- ignore(78) :: //unknown
- uint16L :: //usually either 0 or 65535
- uint32L :: //for outfit_name (below) to be visible in-game, this value should be non-zero
- ("outfit_name" | PacketHelpers.encodedWideStringAligned( outfitNamePadding )) ::
- ("outfit_logo" | uint8L) ::
- ignore(1) :: //unknown
- ("backpack" | bool) :: //requires alt_model flag (does NOT require health == 0)
- bool :: //stream misalignment when set
- ("facingPitch" | Angular.codec_pitch) ::
- ("facingYawUpper" | Angular.codec_yaw(0f)) ::
- ignore(1) :: //unknown
- conditional(alt_model, bool) :: //alt_model flag adds a bit before lfs
- ignore(1) :: //an alternate lfs?
- ("lfs" | bool) ::
- ("grenade_state" | GrenadeState.codec_2u) :: //note: bin10 and bin11 are neutral (bin00 is not defined)
- ("is_cloaking" | bool) ::
- ignore(1) :: //unknown
- bool :: //stream misalignment when set
- ("charging_pose" | bool) ::
- ignore(1) :: //alternate charging pose?
- ("on_zipline" | bool) :: //requires alt_model flag
- ("ribbons" | RibbonBars.codec)
- })
- }).exmap[CharacterAppearanceData] (
+ ("backpack" | bool) :: //requires alt_model flag (does NOT require health == 0)
+ bool :: //stream misalignment when set
+ ("facingPitch" | Angular.codec_pitch) ::
+ ("facingYawUpper" | Angular.codec_yaw(0f)) ::
+ ignore(1) :: //unknown
+ conditional(alt_model, bool) :: //alt_model flag adds a bit before lfs
+ ignore(1) :: //an alternate lfs?
+ ("lfs" | bool) ::
+ ("grenade_state" | GrenadeState.codec_2u) :: //note: bin10 and bin11 are neutral (bin00 is not defined)
+ ("is_cloaking" | bool) ::
+ ignore(1) :: //unknown
+ bool :: //stream misalignment when set
+ ("charging_pose" | bool) ::
+ ignore(1) :: //alternate charging pose?
+ ("on_zipline" | bool) :: //requires alt_model flag
+ ("ribbons" | RibbonBars.codec)
+ })
+ ).exmap[CharacterAppearanceData] (
{
- case _ :: _ :: _ :: false :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: true :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: HNil |
- _ :: _ :: _ :: false :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: true :: _ :: HNil =>
+ case _ :: _ :: false :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: true :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: HNil |
+ _ :: _ :: false :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: true :: _ :: HNil =>
Attempt.Failure(Err("invalid character appearance data; can not encode alternate model without required bit set"))
- case pos :: faction :: bops :: _ :: _ :: jamd :: false :: 0 :: name :: suit :: _ :: sex :: head :: v1 :: v2 :: _ :: _ :: _/*has_outfit_name*/ :: outfit :: logo :: _ :: bpack :: false :: facingPitch :: facingYawUpper :: _ :: _ :: _ :: lfs :: gstate :: cloaking :: _ :: false :: charging :: _ :: zipline :: ribbons :: HNil =>
+ case faction :: bops :: _ :: _ :: jamd :: false :: 0 :: name :: suit :: _ :: sex :: head :: v1 :: v2 :: _ :: _ :: _/*has_outfit_name*/ :: outfit :: logo :: _ :: bpack :: false :: facingPitch :: facingYawUpper :: _ :: _ :: _ :: lfs :: gstate :: cloaking :: _ :: false :: charging :: _ :: zipline :: ribbons :: HNil =>
Attempt.successful(
- CharacterAppearanceData(pos, BasicCharacterData(name, faction, sex, head, v1), v2, bops, jamd, suit, outfit, logo, bpack, facingPitch, facingYawUpper, lfs, gstate, cloaking, charging, zipline, ribbons)
+ CharacterAppearanceData(BasicCharacterData(name, faction, sex, head, v1), v2, bops, jamd, suit, outfit, logo, bpack, facingPitch, facingYawUpper, lfs, gstate, cloaking, charging, zipline, ribbons)(name_padding)
)
case _ =>
Attempt.Failure(Err("invalid character appearance data; can not encode"))
},
{
- case CharacterAppearanceData(_, BasicCharacterData(name, PlanetSideEmpire.NEUTRAL, _, _, _), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) =>
+ case CharacterAppearanceData(BasicCharacterData(name, PlanetSideEmpire.NEUTRAL, _, _, _), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) =>
Attempt.failure(Err(s"character $name's faction can not declare as neutral"))
- case CharacterAppearanceData(pos, BasicCharacterData(name, faction, sex, head, v1), v2, bops, jamd, suit, outfit, logo, bpack, facingPitch, facingYawUpper, lfs, gstate, cloaking, charging, zipline, ribbons) =>
+ case CharacterAppearanceData(BasicCharacterData(name, faction, sex, head, v1), v2, bops, jamd, suit, outfit, logo, bpack, facingPitch, facingYawUpper, lfs, gstate, cloaking, charging, zipline, ribbons) =>
val has_outfit_name : Long = outfit.length.toLong //TODO this is a kludge
- var alt_model : Boolean = false
+ var alt_model : Boolean = false
var alt_model_extrabit : Option[Boolean] = None
if(zipline || bpack) {
alt_model = true
alt_model_extrabit = Some(false)
}
Attempt.successful(
- pos :: faction :: bops :: alt_model :: () :: jamd :: false :: 0 :: name :: suit :: () :: sex :: head :: v1 :: v2 :: () :: 0 :: has_outfit_name :: outfit :: logo :: () :: bpack :: false :: facingPitch :: facingYawUpper :: () :: alt_model_extrabit :: () :: lfs :: gstate :: cloaking :: () :: false :: charging :: () :: zipline :: ribbons :: HNil
+ faction :: bops :: alt_model :: () :: jamd :: false :: 0 :: name :: suit :: () :: sex :: head :: v1 :: v2 :: () :: 0 :: has_outfit_name :: outfit :: logo :: () :: bpack :: false :: facingPitch :: facingYawUpper :: () :: alt_model_extrabit :: () :: lfs :: gstate :: cloaking :: () :: false :: charging :: () :: zipline :: ribbons :: HNil
)
case _ =>
Attempt.Failure(Err("invalid character appearance data; can not decode"))
}
)
+
+ implicit val codec : Codec[CharacterAppearanceData] = codec(0)
}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterData.scala
index 51d08d63c..9ce35e9b8 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterData.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/CharacterData.scala
@@ -42,27 +42,17 @@ object UniformStyle extends Enumeration {
}
/**
- * A part of a representation of the avatar portion of `ObjectCreateMessage` packet data.
- * This densely-packed information outlines most of the specifics of depicting some other character.
+ * A representation of a portion of an avatar's `ObjectCreateDetailedMessage` packet data.
*
- * The character created by this data is treated like an NPC from the perspective of the server.
+ * This densely-packed information outlines most of the specifics required to depict some other player's character.
* Someone else decides how that character is behaving and the server tells each client how to depict that behavior.
* For that reason, the character is mostly for presentation purposes, rather than really being fleshed-out.
- * (As far as the client is concerned, nothing stops this character from being declared an "avatar."
- * A player would find such a client-controlled character lacking many important details and have poor equipment.
- * They would also be competing with some other player for input control, if they could control the character at all.)
+ * Of the inventory for this character, only the initial five weapon slots are defined.
*
- * Divisions exist to make the data more manageable.
- * The first division of data only manages the general appearance of the player's in-game model.
- * The second division (currently, the fields actually in this class) manages the status of the character.
- * In general, it passes more simplified data about the character, the minimum that is necessary to explain status to some other player.
- * For example, health and armor are percentages, and are depicted as bars over the player's head near the nameplate.
- * The third is the inventory (composed of normal-type objects).
- * Rather than equipment other players would never interact with, it only comprises the contents of the five holster slots.
- *
- * If this player is spawned as dead - with their `health` at 0% - he will start standing and then immediately fall into a lying pose.
- * The death pose selected is randomized, can not be influenced, and is not be shared across clients.
- * @param appearance the player's cardinal appearance settings
+ * In the "backend of the client," the character produced by this data is no different
+ * from the kind of character that could be declared a given player's avatar.
+ * In terms of equipment and complicated features common to an avatar character, however,
+ * any user would find this character ill-equipped.
* @param health the amount of health the player has, as a percentage of a filled bar;
* the bar has 85 states, with 3 points for each state;
* when 0% (less than 3 of 255), the player will collapse into a death pose on the ground
@@ -76,89 +66,69 @@ object UniformStyle extends Enumeration {
* @param cosmetics optional decorative features that are added to the player's head model by console/chat commands;
* they become available at battle rank 24, but here they require the third uniform upgrade (rank 25);
* these flags do not exist if they are not applicable
- * @param inventory the avatar's inventory;
- * typically, only the tools and weapons in the equipment holster slots
- * @param drawn_slot the holster that is initially drawn;
- * defaults to `DrawnSlot.None`
- * @see `CharacterAppearanceData`
* @see `DetailedCharacterData`
- * @see `InventoryData`
- * @see `DrawnSlot`
*/
-final case class CharacterData(appearance : CharacterAppearanceData,
- health : Int,
+final case class CharacterData(health : Int,
armor : Int,
uniform_upgrade : UniformStyle.Value,
+ unk : Int,
command_rank : Int,
implant_effects : Option[ImplantEffects.Value],
- cosmetics : Option[Cosmetics],
- inventory : Option[InventoryData],
- drawn_slot : DrawnSlot.Value = DrawnSlot.None
- ) extends ConstructorData {
+ cosmetics : Option[Cosmetics])
+ (is_backpack : Boolean) extends ConstructorData {
override def bitsize : Long = {
//factor guard bool values into the base size, not its corresponding optional field
- val appearanceSize : Long = appearance.bitsize
val effectsSize : Long = if(implant_effects.isDefined) { 4L } else { 0L }
val cosmeticsSize : Long = if(cosmetics.isDefined) { cosmetics.get.bitsize } else { 0L }
- val inventorySize : Long = if(inventory.isDefined) { inventory.get.bitsize } else { 0L }
- 32L + appearanceSize + effectsSize + cosmeticsSize + inventorySize
+ 27L + effectsSize + cosmeticsSize
}
}
object CharacterData extends Marshallable[CharacterData] {
/**
* An overloaded constructor for `CharacterData` that allows for a not-optional inventory.
- * @param appearance the player's cardinal appearance settings
* @param health the amount of health the player has, as a percentage of a filled bar
* @param armor the amount of armor the player has, as a percentage of a filled bar
* @param uniform the level of upgrade to apply to the player's base uniform
* @param cr the player's command rank as a number from 0 to 5
* @param implant_effects the effects of implants that can be seen on a player's character
* @param cosmetics optional decorative features that are added to the player's head model by console/chat commands
- * @param inv the avatar's inventory
- * @param drawn_slot the holster that is initially drawn
+ * //@param inv the avatar's inventory
+ * //@param drawn_slot the holster that is initially drawn
* @return a `CharacterData` object
*/
- def apply(appearance : CharacterAppearanceData, health : Int, armor : Int, uniform : UniformStyle.Value, cr : Int, implant_effects : Option[ImplantEffects.Value], cosmetics : Option[Cosmetics], inv : InventoryData, drawn_slot : DrawnSlot.Value) : CharacterData =
- new CharacterData(appearance, health, armor, uniform, cr, implant_effects, cosmetics, Some(inv), drawn_slot)
+ def apply(health : Int, armor : Int, uniform : UniformStyle.Value, cr : Int, implant_effects : Option[ImplantEffects.Value], cosmetics : Option[Cosmetics]) : (Boolean)=>CharacterData =
+ CharacterData(health, armor, uniform, 0, cr, implant_effects, cosmetics)
- implicit val codec : Codec[CharacterData] = (
- ("app" | CharacterAppearanceData.codec) ::
- ("health" | uint8L) :: //dead state when health == 0
+ def codec(is_backpack : Boolean) : Codec[CharacterData] = (
+ ("health" | uint8L) :: //dead state when health == 0
("armor" | uint8L) ::
(("uniform_upgrade" | UniformStyle.codec) >>:~ { style =>
ignore(3) :: //unknown
("command_rank" | uintL(3)) ::
bool :: //stream misalignment when != 1
optional(bool, "implant_effects" | ImplantEffects.codec) ::
- conditional(style == UniformStyle.ThirdUpgrade, "cosmetics" | Cosmetics.codec) ::
- optional(bool, "inventory" | InventoryData.codec) ::
- ("drawn_slot" | DrawnSlot.codec) ::
- bool //usually false
+ conditional(style == UniformStyle.ThirdUpgrade, "cosmetics" | Cosmetics.codec)
})
- ).exmap[CharacterData] (
+ ).exmap[CharacterData] (
{
- case app :: health :: armor :: uniform :: _ :: cr :: false :: implant_effects :: cosmetics :: inv :: drawn_slot :: false :: HNil =>
- var newHealth = health
- if(app.backpack) {
- newHealth = 0
- }
- Attempt.Successful(CharacterData(app, newHealth, armor, uniform, cr, implant_effects, cosmetics, inv, drawn_slot))
+ case health :: armor :: uniform :: _ :: cr :: false :: implant_effects :: cosmetics :: HNil =>
+ val newHealth = if(is_backpack) { 0 } else { health }
+ Attempt.Successful(new CharacterData(newHealth, armor, uniform, 0, cr, implant_effects, cosmetics)(is_backpack))
case _ =>
Attempt.Failure(Err("invalid character data; can not encode"))
},
{
- case CharacterData(app, health, armor, uniform, cr, implant_effects, cosmetics, inv, drawn_slot) =>
- var newHealth = health
- if(app.backpack) {
- newHealth = 0
- }
- Attempt.Successful(app :: newHealth :: armor :: uniform :: () :: cr :: false :: implant_effects :: cosmetics :: inv :: drawn_slot :: false :: HNil)
+ case CharacterData(health, armor, uniform, _, cr, implant_effects, cosmetics) =>
+ val newHealth = if(is_backpack) { 0 } else { health }
+ Attempt.Successful(newHealth :: armor :: uniform :: () :: cr :: false :: implant_effects :: cosmetics :: HNil)
case _ =>
Attempt.Failure(Err("invalid character data; can not decode"))
}
)
+
+ implicit val codec : Codec[CharacterData] = codec(false)
}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedCharacterData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedCharacterData.scala
index c2fa2d747..fa7f7f933 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedCharacterData.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedCharacterData.scala
@@ -27,22 +27,14 @@ final case class ImplantEntry(implant : ImplantType.Value,
}
/**
- * A representation of the avatar portion of `ObjectCreateDetailedMessage` packet data.
- * This densely-packed information outlines most of the specifics required to depict a character as an avatar.
+ * A representation of a portion of an avatar's `ObjectCreateDetailedMessage` packet data.
*
- * As an avatar, the character created by this data is expected to be controllable by the client that gets sent this data.
- * It goes into depth about information related to the given character in-game career that is not revealed to other players.
- *
- * Divisions exist to make the data more manageable.
- * The first division of data only manages the general appearance of the player's in-game model.
- * It is shared between `DetailedCharacterData` avatars and `CharacterData` player characters.
- * The second division (of fields) manages the status of the character as an avatar.
- * In general, it passes more thorough data about the character that the client can display to the owner of the client.
+ * This densely-packed information outlines most of the specifics required to depict a character as an avatar.
+ * It goes into depth about information related to the given character in-game career that is not revealed to other players.
+ * To be specific, it passes more thorough data about the character that the client can display to the owner of the client.
* For example, health is a full number, rather than a percentage.
* Just as prominent is the list of first time events and the list of completed tutorials.
- * The third subdivision is also exclusive to avatar-prepared characters and contains (omitted).
- * The fourth is the inventory (composed of `Direct`-type objects).
- * @param appearance data about the avatar's basic aesthetics
+ * Additionally, a full inventory, as opposed to the initial five weapon slots.
* @param bep the avatar's battle experience points, which determines his Battle Rank
* @param cep the avatar's command experience points, which determines his Command Rank
* @param healthMax for `x / y` of hitpoints, this is the avatar's `y` value;
@@ -73,16 +65,10 @@ final case class ImplantEntry(implant : ImplantType.Value,
* @param cosmetics optional decorative features that are added to the player's head model by console/chat commands;
* they become available at battle rank 24;
* these flags do not exist if they are not applicable
- * @param inventory the avatar's inventory
- * @param drawn_slot the holster that is initially drawn
- * @see `CharacterAppearanceData`
- * `CharacterData`
- * `CertificationType`
- * `InventoryData`
- * `DrawnSlot`
+ * @see `CharacterData`
+ * `CertificationType`
*/
-final case class DetailedCharacterData(appearance : CharacterAppearanceData,
- bep : Long,
+final case class DetailedCharacterData(bep : Long,
cep : Long,
healthMax : Int,
health : Int,
@@ -96,20 +82,17 @@ final case class DetailedCharacterData(appearance : CharacterAppearanceData,
implants : List[ImplantEntry],
firstTimeEvents : List[String],
tutorials : List[String],
- cosmetics : Option[Cosmetics],
- inventory : Option[InventoryData],
- drawn_slot : DrawnSlot.Value = DrawnSlot.None
- ) extends ConstructorData {
+ cosmetics : Option[Cosmetics])
+ (pad_length : Option[Int]) extends ConstructorData {
override def bitsize : Long = {
//factor guard bool values into the base size, not corresponding optional fields, unless contained or enumerated
- val appearanceSize = appearance.bitsize
val certSize = (certs.length + 1) * 8 //cert list
var implantSize : Long = 0L //implant list
for(entry <- implants) {
implantSize += entry.bitsize
}
- val implantPadding = DetailedCharacterData.implantFieldPadding(implants, CharacterAppearanceData.altModelBit(appearance))
+ val implantPadding = DetailedCharacterData.implantFieldPadding(implants, pad_length)
val fteLen = firstTimeEvents.size //fte list
var eventListSize : Long = 32L + DetailedCharacterData.ftePadding(fteLen, implantPadding)
for(str <- firstTimeEvents) {
@@ -123,20 +106,13 @@ final case class DetailedCharacterData(appearance : CharacterAppearanceData,
val br24 = DetailedCharacterData.isBR24(bep) //character is at least BR24
val extraBitSize : Long = if(br24) { 33L } else { 46L }
val cosmeticsSize : Long = if(br24) { cosmetics.get.bitsize } else { 0L }
- val inventorySize : Long = if(inventory.isDefined) { //inventory
- inventory.get.bitsize
- }
- else {
- 0L
- }
- 603L + appearanceSize + certSize + implantSize + eventListSize + extraBitSize + cosmeticsSize + tutorialListSize + inventorySize
+ 598L + certSize + implantSize + eventListSize + extraBitSize + cosmeticsSize + tutorialListSize
}
}
object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
/**
* Overloaded constructor for `DetailedCharacterData` that requires an inventory and drops unknown values.
- * @param appearance data about the avatar's basic aesthetics
* @param bep the avatar's battle experience points, which determines his Battle Rank
* @param cep the avatar's command experience points, which determines his Command Rank
* @param healthMax for `x / y` of hitpoints, this is the avatar's `y` value
@@ -148,12 +124,10 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
* @param implants the `List` of implant slots currently possessed by this avatar
* @param firstTimeEvents the list of first time events performed by this avatar
* @param tutorials the list of tutorials completed by this avatar
- * @param inventory the avatar's inventory
- * @param drawn_slot the holster that is initially drawn
* @return a `DetailedCharacterData` object
*/
- def apply(appearance : CharacterAppearanceData, bep : Long, cep : Long, healthMax : Int, health : Int, armor : Int, staminaMax : Int, stamina : Int, certs : List[CertificationType.Value], implants : List[ImplantEntry], firstTimeEvents : List[String], tutorials : List[String], cosmetics : Option[Cosmetics], inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedCharacterData =
- new DetailedCharacterData(appearance, bep, cep, healthMax, health, armor, 1, 7, 7, staminaMax, stamina, certs, implants, firstTimeEvents, tutorials, cosmetics, Some(inventory), drawn_slot)
+ def apply(bep : Long, cep : Long, healthMax : Int, health : Int, armor : Int, staminaMax : Int, stamina : Int, certs : List[CertificationType.Value], implants : List[ImplantEntry], firstTimeEvents : List[String], tutorials : List[String], cosmetics : Option[Cosmetics]) : (Option[Int])=>DetailedCharacterData =
+ DetailedCharacterData(bep, cep, healthMax, health, armor, 1, 7, 7, staminaMax, stamina, certs, implants, firstTimeEvents, tutorials, cosmetics)
/**
* `Codec` for entries in the `List` of implants.
@@ -278,57 +252,52 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
def isBR24(bep : Long) : Boolean = bep > 2286230
- implicit val codec : Codec[DetailedCharacterData] = (
- ("appearance" | CharacterAppearanceData.codec) >>:~ { app =>
- ("bep" | uint32L) >>:~ { bep =>
- ("cep" | uint32L) ::
- ignore(96) ::
- ("healthMax" | uint16L) ::
- ("health" | uint16L) ::
- ignore(1) ::
- ("armor" | uint16L) ::
- ignore(9) ::
- ("unk1" | uint8L) ::
- ignore(8) ::
- ("unk2" | uint4L) ::
- ("unk3" | uintL(3)) ::
- ("staminaMax" | uint16L) ::
- ("stamina" | uint16L) ::
- ignore(147) ::
- ("certs" | listOfN(uint8L, CertificationType.codec)) ::
- optional(bool, uint32L) :: //ask about sample CCRIDER
- ignore(4) ::
- (("implants" | PacketHelpers.listOfNSized(numberOfImplantSlots(bep), implant_entry_codec)) >>:~ { implants =>
- ignore(12) ::
- (("firstTimeEvent_length" | uint32L) >>:~ { len =>
- conditional(len > 0, "firstTimeEvent_firstEntry" | PacketHelpers.encodedStringAligned(ftePadding(len, implantFieldPadding(implants, CharacterAppearanceData.altModelBit(app))))) ::
- ("firstTimeEvent_list" | PacketHelpers.listOfNSized(len - 1, PacketHelpers.encodedString)) ::
- (("tutorial_length" | uint32L) >>:~ { len2 =>
- conditional(len2 > 0, "tutorial_firstEntry" | PacketHelpers.encodedStringAligned(tutPadding(len, len2, implantFieldPadding(implants, CharacterAppearanceData.altModelBit(app))))) ::
- ("tutorial_list" | PacketHelpers.listOfNSized(len2 - 1, PacketHelpers.encodedString)) ::
- ignore(160) ::
- (bool >>:~ { br24 => //BR24+
- newcodecs.binary_choice(br24, ignore(33), ignore(46)) ::
- conditional(br24, Cosmetics.codec) ::
- optional(bool, "inventory" | InventoryData.codec_detailed) ::
- ("drawn_slot" | DrawnSlot.codec) ::
- bool //usually false
- })
- })
- })
- })
- }
+ def codec(pad_length : Option[Int]) : Codec[DetailedCharacterData] = (
+ ("bep" | uint32L) >>:~ { bep =>
+ ("cep" | uint32L) ::
+ ignore(96) ::
+ ("healthMax" | uint16L) ::
+ ("health" | uint16L) ::
+ ignore(1) ::
+ ("armor" | uint16L) ::
+ ignore(9) ::
+ ("unk1" | uint8L) ::
+ ignore(8) ::
+ ("unk2" | uint4L) ::
+ ("unk3" | uintL(3)) ::
+ ("staminaMax" | uint16L) ::
+ ("stamina" | uint16L) ::
+ ignore(147) ::
+ ("certs" | listOfN(uint8L, CertificationType.codec)) ::
+ optional(bool, uint32L) :: //ask about sample CCRIDER
+ ignore(4) ::
+ (("implants" | PacketHelpers.listOfNSized(numberOfImplantSlots(bep), implant_entry_codec)) >>:~ { implants =>
+ ignore(12) ::
+ (("firstTimeEvent_length" | uint32L) >>:~ { len =>
+ conditional(len > 0, "firstTimeEvent_firstEntry" | PacketHelpers.encodedStringAligned(ftePadding(len, implantFieldPadding(implants, pad_length)))) ::
+ ("firstTimeEvent_list" | PacketHelpers.listOfNSized(len - 1, PacketHelpers.encodedString)) ::
+ (("tutorial_length" | uint32L) >>:~ { len2 =>
+ conditional(len2 > 0, "tutorial_firstEntry" | PacketHelpers.encodedStringAligned(tutPadding(len, len2, implantFieldPadding(implants, pad_length)))) ::
+ ("tutorial_list" | PacketHelpers.listOfNSized(len2 - 1, PacketHelpers.encodedString)) ::
+ ignore(160) ::
+ (bool >>:~ { br24 => //BR24+
+ newcodecs.binary_choice(br24, ignore(33), ignore(46)) ::
+ conditional(br24, Cosmetics.codec)
+ })
+ })
+ })
+ })
}
).exmap[DetailedCharacterData] (
{
- case app :: bep :: cep :: _ :: hpmax :: hp :: _ :: armor :: _ :: u1 :: _ :: u2 :: u3 :: stamax :: stam :: _ :: certs :: _ :: _ :: implants :: _ :: _ :: fte0 :: fte1 :: _ :: tut0 :: tut1 :: _ :: _ :: _ :: cosmetics :: inv :: drawn :: false :: HNil =>
+ case bep :: cep :: _ :: hpmax :: hp :: _ :: armor :: _ :: u1 :: _ :: u2 :: u3 :: stamax :: stam :: _ :: certs :: _ :: _ :: implants :: _ :: _ :: fte0 :: fte1 :: _ :: tut0 :: tut1 :: _ :: _ :: _ :: cosmetics :: HNil =>
//prepend the displaced first elements to their lists
val fteList : List[String] = if(fte0.isDefined) { fte0.get +: fte1 } else { fte1 }
val tutList : List[String] = if(tut0.isDefined) { tut0.get +: tut1 } else { tut1 }
- Attempt.successful(DetailedCharacterData(app, bep, cep, hpmax, hp, armor, u1, u2, u3, stamax, stam, certs, implants, fteList, tutList, cosmetics, inv, drawn))
+ Attempt.successful(DetailedCharacterData(bep, cep, hpmax, hp, armor, u1, u2, u3, stamax, stam, certs, implants, fteList, tutList, cosmetics)(pad_length))
},
{
- case DetailedCharacterData(app, bep, cep, hpmax, hp, armor, u1, u2, u3, stamax, stam, certs, implants, fteList, tutList, cos, inv, drawn) =>
+ case DetailedCharacterData(bep, cep, hpmax, hp, armor, u1, u2, u3, stamax, stam, certs, implants, fteList, tutList, cos) =>
val implantCapacity : Int = numberOfImplantSlots(bep)
val implantList = if(implants.length > implantCapacity) {
implants.slice(0, implantCapacity)
@@ -349,7 +318,9 @@ object DetailedCharacterData extends Marshallable[DetailedCharacterData] {
}
val br24 : Boolean = isBR24(bep)
val cosmetics : Option[Cosmetics] = if(br24) { cos } else { None }
- Attempt.successful(app :: bep :: cep :: () :: hpmax :: hp :: () :: armor :: () :: u1 :: () :: u2 :: u3 :: stamax :: stam :: () :: certs :: None :: () :: implantList :: () :: fteList.size.toLong :: firstEvent :: fteListCopy :: tutList.size.toLong :: firstTutorial :: tutListCopy :: () :: br24 :: () :: cosmetics :: inv :: drawn :: false :: HNil)
+ Attempt.successful(bep :: cep :: () :: hpmax :: hp :: () :: armor :: () :: u1 :: () :: u2 :: u3 :: stamax :: stam :: () :: certs :: None :: () :: implantList :: () :: fteList.size.toLong :: firstEvent :: fteListCopy :: tutList.size.toLong :: firstTutorial :: tutListCopy :: () :: br24 :: () :: cosmetics :: HNil)
}
)
+
+ implicit val codec : Codec[DetailedCharacterData] = codec(None)
}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedPlayerData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedPlayerData.scala
new file mode 100644
index 000000000..6fcff934f
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/DetailedPlayerData.scala
@@ -0,0 +1,103 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game.objectcreate
+
+import net.psforever.packet.Marshallable
+import scodec.codecs._
+import scodec.Codec
+import shapeless.{::, HNil}
+
+/**
+ * A representation of an `avatar` player for the `ObjectCreateDetailedMessage` packet.
+ * As an avatar, the character created by this data is expected to be controllable by the client that gets sent this data.
+ *
+ * Divisions exist to make the data more manageable.
+ * The first division defines the player's location within the game coordinate system.
+ * The second division defines features of the `avatar`
+ * that are shared by both the `ObjectCreateDetailedMessage` version of a controlled player character (this)
+ * and the `ObjectCreateMessage` version of a player character.
+ * The third field expands on the nature of the character and this avatar's campaign.
+ * Expansive information about previous interactions, the contents of their inventory, and equipment permissions are included.
+ *
+ * The presence or absence of position data as the first division creates a cascading effect
+ * causing all of fields in the other two divisions to gain offsets.
+ * These offsets exist in the form of `String` and `List` padding.
+ * @see `DetailedCharacterData`
+ * `InventoryData`
+ * `DrawnSlot`
+ * @param pos the optional position of the character in the world environment
+ * @param basic_appearance common fields regarding the the character's appearance
+ * @param character_data the class-specific data that explains about the character
+ * @param position_defined used by the `Codec` to seed the state of the optional `pos` field
+ * @param inventory the player's full inventory
+ * @param drawn_slot the holster that is initially drawn
+ */
+final case class DetailedPlayerData(pos : Option[PlacementData],
+ basic_appearance : CharacterAppearanceData,
+ character_data : DetailedCharacterData,
+ inventory : Option[InventoryData],
+ drawn_slot : DrawnSlot.Value)
+ (position_defined : Boolean) extends ConstructorData {
+ override def bitsize : Long = {
+ val posSize : Long = if(pos.isDefined) { pos.get.bitsize } else { 0 }
+ val appSize : Long = basic_appearance.bitsize
+ val charSize = character_data.bitsize
+ val inventorySize : Long = if(inventory.isDefined) { inventory.get.bitsize } else { 0L }
+ 5L + posSize + appSize + charSize + inventorySize
+ }
+}
+
+object DetailedPlayerData extends Marshallable[DetailedPlayerData] {
+ /**
+ * Overloaded constructor that ignores the coordinate information.
+ * It passes information between the three major divisions for the purposes of offset calculations.
+ * @param basic_appearance a curried function for the common fields regarding the the character's appearance
+ * @param character_data a curried function for the class-specific data that explains about the character
+ * @return a `DetailedPlayerData` object
+ */
+ def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
+ val appearance = basic_appearance(0)
+ DetailedPlayerData(None, appearance, character_data(appearance.altModelBit), Some(inventory), drawn_slot)(false)
+ }
+ /** */
+ def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
+ val appearance = basic_appearance(0)
+ DetailedPlayerData(None, appearance, character_data(appearance.altModelBit), None, drawn_slot)(false)
+ }
+ /**
+ * Overloaded constructor that includes the coordinate information.
+ * It passes information between the three major divisions for the purposes of offset calculations.
+ * @param basic_appearance a curried function for the common fields regarding the the character's appearance
+ * @param character_data a curried function for the class-specific data that explains about the character
+ * @return a `DetailedPlayerData` object
+ */
+ def apply(pos : PlacementData, basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
+ val appearance = basic_appearance(PlayerData.placementOffset(Some(pos)))
+ DetailedPlayerData(Some(pos), appearance, character_data(appearance.altModelBit), Some(inventory), drawn_slot)(true)
+ }
+ /** */
+ def apply(pos : PlacementData, basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Option[Int])=>DetailedCharacterData, drawn_slot : DrawnSlot.Value) : DetailedPlayerData = {
+ val appearance = basic_appearance(PlayerData.placementOffset(Some(pos)))
+ DetailedPlayerData(Some(pos), appearance, character_data(appearance.altModelBit), None, drawn_slot)(true)
+ }
+
+ def codec(position_defined : Boolean) : Codec[DetailedPlayerData] = (
+ conditional(position_defined, "pos" | PlacementData.codec) >>:~ { pos =>
+ ("basic_appearance" | CharacterAppearanceData.codec(PlayerData.placementOffset(pos))) >>:~ { app =>
+ ("character_data" | DetailedCharacterData.codec(app.altModelBit)) ::
+ optional(bool, "inventory" | InventoryData.codec_detailed) ::
+ ("drawn_slot" | DrawnSlot.codec) ::
+ bool //usually false
+ }
+ }).xmap[DetailedPlayerData] (
+ {
+ case pos :: app :: data :: inv :: hand :: _ :: HNil =>
+ DetailedPlayerData(pos, app, data, inv, hand)(pos.isDefined)
+ },
+ {
+ case DetailedPlayerData(pos, app, data, inv, hand) =>
+ pos :: app :: data :: inv :: hand :: false :: HNil
+ }
+ )
+
+ implicit val codec : Codec[DetailedPlayerData] = codec(false)
+}
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
index 40a95b499..001351011 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/ObjectClass.scala
@@ -666,7 +666,7 @@ object ObjectClass {
case ObjectClass.advanced_ace => ConstructorData.genericCodec(DetailedACEData.codec, "advanced ace")
case ObjectClass.boomer_trigger => ConstructorData.genericCodec(DetailedBoomerTriggerData.codec, "boomer trigger")
//other
- case ObjectClass.avatar => ConstructorData.genericCodec(DetailedCharacterData.codec, "avatar")
+ case ObjectClass.avatar => ConstructorData.genericCodec(DetailedPlayerData.codec(true), "avatar")
case ObjectClass.locker_container => ConstructorData.genericCodec(DetailedLockerContainerData.codec, "locker container")
//failure case
@@ -953,6 +953,7 @@ object ObjectClass {
//other
case ObjectClass.ams_order_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
case ObjectClass.ams_respawn_tube => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
+ case ObjectClass.avatar => ConstructorData.genericCodec(PlayerData.codec(false), "avatar")
case ObjectClass.bfr_rearm_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
case ObjectClass.implant_terminal_interface => ConstructorData.genericCodec(CommonTerminalData.codec, "implant terminal")
case ObjectClass.lodestar_repair_terminal => ConstructorData.genericCodec(CommonTerminalData.codec, "terminal")
@@ -1268,7 +1269,7 @@ object ObjectClass {
case ObjectClass.wasp => ConstructorData.genericCodec(VehicleData.codec(VehicleFormat.Variant), "vehicle")
//other
case ObjectClass.ams_respawn_tube => DroppedItemData.genericCodec(CommonTerminalData.codec, "terminal")
- case ObjectClass.avatar => ConstructorData.genericCodec(CharacterData.codec, "avatar")
+ case ObjectClass.avatar => ConstructorData.genericCodec(PlayerData.codec(true), "avatar")
case ObjectClass.capture_flag => ConstructorData.genericCodec(CaptureFlagData.codec, "capture flag")
case ObjectClass.implant_terminal_interface => ConstructorData.genericCodec(CommonTerminalData.codec, "implant terminal")
case ObjectClass.locker_container => ConstructorData.genericCodec(LockerContainerData.codec, "locker container")
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/PlayerData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/PlayerData.scala
new file mode 100644
index 000000000..f808af508
--- /dev/null
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/PlayerData.scala
@@ -0,0 +1,128 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.packet.game.objectcreate
+
+import net.psforever.packet.Marshallable
+import scodec.codecs._
+import scodec.Codec
+import shapeless.{::, HNil}
+
+/**
+ * A representation of another player's character for the `ObjectCreateDetailedMessage` packet.
+ * In general, this packet is used to describe other players.
+ *
+ * Divisions exist to make the data more manageable.
+ * The first division defines the player's location within the game coordinate system.
+ * The second division defines features of the character
+ * that are shared by both the `ObjectCreateDetailedMessage` version of a controlled player character
+ * and the `ObjectCreateMessage` version of a player character (this).
+ * The third field provides further information on the appearance of the player character, albeit condensed.
+ * One of the most compact forms of a player character description is transcribed using this information.
+ *
+ * The presence or absence of position data as the first division creates a cascading effect
+ * causing all of fields in the other two divisions to gain offsets.
+ * These offsets exist in the form of `String` and `List` padding.
+ * @see `CharacterData`
+ * `InventoryData`
+ * `DrawnSlot`
+ * @param pos the optional position of the character in the world environment
+ * @param basic_appearance common fields regarding the the character's appearance
+ * @param character_data the class-specific data that explains about the character
+ * @param inventory the player's inventory;
+ * typically, only the tools and weapons in the equipment holster slots
+ * @param drawn_slot the holster that is initially drawn
+ * @param position_defined used by the `Codec` to seed the state of the optional `pos` field
+ */
+final case class PlayerData(pos : Option[PlacementData],
+ basic_appearance : CharacterAppearanceData,
+ character_data : CharacterData,
+ inventory : Option[InventoryData],
+ drawn_slot : DrawnSlot.Value)
+ (position_defined : Boolean) extends ConstructorData {
+ override def bitsize : Long = {
+ val posSize : Long = if(pos.isDefined) { pos.get.bitsize } else { 0 }
+ val appSize : Long = basic_appearance.bitsize
+ val charSize = character_data.bitsize
+ val inventorySize : Long = if(inventory.isDefined) { inventory.get.bitsize } else { 0L }
+ 5L + posSize + appSize + charSize + inventorySize
+ }
+}
+
+object PlayerData extends Marshallable[PlayerData] {
+ /**
+ * Overloaded constructor that ignores the coordinate information.
+ * It passes information between the three major divisions for the purposes of offset calculations.
+ * @param basic_appearance a curried function for the common fields regarding the the character's appearance
+ * @param character_data a curried function for the class-specific data that explains about the character
+ * @param inventory the player's inventory
+ * @param drawn_slot the holster that is initially drawn
+ * @return a `PlayerData` object
+ */
+ def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean)=>CharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Type) : PlayerData = {
+ val appearance = basic_appearance(0)
+ PlayerData(None, appearance, character_data(appearance.backpack), Some(inventory), drawn_slot)(false)
+ }
+ /** */
+ def apply(basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean)=>CharacterData, hand_held : DrawnSlot.Type) : PlayerData = {
+ val appearance = basic_appearance(0)
+ PlayerData(None, appearance, character_data(appearance.backpack), None, hand_held)(false)
+ }
+ /**
+ * Overloaded constructor that includes the coordinate information.
+ * It passes information between the three major divisions for the purposes of offset calculations.
+ * @param pos the optional position of the character in the world environment
+ * @param basic_appearance a curried function for the common fields regarding the the character's appearance
+ * @param character_data a curried function for the class-specific data that explains about the character
+ * @param inventory the player's inventory
+ * @param drawn_slot the holster that is initially drawn
+ * @return a `PlayerData` object
+ */
+ def apply(pos : PlacementData, basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean)=>CharacterData, inventory : InventoryData, drawn_slot : DrawnSlot.Type) : PlayerData = {
+ val appearance = basic_appearance( placementOffset(Some(pos)) )
+ PlayerData(Some(pos), appearance, character_data(appearance.backpack), Some(inventory), drawn_slot)(true)
+ }
+ /** */
+ def apply(pos : PlacementData, basic_appearance : (Int)=>CharacterAppearanceData, character_data : (Boolean)=>CharacterData, hand_held : DrawnSlot.Type) : PlayerData = {
+ val appearance = basic_appearance( placementOffset(Some(pos)) )
+ PlayerData(Some(pos), appearance, character_data(appearance.backpack), None, hand_held)(true)
+ }
+
+ /**
+ * Determine the padding offset for a subsequent field given the existence of `PlacementData`.
+ * The padding will always be a number 0-7.
+ * @see `PlacemtnData`
+ * @param pos the optional `PlacementData` object that creates the shift in bits
+ * @return the pad length in bits
+ */
+ def placementOffset(pos : Option[PlacementData]) : Int = {
+ if(pos.isEmpty) {
+ 0
+ }
+ else if(pos.get.vel.isDefined) {
+ 2
+ }
+ else {
+ 4
+ }
+ }
+
+ def codec(position_defined : Boolean) : Codec[PlayerData] = (
+ conditional(position_defined, "pos" | PlacementData.codec) >>:~ { pos =>
+ ("basic_appearance" | CharacterAppearanceData.codec(placementOffset(pos))) >>:~ { app =>
+ ("character_data" | CharacterData.codec(app.backpack)) ::
+ optional(bool, "inventory" | InventoryData.codec) ::
+ ("drawn_slot" | DrawnSlot.codec) ::
+ bool //usually false
+ }
+ }).xmap[PlayerData] (
+ {
+ case pos :: app :: data :: inv :: hand :: _ :: HNil =>
+ PlayerData(pos, app, data, inv, hand)(pos.isDefined)
+ },
+ {
+ case PlayerData(pos, app, data, inv, hand) =>
+ pos :: app :: data :: inv :: hand :: false :: HNil
+ }
+ )
+
+ implicit val codec : Codec[PlayerData] = codec(false)
+}
diff --git a/common/src/test/scala/game/objectcreate/CharacterDataTest.scala b/common/src/test/scala/game/objectcreate/CharacterDataTest.scala
index 21d1cee08..843e61a2c 100644
--- a/common/src/test/scala/game/objectcreate/CharacterDataTest.scala
+++ b/common/src/test/scala/game/objectcreate/CharacterDataTest.scala
@@ -20,93 +20,92 @@ class CharacterDataTest extends Specification {
cls mustEqual ObjectClass.avatar
guid mustEqual PlanetSideGUID(3902)
parent.isDefined mustEqual false
- data.isDefined mustEqual true
- data.get.isInstanceOf[CharacterData] mustEqual true
- val pc = data.get.asInstanceOf[CharacterData]
- pc.appearance.pos.coord.x mustEqual 3674.8438f
- pc.appearance.pos.coord.y mustEqual 2726.789f
- pc.appearance.pos.coord.z mustEqual 91.15625f
- pc.appearance.pos.orient.x mustEqual 0f
- pc.appearance.pos.orient.y mustEqual 0f
- pc.appearance.pos.orient.z mustEqual 64.6875f
- pc.appearance.pos.vel.isDefined mustEqual true
- pc.appearance.pos.vel.get.x mustEqual 1.4375f
- pc.appearance.pos.vel.get.y mustEqual -0.4375f
- pc.appearance.pos.vel.get.z mustEqual 0f
- pc.appearance.basic_appearance.name mustEqual "ScrawnyRonnie"
- pc.appearance.basic_appearance.faction mustEqual PlanetSideEmpire.TR
- pc.appearance.basic_appearance.sex mustEqual CharacterGender.Male
- pc.appearance.basic_appearance.head mustEqual 5
- pc.appearance.basic_appearance.voice mustEqual 5
- pc.appearance.voice2 mustEqual 3
- pc.appearance.black_ops mustEqual false
- pc.appearance.jammered mustEqual false
- pc.appearance.exosuit mustEqual ExoSuitType.Reinforced
- pc.appearance.outfit_name mustEqual "Black Beret Armoured Corps"
- pc.appearance.outfit_logo mustEqual 23
- pc.appearance.facingPitch mustEqual 340.3125f
- pc.appearance.facingYawUpper mustEqual 0
- pc.appearance.lfs mustEqual false
- pc.appearance.grenade_state mustEqual GrenadeState.None
- pc.appearance.is_cloaking mustEqual false
- pc.appearance.charging_pose mustEqual false
- pc.appearance.on_zipline mustEqual false
- pc.appearance.ribbons.upper mustEqual MeritCommendation.MarkovVeteran
- pc.appearance.ribbons.middle mustEqual MeritCommendation.HeavyInfantry4
- pc.appearance.ribbons.lower mustEqual MeritCommendation.TankBuster7
- pc.appearance.ribbons.tos mustEqual MeritCommendation.SixYearTR
- pc.health mustEqual 255
- pc.armor mustEqual 253
- pc.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
- pc.command_rank mustEqual 5
- pc.implant_effects.isDefined mustEqual true
- pc.implant_effects.get mustEqual ImplantEffects.NoEffects
- pc.cosmetics.isDefined mustEqual true
- pc.cosmetics.get.no_helmet mustEqual true
- pc.cosmetics.get.beret mustEqual true
- pc.cosmetics.get.sunglasses mustEqual true
- pc.cosmetics.get.earpiece mustEqual true
- pc.cosmetics.get.brimmed_cap mustEqual false
- //short test of inventory items
- pc.inventory.isDefined mustEqual true
- val contents = pc.inventory.get.contents
- contents.size mustEqual 5
- //0
- contents.head.objectClass mustEqual ObjectClass.plasma_grenade
- contents.head.guid mustEqual PlanetSideGUID(3662)
- contents.head.parentSlot mustEqual 0
- contents.head.obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
- contents.head.obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.plasma_grenade_ammo
- contents.head.obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3751)
- //1
- contents(1).objectClass mustEqual ObjectClass.bank
- contents(1).guid mustEqual PlanetSideGUID(3908)
- contents(1).parentSlot mustEqual 1
- contents(1).obj.asInstanceOf[WeaponData].fire_mode mustEqual 1
- contents(1).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.armor_canister
- contents(1).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(4143)
- //2
- contents(2).objectClass mustEqual ObjectClass.mini_chaingun
- contents(2).guid mustEqual PlanetSideGUID(4164)
- contents(2).parentSlot mustEqual 2
- contents(2).obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
- contents(2).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.bullet_9mm
- contents(2).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3728)
- //3
- contents(3).objectClass mustEqual ObjectClass.phoenix //actually, a decimator
- contents(3).guid mustEqual PlanetSideGUID(3603)
- contents(3).parentSlot mustEqual 3
- contents(3).obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
- contents(3).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.phoenix_missile
- contents(3).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3056)
- //4
- contents(4).objectClass mustEqual ObjectClass.chainblade
- contents(4).guid mustEqual PlanetSideGUID(4088)
- contents(4).parentSlot mustEqual 4
- contents(4).obj.asInstanceOf[WeaponData].fire_mode mustEqual 1
- contents(4).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.melee_ammo
- contents(4).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3279)
- pc.drawn_slot mustEqual DrawnSlot.Rifle1
+ data match {
+ case Some(PlayerData(Some(pos), basic, char, inv, hand)) =>
+ pos.coord mustEqual Vector3(3674.8438f, 2726.789f, 91.15625f)
+ pos.orient mustEqual Vector3(0f, 0f, 64.6875f)
+ pos.vel.isDefined mustEqual true
+ pos.vel.get mustEqual Vector3(1.4375f, -0.4375f, 0f)
+
+ basic.app.name mustEqual "ScrawnyRonnie"
+ basic.app.faction mustEqual PlanetSideEmpire.TR
+ basic.app.sex mustEqual CharacterGender.Male
+ basic.app.head mustEqual 5
+ basic.app.voice mustEqual 5
+ basic.voice2 mustEqual 3
+ basic.black_ops mustEqual false
+ basic.jammered mustEqual false
+ basic.exosuit mustEqual ExoSuitType.Reinforced
+ basic.outfit_name mustEqual "Black Beret Armoured Corps"
+ basic.outfit_logo mustEqual 23
+ basic.facingPitch mustEqual 340.3125f
+ basic.facingYawUpper mustEqual 0
+ basic.lfs mustEqual false
+ basic.grenade_state mustEqual GrenadeState.None
+ basic.is_cloaking mustEqual false
+ basic.charging_pose mustEqual false
+ basic.on_zipline mustEqual false
+ basic.ribbons.upper mustEqual MeritCommendation.MarkovVeteran
+ basic.ribbons.middle mustEqual MeritCommendation.HeavyInfantry4
+ basic.ribbons.lower mustEqual MeritCommendation.TankBuster7
+ basic.ribbons.tos mustEqual MeritCommendation.SixYearTR
+
+ char.health mustEqual 255
+ char.armor mustEqual 253
+ char.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
+ char.command_rank mustEqual 5
+ char.implant_effects.isDefined mustEqual true
+ char.implant_effects.get mustEqual ImplantEffects.NoEffects
+ char.cosmetics.isDefined mustEqual true
+ char.cosmetics.get.no_helmet mustEqual true
+ char.cosmetics.get.beret mustEqual true
+ char.cosmetics.get.sunglasses mustEqual true
+ char.cosmetics.get.earpiece mustEqual true
+ char.cosmetics.get.brimmed_cap mustEqual false
+ //short test of inventory items
+ inv.isDefined mustEqual true
+ val contents = inv.get.contents
+ contents.size mustEqual 5
+ //0
+ contents.head.objectClass mustEqual ObjectClass.plasma_grenade
+ contents.head.guid mustEqual PlanetSideGUID(3662)
+ contents.head.parentSlot mustEqual 0
+ contents.head.obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
+ contents.head.obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.plasma_grenade_ammo
+ contents.head.obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3751)
+ //1
+ contents(1).objectClass mustEqual ObjectClass.bank
+ contents(1).guid mustEqual PlanetSideGUID(3908)
+ contents(1).parentSlot mustEqual 1
+ contents(1).obj.asInstanceOf[WeaponData].fire_mode mustEqual 1
+ contents(1).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.armor_canister
+ contents(1).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(4143)
+ //2
+ contents(2).objectClass mustEqual ObjectClass.mini_chaingun
+ contents(2).guid mustEqual PlanetSideGUID(4164)
+ contents(2).parentSlot mustEqual 2
+ contents(2).obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
+ contents(2).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.bullet_9mm
+ contents(2).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3728)
+ //3
+ contents(3).objectClass mustEqual ObjectClass.phoenix //actually, a decimator
+ contents(3).guid mustEqual PlanetSideGUID(3603)
+ contents(3).parentSlot mustEqual 3
+ contents(3).obj.asInstanceOf[WeaponData].fire_mode mustEqual 0
+ contents(3).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.phoenix_missile
+ contents(3).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3056)
+ //4
+ contents(4).objectClass mustEqual ObjectClass.chainblade
+ contents(4).guid mustEqual PlanetSideGUID(4088)
+ contents(4).parentSlot mustEqual 4
+ contents(4).obj.asInstanceOf[WeaponData].fire_mode mustEqual 1
+ contents(4).obj.asInstanceOf[WeaponData].ammo.head.objectClass mustEqual ObjectClass.melee_ammo
+ contents(4).obj.asInstanceOf[WeaponData].ammo.head.guid mustEqual PlanetSideGUID(3279)
+
+ hand mustEqual DrawnSlot.Rifle1
+ case _ =>
+ ko
+ }
case _ =>
ko
}
@@ -119,104 +118,105 @@ class CharacterDataTest extends Specification {
cls mustEqual ObjectClass.avatar
guid mustEqual PlanetSideGUID(3380)
parent.isDefined mustEqual false
- data.isDefined mustEqual true
- data.get.isInstanceOf[CharacterData] mustEqual true
- val pc = data.get.asInstanceOf[CharacterData]
- pc.appearance.pos.coord.x mustEqual 4629.8906f
- pc.appearance.pos.coord.y mustEqual 6316.4453f
- pc.appearance.pos.coord.z mustEqual 54.734375f
- pc.appearance.pos.orient.x mustEqual 0f
- pc.appearance.pos.orient.y mustEqual 0f
- pc.appearance.pos.orient.z mustEqual 126.5625f
- pc.appearance.pos.vel.isDefined mustEqual false
- pc.appearance.basic_appearance.name mustEqual "Angello"
- pc.appearance.basic_appearance.faction mustEqual PlanetSideEmpire.VS
- pc.appearance.basic_appearance.sex mustEqual CharacterGender.Male
- pc.appearance.basic_appearance.head mustEqual 10
- pc.appearance.basic_appearance.voice mustEqual 2
- pc.appearance.voice2 mustEqual 0
- pc.appearance.black_ops mustEqual false
- pc.appearance.jammered mustEqual false
- pc.appearance.exosuit mustEqual ExoSuitType.MAX
- pc.appearance.outfit_name mustEqual "Original District"
- pc.appearance.outfit_logo mustEqual 23
- pc.appearance.facingPitch mustEqual 0
- pc.appearance.facingYawUpper mustEqual 180.0f
- pc.appearance.lfs mustEqual false
- pc.appearance.grenade_state mustEqual GrenadeState.None
- pc.appearance.is_cloaking mustEqual false
- pc.appearance.charging_pose mustEqual false
- pc.appearance.on_zipline mustEqual false
- pc.appearance.ribbons.upper mustEqual MeritCommendation.Jacking2
- pc.appearance.ribbons.middle mustEqual MeritCommendation.ScavengerVS1
- pc.appearance.ribbons.lower mustEqual MeritCommendation.AMSSupport4
- pc.appearance.ribbons.tos mustEqual MeritCommendation.SixYearVS
- pc.health mustEqual 0
- pc.armor mustEqual 0
- pc.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
- pc.command_rank mustEqual 2
- pc.implant_effects.isDefined mustEqual false
- pc.cosmetics.isDefined mustEqual true
- pc.cosmetics.get.no_helmet mustEqual true
- pc.cosmetics.get.beret mustEqual true
- pc.cosmetics.get.sunglasses mustEqual true
- pc.cosmetics.get.earpiece mustEqual true
- pc.cosmetics.get.brimmed_cap mustEqual false
- pc.inventory.isDefined mustEqual false
- pc.drawn_slot mustEqual DrawnSlot.Pistol1
+ data match {
+ case Some(PlayerData(Some(pos), basic, char, None, hand)) =>
+ pos.coord mustEqual Vector3(4629.8906f, 6316.4453f, 54.734375f)
+ pos.orient mustEqual Vector3(0, 0, 126.5625f)
+ pos.vel.isDefined mustEqual false
+
+ basic.app.name mustEqual "Angello"
+ basic.app.faction mustEqual PlanetSideEmpire.VS
+ basic.app.sex mustEqual CharacterGender.Male
+ basic.app.head mustEqual 10
+ basic.app.voice mustEqual 2
+ basic.voice2 mustEqual 0
+ basic.black_ops mustEqual false
+ basic.jammered mustEqual false
+ basic.exosuit mustEqual ExoSuitType.MAX
+ basic.outfit_name mustEqual "Original District"
+ basic.outfit_logo mustEqual 23
+ basic.facingPitch mustEqual 0
+ basic.facingYawUpper mustEqual 180.0f
+ basic.lfs mustEqual false
+ basic.grenade_state mustEqual GrenadeState.None
+ basic.is_cloaking mustEqual false
+ basic.charging_pose mustEqual false
+ basic.on_zipline mustEqual false
+ basic.ribbons.upper mustEqual MeritCommendation.Jacking2
+ basic.ribbons.middle mustEqual MeritCommendation.ScavengerVS1
+ basic.ribbons.lower mustEqual MeritCommendation.AMSSupport4
+ basic.ribbons.tos mustEqual MeritCommendation.SixYearVS
+
+ char.health mustEqual 0
+ char.armor mustEqual 0
+ char.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
+ char.command_rank mustEqual 2
+ char.implant_effects.isDefined mustEqual false
+ char.cosmetics.isDefined mustEqual true
+ char.cosmetics.get.no_helmet mustEqual true
+ char.cosmetics.get.beret mustEqual true
+ char.cosmetics.get.sunglasses mustEqual true
+ char.cosmetics.get.earpiece mustEqual true
+ char.cosmetics.get.brimmed_cap mustEqual false
+
+ hand mustEqual DrawnSlot.Pistol1
+ case _ =>
+ ko
+ }
case _ =>
ko
}
}
"encode" in {
- val obj = CharacterData(
- CharacterAppearanceData(
- PlacementData(
- Vector3(3674.8438f, 2726.789f, 91.15625f),
- Vector3(0f, 0f, 64.6875f),
- Some(Vector3(1.4375f, -0.4375f, 0f))
- ),
- BasicCharacterData(
- "ScrawnyRonnie",
- PlanetSideEmpire.TR,
- CharacterGender.Male,
- 5,
- 5
- ),
- 3,
- false,
- false,
- ExoSuitType.Reinforced,
- "Black Beret Armoured Corps",
- 23,
- false,
- 340.3125f, 0f,
- false,
- GrenadeState.None,
- false, false, false,
- RibbonBars(
- MeritCommendation.MarkovVeteran,
- MeritCommendation.HeavyInfantry4,
- MeritCommendation.TankBuster7,
- MeritCommendation.SixYearTR
- )
+ val pos : PlacementData = PlacementData(
+ Vector3(3674.8438f, 2726.789f, 91.15625f),
+ Vector3(0f, 0f, 64.6875f),
+ Some(Vector3(1.4375f, -0.4375f, 0f))
+ )
+ val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
+ BasicCharacterData(
+ "ScrawnyRonnie",
+ PlanetSideEmpire.TR,
+ CharacterGender.Male,
+ 5,
+ 5
),
+ 3,
+ false,
+ false,
+ ExoSuitType.Reinforced,
+ "Black Beret Armoured Corps",
+ 23,
+ false,
+ 340.3125f, 0f,
+ false,
+ GrenadeState.None,
+ false, false, false,
+ RibbonBars(
+ MeritCommendation.MarkovVeteran,
+ MeritCommendation.HeavyInfantry4,
+ MeritCommendation.TankBuster7,
+ MeritCommendation.SixYearTR
+ )
+ )
+ val char : (Boolean)=>CharacterData = CharacterData(
255, 253,
UniformStyle.ThirdUpgrade,
5,
Some(ImplantEffects.NoEffects),
- Some(Cosmetics(true, true, true, true, false)),
- InventoryData(
- InventoryItemData(ObjectClass.plasma_grenade, PlanetSideGUID(3662), 0, WeaponData(0, 0, ObjectClass.plasma_grenade_ammo, PlanetSideGUID(3751), 0, AmmoBoxData())) ::
- InventoryItemData(ObjectClass.bank, PlanetSideGUID(3908), 1, WeaponData(0, 0, 1, ObjectClass.armor_canister, PlanetSideGUID(4143), 0, AmmoBoxData())) ::
- InventoryItemData(ObjectClass.mini_chaingun, PlanetSideGUID(4164), 2, WeaponData(0, 0, ObjectClass.bullet_9mm, PlanetSideGUID(3728), 0, AmmoBoxData())) ::
- InventoryItemData(ObjectClass.phoenix, PlanetSideGUID(3603), 3, WeaponData(0, 0, ObjectClass.phoenix_missile, PlanetSideGUID(3056), 0, AmmoBoxData())) ::
- InventoryItemData(ObjectClass.chainblade, PlanetSideGUID(4088), 4, WeaponData(0, 0, 1, ObjectClass.melee_ammo, PlanetSideGUID(3279), 0, AmmoBoxData())) ::
- Nil
- ),
- DrawnSlot.Rifle1
+ Some(Cosmetics(true, true, true, true, false))
)
+ val inv = InventoryData(
+ InventoryItemData(ObjectClass.plasma_grenade, PlanetSideGUID(3662), 0, WeaponData(0, 0, ObjectClass.plasma_grenade_ammo, PlanetSideGUID(3751), 0, AmmoBoxData())) ::
+ InventoryItemData(ObjectClass.bank, PlanetSideGUID(3908), 1, WeaponData(0, 0, 1, ObjectClass.armor_canister, PlanetSideGUID(4143), 0, AmmoBoxData())) ::
+ InventoryItemData(ObjectClass.mini_chaingun, PlanetSideGUID(4164), 2, WeaponData(0, 0, ObjectClass.bullet_9mm, PlanetSideGUID(3728), 0, AmmoBoxData())) ::
+ InventoryItemData(ObjectClass.phoenix, PlanetSideGUID(3603), 3, WeaponData(0, 0, ObjectClass.phoenix_missile, PlanetSideGUID(3056), 0, AmmoBoxData())) ::
+ InventoryItemData(ObjectClass.chainblade, PlanetSideGUID(4088), 4, WeaponData(0, 0, 1, ObjectClass.melee_ammo, PlanetSideGUID(3279), 0, AmmoBoxData())) ::
+ Nil
+ )
+ val obj = PlayerData.apply(pos, app, char, inv, DrawnSlot.Rifle1)
+
val msg = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(3902), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
val pkt_bitv = pkt.toBitVector
@@ -230,42 +230,45 @@ class CharacterDataTest extends Specification {
}
"encode (backpack)" in {
- val obj = CharacterData(
- CharacterAppearanceData(
- PlacementData(4629.8906f, 6316.4453f, 54.734375f, 0f, 0f, 126.5625f),
- BasicCharacterData(
- "Angello",
- PlanetSideEmpire.VS,
- CharacterGender.Male,
- 10,
- 2
- ),
- 0,
- false,
- false,
- ExoSuitType.MAX,
- "Original District",
- 23,
- true, //backpack
- 0f, 180.0f,
- false,
- GrenadeState.None,
- false, false, false,
- RibbonBars(
- MeritCommendation.Jacking2,
- MeritCommendation.ScavengerVS1,
- MeritCommendation.AMSSupport4,
- MeritCommendation.SixYearVS
- )
+ val pos = PlacementData(
+ Vector3(4629.8906f, 6316.4453f, 54.734375f),
+ Vector3(0, 0, 126.5625f)
+ )
+ val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
+ BasicCharacterData(
+ "Angello",
+ PlanetSideEmpire.VS,
+ CharacterGender.Male,
+ 10,
+ 2
),
+ 0,
+ false,
+ false,
+ ExoSuitType.MAX,
+ "Original District",
+ 23,
+ true, //backpack
+ 0f, 180.0f,
+ false,
+ GrenadeState.None,
+ false, false, false,
+ RibbonBars(
+ MeritCommendation.Jacking2,
+ MeritCommendation.ScavengerVS1,
+ MeritCommendation.AMSSupport4,
+ MeritCommendation.SixYearVS
+ )
+ )
+ val char : (Boolean)=>CharacterData = CharacterData(
0, 0,
UniformStyle.ThirdUpgrade,
2,
None,
- Some(Cosmetics(true, true, true, true, false)),
- None,
- DrawnSlot.Pistol1
+ Some(Cosmetics(true, true, true, true, false))
)
+ val obj = PlayerData.apply(pos, app, char, DrawnSlot.Pistol1)
+
val msg = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(3380), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
val pkt_bitv = pkt.toBitVector
diff --git a/common/src/test/scala/game/objectcreatedetailed/DetailedCharacterDataTest.scala b/common/src/test/scala/game/objectcreatedetailed/DetailedCharacterDataTest.scala
index 38a31b52d..b7fb703b4 100644
--- a/common/src/test/scala/game/objectcreatedetailed/DetailedCharacterDataTest.scala
+++ b/common/src/test/scala/game/objectcreatedetailed/DetailedCharacterDataTest.scala
@@ -20,130 +20,132 @@ class DetailedCharacterDataTest extends Specification {
cls mustEqual ObjectClass.avatar
guid mustEqual PlanetSideGUID(75)
parent.isDefined mustEqual false
- data.isDefined mustEqual true
- val char = data.get.asInstanceOf[DetailedCharacterData]
- char.appearance.pos.coord.x mustEqual 3674.8438f
- char.appearance.pos.coord.y mustEqual 2726.789f
- char.appearance.pos.coord.z mustEqual 91.15625f
- char.appearance.pos.orient.x mustEqual 0
- char.appearance.pos.orient.y mustEqual 0f
- char.appearance.pos.orient.z mustEqual 36.5625f
- char.appearance.basic_appearance.name mustEqual "IlllIIIlllIlIllIlllIllI"
- char.appearance.basic_appearance.faction mustEqual PlanetSideEmpire.VS
- char.appearance.basic_appearance.sex mustEqual CharacterGender.Female
- char.appearance.basic_appearance.head mustEqual 41
- char.appearance.basic_appearance.voice mustEqual 1 //female 1
- char.appearance.voice2 mustEqual 3
- char.appearance.black_ops mustEqual false
- char.appearance.jammered mustEqual false
- char.appearance.exosuit mustEqual ExoSuitType.Standard
- char.appearance.outfit_name mustEqual ""
- char.appearance.outfit_logo mustEqual 0
- char.appearance.backpack mustEqual false
- char.appearance.facingPitch mustEqual 2.8125f
- char.appearance.facingYawUpper mustEqual 210.9375f
- char.appearance.lfs mustEqual true
- char.appearance.grenade_state mustEqual GrenadeState.None
- char.appearance.is_cloaking mustEqual false
- char.appearance.charging_pose mustEqual false
- char.appearance.on_zipline mustEqual false
- char.appearance.ribbons.upper mustEqual MeritCommendation.None
- char.appearance.ribbons.middle mustEqual MeritCommendation.None
- char.appearance.ribbons.lower mustEqual MeritCommendation.None
- char.appearance.ribbons.tos mustEqual MeritCommendation.None
- char.bep mustEqual 0
- char.cep mustEqual 0
- char.healthMax mustEqual 100
- char.health mustEqual 100
- char.armor mustEqual 50 //standard exosuit value
- char.unk1 mustEqual 1
- char.unk2 mustEqual 7
- char.unk3 mustEqual 7
- char.staminaMax mustEqual 100
- char.stamina mustEqual 100
- char.certs.length mustEqual 7
- char.certs.head mustEqual CertificationType.StandardAssault
- char.certs(1) mustEqual CertificationType.MediumAssault
- char.certs(2) mustEqual CertificationType.ATV
- char.certs(3) mustEqual CertificationType.Harasser
- char.certs(4) mustEqual CertificationType.StandardExoSuit
- char.certs(5) mustEqual CertificationType.AgileExoSuit
- char.certs(6) mustEqual CertificationType.ReinforcedExoSuit
- char.implants.length mustEqual 0
- char.firstTimeEvents.size mustEqual 4
- char.firstTimeEvents.head mustEqual "xpe_sanctuary_help"
- char.firstTimeEvents(1) mustEqual "xpe_th_firemodes"
- char.firstTimeEvents(2) mustEqual "used_beamer"
- char.firstTimeEvents(3) mustEqual "map13"
- char.tutorials.size mustEqual 0
- char.cosmetics.isDefined mustEqual false
- char.inventory.isDefined mustEqual true
- val inventory = char.inventory.get.contents
- inventory.size mustEqual 10
- //0
- inventory.head.objectClass mustEqual ObjectClass.beamer
- inventory.head.guid mustEqual PlanetSideGUID(76)
- inventory.head.parentSlot mustEqual 0
- var wep = inventory.head.obj.asInstanceOf[DetailedWeaponData]
- wep.ammo.head.objectClass mustEqual ObjectClass.energy_cell
- wep.ammo.head.guid mustEqual PlanetSideGUID(77)
- wep.ammo.head.parentSlot mustEqual 0
- wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 16
- //1
- inventory(1).objectClass mustEqual ObjectClass.suppressor
- inventory(1).guid mustEqual PlanetSideGUID(78)
- inventory(1).parentSlot mustEqual 2
- wep = inventory(1).obj.asInstanceOf[DetailedWeaponData]
- wep.ammo.head.objectClass mustEqual ObjectClass.bullet_9mm
- wep.ammo.head.guid mustEqual PlanetSideGUID(79)
- wep.ammo.head.parentSlot mustEqual 0
- wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 25
- //2
- inventory(2).objectClass mustEqual ObjectClass.forceblade
- inventory(2).guid mustEqual PlanetSideGUID(80)
- inventory(2).parentSlot mustEqual 4
- wep = inventory(2).obj.asInstanceOf[DetailedWeaponData]
- wep.ammo.head.objectClass mustEqual ObjectClass.melee_ammo
- wep.ammo.head.guid mustEqual PlanetSideGUID(81)
- wep.ammo.head.parentSlot mustEqual 0
- wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 1
- //3
- inventory(3).objectClass mustEqual ObjectClass.locker_container
- inventory(3).guid mustEqual PlanetSideGUID(82)
- inventory(3).parentSlot mustEqual 5
- inventory(3).obj.isInstanceOf[DetailedLockerContainerData] mustEqual true
- inventory(3).obj.asInstanceOf[DetailedLockerContainerData].inventory.isDefined mustEqual false
- //4
- inventory(4).objectClass mustEqual ObjectClass.bullet_9mm
- inventory(4).guid mustEqual PlanetSideGUID(83)
- inventory(4).parentSlot mustEqual 6
- inventory(4).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
- //5
- inventory(5).objectClass mustEqual ObjectClass.bullet_9mm
- inventory(5).guid mustEqual PlanetSideGUID(84)
- inventory(5).parentSlot mustEqual 9
- inventory(5).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
- //6
- inventory(6).objectClass mustEqual ObjectClass.bullet_9mm
- inventory(6).guid mustEqual PlanetSideGUID(85)
- inventory(6).parentSlot mustEqual 12
- inventory(6).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
- //7
- inventory(7).objectClass mustEqual ObjectClass.bullet_9mm_AP
- inventory(7).guid mustEqual PlanetSideGUID(86)
- inventory(7).parentSlot mustEqual 33
- inventory(7).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
- //8
- inventory(8).objectClass mustEqual ObjectClass.energy_cell
- inventory(8).guid mustEqual PlanetSideGUID(87)
- inventory(8).parentSlot mustEqual 36
- inventory(8).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
- //9
- inventory(9).objectClass mustEqual ObjectClass.remote_electronics_kit
- inventory(9).guid mustEqual PlanetSideGUID(88)
- inventory(9).parentSlot mustEqual 39
- //the rek has data but none worth testing here
- char.drawn_slot mustEqual DrawnSlot.Pistol1
+ data match {
+ case Some(DetailedPlayerData(Some(pos), basic, char, inv, hand)) =>
+ pos.coord mustEqual Vector3(3674.8438f, 2726.789f, 91.15625f)
+ pos.orient mustEqual Vector3(0, 0, 36.5625f)
+ pos.vel.isDefined mustEqual false
+
+ basic.app.name mustEqual "IlllIIIlllIlIllIlllIllI"
+ basic.app.faction mustEqual PlanetSideEmpire.VS
+ basic.app.sex mustEqual CharacterGender.Female
+ basic.app.head mustEqual 41
+ basic.app.voice mustEqual 1 //female 1
+ basic.voice2 mustEqual 3
+ basic.black_ops mustEqual false
+ basic.jammered mustEqual false
+ basic.exosuit mustEqual ExoSuitType.Standard
+ basic.outfit_name mustEqual ""
+ basic.outfit_logo mustEqual 0
+ basic.backpack mustEqual false
+ basic.facingPitch mustEqual 2.8125f
+ basic.facingYawUpper mustEqual 210.9375f
+ basic.lfs mustEqual true
+ basic.grenade_state mustEqual GrenadeState.None
+ basic.is_cloaking mustEqual false
+ basic.charging_pose mustEqual false
+ basic.on_zipline mustEqual false
+ basic.ribbons.upper mustEqual MeritCommendation.None
+ basic.ribbons.middle mustEqual MeritCommendation.None
+ basic.ribbons.lower mustEqual MeritCommendation.None
+ basic.ribbons.tos mustEqual MeritCommendation.None
+
+ char.bep mustEqual 0
+ char.cep mustEqual 0
+ char.healthMax mustEqual 100
+ char.health mustEqual 100
+ char.armor mustEqual 50 //standard exosuit value
+ char.unk1 mustEqual 1
+ char.unk2 mustEqual 7
+ char.unk3 mustEqual 7
+ char.staminaMax mustEqual 100
+ char.stamina mustEqual 100
+ char.certs.length mustEqual 7
+ char.certs.head mustEqual CertificationType.StandardAssault
+ char.certs(1) mustEqual CertificationType.MediumAssault
+ char.certs(2) mustEqual CertificationType.ATV
+ char.certs(3) mustEqual CertificationType.Harasser
+ char.certs(4) mustEqual CertificationType.StandardExoSuit
+ char.certs(5) mustEqual CertificationType.AgileExoSuit
+ char.certs(6) mustEqual CertificationType.ReinforcedExoSuit
+ char.implants.length mustEqual 0
+ char.firstTimeEvents.size mustEqual 4
+ char.firstTimeEvents.head mustEqual "xpe_sanctuary_help"
+ char.firstTimeEvents(1) mustEqual "xpe_th_firemodes"
+ char.firstTimeEvents(2) mustEqual "used_beamer"
+ char.firstTimeEvents(3) mustEqual "map13"
+ char.tutorials.size mustEqual 0
+ char.cosmetics.isDefined mustEqual false
+ inv.isDefined mustEqual true
+ val inventory = inv.get.contents
+ inventory.size mustEqual 10
+ //0
+ inventory.head.objectClass mustEqual ObjectClass.beamer
+ inventory.head.guid mustEqual PlanetSideGUID(76)
+ inventory.head.parentSlot mustEqual 0
+ var wep = inventory.head.obj.asInstanceOf[DetailedWeaponData]
+ wep.ammo.head.objectClass mustEqual ObjectClass.energy_cell
+ wep.ammo.head.guid mustEqual PlanetSideGUID(77)
+ wep.ammo.head.parentSlot mustEqual 0
+ wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 16
+ //1
+ inventory(1).objectClass mustEqual ObjectClass.suppressor
+ inventory(1).guid mustEqual PlanetSideGUID(78)
+ inventory(1).parentSlot mustEqual 2
+ wep = inventory(1).obj.asInstanceOf[DetailedWeaponData]
+ wep.ammo.head.objectClass mustEqual ObjectClass.bullet_9mm
+ wep.ammo.head.guid mustEqual PlanetSideGUID(79)
+ wep.ammo.head.parentSlot mustEqual 0
+ wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 25
+ //2
+ inventory(2).objectClass mustEqual ObjectClass.forceblade
+ inventory(2).guid mustEqual PlanetSideGUID(80)
+ inventory(2).parentSlot mustEqual 4
+ wep = inventory(2).obj.asInstanceOf[DetailedWeaponData]
+ wep.ammo.head.objectClass mustEqual ObjectClass.melee_ammo
+ wep.ammo.head.guid mustEqual PlanetSideGUID(81)
+ wep.ammo.head.parentSlot mustEqual 0
+ wep.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 1
+ //3
+ inventory(3).objectClass mustEqual ObjectClass.locker_container
+ inventory(3).guid mustEqual PlanetSideGUID(82)
+ inventory(3).parentSlot mustEqual 5
+ inventory(3).obj.isInstanceOf[DetailedLockerContainerData] mustEqual true
+ inventory(3).obj.asInstanceOf[DetailedLockerContainerData].inventory.isDefined mustEqual false
+ //4
+ inventory(4).objectClass mustEqual ObjectClass.bullet_9mm
+ inventory(4).guid mustEqual PlanetSideGUID(83)
+ inventory(4).parentSlot mustEqual 6
+ inventory(4).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
+ //5
+ inventory(5).objectClass mustEqual ObjectClass.bullet_9mm
+ inventory(5).guid mustEqual PlanetSideGUID(84)
+ inventory(5).parentSlot mustEqual 9
+ inventory(5).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
+ //6
+ inventory(6).objectClass mustEqual ObjectClass.bullet_9mm
+ inventory(6).guid mustEqual PlanetSideGUID(85)
+ inventory(6).parentSlot mustEqual 12
+ inventory(6).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
+ //7
+ inventory(7).objectClass mustEqual ObjectClass.bullet_9mm_AP
+ inventory(7).guid mustEqual PlanetSideGUID(86)
+ inventory(7).parentSlot mustEqual 33
+ inventory(7).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
+ //8
+ inventory(8).objectClass mustEqual ObjectClass.energy_cell
+ inventory(8).guid mustEqual PlanetSideGUID(87)
+ inventory(8).parentSlot mustEqual 36
+ inventory(8).obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 50
+ //9
+ inventory(9).objectClass mustEqual ObjectClass.remote_electronics_kit
+ inventory(9).guid mustEqual PlanetSideGUID(88)
+ inventory(9).parentSlot mustEqual 39
+ //the rek has data but none worth testing here
+ hand mustEqual DrawnSlot.Pistol1
+ case _ =>
+ ko
+ }
case _ =>
ko
}
@@ -155,76 +157,80 @@ class DetailedCharacterDataTest extends Specification {
//this test is mainly for an alternate bitstream parsing order
//the object produced is massive and most of it is already covered in other tests
//only certain details towards the end of the stream will be checked
- data.isDefined mustEqual true
- val char = data.get.asInstanceOf[DetailedCharacterData]
- DetailedCharacterData.isBR24(char.bep) mustEqual true
- char.certs.size mustEqual 15
- char.certs.head mustEqual CertificationType.StandardAssault
- char.certs(14) mustEqual CertificationType.CombatEngineering
- char.implants.size mustEqual 3
- char.implants.head.implant mustEqual ImplantType.AudioAmplifier
- char.implants.head.activation mustEqual None
- char.implants(1).implant mustEqual ImplantType.Targeting
- char.implants(1).activation mustEqual None
- char.implants(2).implant mustEqual ImplantType.Surge
- char.implants(2).activation mustEqual None
- char.firstTimeEvents.size mustEqual 298
- char.firstTimeEvents.head mustEqual "xpe_overhead_map"
- char.firstTimeEvents(297) mustEqual "map10"
- char.tutorials.size mustEqual 3
- char.tutorials.head mustEqual "training_start_nc"
- char.tutorials(1) mustEqual "training_ui"
- char.tutorials(2) mustEqual "training_map"
- char.cosmetics.isDefined mustEqual true
- char.cosmetics.get.no_helmet mustEqual true
- char.cosmetics.get.beret mustEqual true
- char.cosmetics.get.earpiece mustEqual true
- char.cosmetics.get.sunglasses mustEqual true
- char.cosmetics.get.brimmed_cap mustEqual false
- //inventory
- char.inventory.isDefined mustEqual true
- char.inventory.get.contents.size mustEqual 12
- //0
- char.inventory.get.contents.head.objectClass mustEqual 531
- char.inventory.get.contents.head.guid mustEqual PlanetSideGUID(4202)
- char.inventory.get.contents.head.parentSlot mustEqual 0
- val wep1 = char.inventory.get.contents.head.obj.asInstanceOf[DetailedWeaponData]
- wep1.unk1 mustEqual 2
- wep1.unk2 mustEqual 8
- wep1.ammo.head.objectClass mustEqual 389
- wep1.ammo.head.guid mustEqual PlanetSideGUID(3942)
- wep1.ammo.head.parentSlot mustEqual 0
- wep1.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].unk mustEqual 8
- wep1.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 100
- //4
- char.inventory.get.contents(4).objectClass mustEqual 456
- char.inventory.get.contents(4).guid mustEqual PlanetSideGUID(5374)
- char.inventory.get.contents(4).parentSlot mustEqual 5
- char.inventory.get.contents(4).obj.asInstanceOf[DetailedLockerContainerData].inventory.get.contents.size mustEqual 61
- //11
- char.inventory.get.contents(11).objectClass mustEqual 673
- char.inventory.get.contents(11).guid mustEqual PlanetSideGUID(3661)
- char.inventory.get.contents(11).parentSlot mustEqual 60
- val wep2 = char.inventory.get.contents(11).obj.asInstanceOf[DetailedWeaponData]
- wep2.unk1 mustEqual 2
- wep2.unk2 mustEqual 8
- wep2.ammo.head.objectClass mustEqual 674
- wep2.ammo.head.guid mustEqual PlanetSideGUID(8542)
- wep2.ammo.head.parentSlot mustEqual 0
- wep2.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].unk mustEqual 8
- wep2.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 3
- char.drawn_slot mustEqual DrawnSlot.None
+ data match {
+ case Some(DetailedPlayerData(Some(_), _, char, inv, hand)) =>
+ DetailedCharacterData.isBR24(char.bep) mustEqual true
+ char.certs.size mustEqual 15
+ char.certs.head mustEqual CertificationType.StandardAssault
+ char.certs(14) mustEqual CertificationType.CombatEngineering
+ char.implants.size mustEqual 3
+ char.implants.head.implant mustEqual ImplantType.AudioAmplifier
+ char.implants.head.activation mustEqual None
+ char.implants(1).implant mustEqual ImplantType.Targeting
+ char.implants(1).activation mustEqual None
+ char.implants(2).implant mustEqual ImplantType.Surge
+ char.implants(2).activation mustEqual None
+ char.firstTimeEvents.size mustEqual 298
+ char.firstTimeEvents.head mustEqual "xpe_overhead_map"
+ char.firstTimeEvents(297) mustEqual "map10"
+ char.tutorials.size mustEqual 3
+ char.tutorials.head mustEqual "training_start_nc"
+ char.tutorials(1) mustEqual "training_ui"
+ char.tutorials(2) mustEqual "training_map"
+ char.cosmetics.isDefined mustEqual true
+ char.cosmetics.get.no_helmet mustEqual true
+ char.cosmetics.get.beret mustEqual true
+ char.cosmetics.get.earpiece mustEqual true
+ char.cosmetics.get.sunglasses mustEqual true
+ char.cosmetics.get.brimmed_cap mustEqual false
+ //inventory
+ inv.isDefined mustEqual true
+ inv.get.contents.size mustEqual 12
+ //0
+ inv.get.contents.head.objectClass mustEqual 531
+ inv.get.contents.head.guid mustEqual PlanetSideGUID(4202)
+ inv.get.contents.head.parentSlot mustEqual 0
+ val wep1 = inv.get.contents.head.obj.asInstanceOf[DetailedWeaponData]
+ wep1.unk1 mustEqual 2
+ wep1.unk2 mustEqual 8
+ wep1.ammo.head.objectClass mustEqual 389
+ wep1.ammo.head.guid mustEqual PlanetSideGUID(3942)
+ wep1.ammo.head.parentSlot mustEqual 0
+ wep1.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].unk mustEqual 8
+ wep1.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 100
+ //4
+ inv.get.contents(4).objectClass mustEqual 456
+ inv.get.contents(4).guid mustEqual PlanetSideGUID(5374)
+ inv.get.contents(4).parentSlot mustEqual 5
+ inv.get.contents(4).obj.asInstanceOf[DetailedLockerContainerData].inventory.get.contents.size mustEqual 61
+ //11
+ inv.get.contents(11).objectClass mustEqual 673
+ inv.get.contents(11).guid mustEqual PlanetSideGUID(3661)
+ inv.get.contents(11).parentSlot mustEqual 60
+ val wep2 = inv.get.contents(11).obj.asInstanceOf[DetailedWeaponData]
+ wep2.unk1 mustEqual 2
+ wep2.unk2 mustEqual 8
+ wep2.ammo.head.objectClass mustEqual 674
+ wep2.ammo.head.guid mustEqual PlanetSideGUID(8542)
+ wep2.ammo.head.parentSlot mustEqual 0
+ wep2.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].unk mustEqual 8
+ wep2.ammo.head.obj.asInstanceOf[DetailedAmmoBoxData].magazine mustEqual 3
+
+ hand mustEqual DrawnSlot.None
+ case _ =>
+ ko
+ }
case _ =>
ko
}
}
"encode (character)" in {
- val app = CharacterAppearanceData(
- PlacementData(
- Vector3(3674.8438f, 2726.789f, 91.15625f),
- Vector3(0f, 0f, 36.5625f)
- ),
+ val pos : PlacementData = PlacementData(
+ 3674.8438f, 2726.789f, 91.15625f,
+ 0, 0, 36.5625f
+ )
+ val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
BasicCharacterData(
"IlllIIIlllIlIllIlllIllI",
PlanetSideEmpire.VS,
@@ -247,19 +253,7 @@ class DetailedCharacterDataTest extends Specification {
false,
RibbonBars()
)
- val inv = InventoryItemData(ObjectClass.beamer, PlanetSideGUID(76), 0, DetailedWeaponData(4, 8, ObjectClass.energy_cell, PlanetSideGUID(77), 0, DetailedAmmoBoxData(8, 16))) ::
- InventoryItemData(ObjectClass.suppressor, PlanetSideGUID(78), 2, DetailedWeaponData(4, 8, ObjectClass.bullet_9mm, PlanetSideGUID(79), 0, DetailedAmmoBoxData(8, 25))) ::
- InventoryItemData(ObjectClass.forceblade, PlanetSideGUID(80), 4, DetailedWeaponData(4, 8, ObjectClass.melee_ammo, PlanetSideGUID(81), 0, DetailedAmmoBoxData(8, 1))) ::
- InventoryItemData(ObjectClass.locker_container, PlanetSideGUID(82), 5, DetailedLockerContainerData(8)) ::
- InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(83), 6, DetailedAmmoBoxData(8, 50)) ::
- InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(84), 9, DetailedAmmoBoxData(8, 50)) ::
- InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(85), 12, DetailedAmmoBoxData(8, 50)) ::
- InventoryItemData(ObjectClass.bullet_9mm_AP, PlanetSideGUID(86), 33, DetailedAmmoBoxData(8, 50)) ::
- InventoryItemData(ObjectClass.energy_cell, PlanetSideGUID(87), 36, DetailedAmmoBoxData(8, 50)) ::
- InventoryItemData(ObjectClass.remote_electronics_kit, PlanetSideGUID(88), 39, DetailedREKData(8)) ::
- Nil
- val obj = DetailedCharacterData(
- app,
+ val char : (Option[Int])=>DetailedCharacterData = DetailedCharacterData(
0,
0,
100, 100,
@@ -278,13 +272,25 @@ class DetailedCharacterDataTest extends Specification {
List(),
"xpe_sanctuary_help" :: "xpe_th_firemodes" :: "used_beamer" :: "map13" :: Nil,
List.empty,
- None,
- Some(InventoryData(inv)),
- DrawnSlot.Pistol1
+ None
)
+ val inv = InventoryData(
+ InventoryItemData(ObjectClass.beamer, PlanetSideGUID(76), 0, DetailedWeaponData(4, 8, ObjectClass.energy_cell, PlanetSideGUID(77), 0, DetailedAmmoBoxData(8, 16))) ::
+ InventoryItemData(ObjectClass.suppressor, PlanetSideGUID(78), 2, DetailedWeaponData(4, 8, ObjectClass.bullet_9mm, PlanetSideGUID(79), 0, DetailedAmmoBoxData(8, 25))) ::
+ InventoryItemData(ObjectClass.forceblade, PlanetSideGUID(80), 4, DetailedWeaponData(4, 8, ObjectClass.melee_ammo, PlanetSideGUID(81), 0, DetailedAmmoBoxData(8, 1))) ::
+ InventoryItemData(ObjectClass.locker_container, PlanetSideGUID(82), 5, DetailedLockerContainerData(8)) ::
+ InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(83), 6, DetailedAmmoBoxData(8, 50)) ::
+ InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(84), 9, DetailedAmmoBoxData(8, 50)) ::
+ InventoryItemData(ObjectClass.bullet_9mm, PlanetSideGUID(85), 12, DetailedAmmoBoxData(8, 50)) ::
+ InventoryItemData(ObjectClass.bullet_9mm_AP, PlanetSideGUID(86), 33, DetailedAmmoBoxData(8, 50)) ::
+ InventoryItemData(ObjectClass.energy_cell, PlanetSideGUID(87), 36, DetailedAmmoBoxData(8, 50)) ::
+ InventoryItemData(ObjectClass.remote_electronics_kit, PlanetSideGUID(88), 39, DetailedREKData(8)) ::
+ Nil
+ )
+ val obj = DetailedPlayerData.apply(pos, app, char, inv, DrawnSlot.Pistol1)
+
val msg = ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
-
val pkt_bitv = pkt.toBitVector
val ori_bitv = string_testchar.toBitVector
pkt_bitv.take(153) mustEqual ori_bitv.take(153) //skip 1
@@ -295,26 +301,31 @@ class DetailedCharacterDataTest extends Specification {
}
"encode (character, br32)" in {
- val obj = DetailedCharacterData(
- CharacterAppearanceData(
- PlacementData(
- Vector3(5500.0f, 3800.0f, 71.484375f),
- Vector3(0.0f, 0.0f, 90.0f),
- None
- ),
- BasicCharacterData("KiCkJr", PlanetSideEmpire.NC, CharacterGender.Male, 24, 4),
- 3,
- false, false,
- ExoSuitType.Agile,
- "",
- 14,
- false,
- 354.375f, 354.375f,
- false,
- GrenadeState.None,
- false, false, false,
- RibbonBars(MeritCommendation.Loser4, MeritCommendation.EventNCElite, MeritCommendation.HeavyAssault6, MeritCommendation.SixYearNC)
- ),
+ val pos : PlacementData = PlacementData(
+ Vector3(5500.0f, 3800.0f, 71.484375f),
+ Vector3(0, 0, 90.0f),
+ None
+ )
+ val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
+ BasicCharacterData("KiCkJr", PlanetSideEmpire.NC, CharacterGender.Male, 24, 4),
+ 3,
+ false, false,
+ ExoSuitType.Agile,
+ "",
+ 14,
+ false,
+ 354.375f, 354.375f,
+ false,
+ GrenadeState.None,
+ false, false, false,
+ RibbonBars(
+ MeritCommendation.Loser4,
+ MeritCommendation.EventNCElite,
+ MeritCommendation.HeavyAssault6,
+ MeritCommendation.SixYearNC
+ )
+ )
+ val char : (Option[Int])=>DetailedCharacterData = DetailedCharacterData(
6366766,
694787,
100, 100, 100,
@@ -647,198 +658,196 @@ class DetailedCharacterDataTest extends Specification {
"training_ui",
"training_map"
),
- Some(Cosmetics(true, true, true, true, false)),
- Some(
- InventoryData(
- List(
- InternalSlot(531, PlanetSideGUID(4202), 0,
- DetailedWeaponData(2, 8, 0, List(InternalSlot(389, PlanetSideGUID(3942), 0,DetailedAmmoBoxData(8, 100))))
- ),
- InternalSlot(132, PlanetSideGUID(6924), 1,
- DetailedWeaponData(2, 8, 0, List(InternalSlot(111, PlanetSideGUID(9157), 0, DetailedAmmoBoxData(8, 100))))
- ),
- InternalSlot(714, PlanetSideGUID(8498), 2,
- DetailedWeaponData(2, 8, 0, List(InternalSlot(755, PlanetSideGUID(5356), 0, DetailedAmmoBoxData(8, 16))))
- ),
- InternalSlot(468, PlanetSideGUID(7198), 4,
- DetailedWeaponData(2, 8, 0, List(InternalSlot(540, PlanetSideGUID(5009), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(456, PlanetSideGUID(5374), 5,
- DetailedLockerContainerData(8, Some(InventoryData(List(
- InternalSlot(429, PlanetSideGUID(3021), 0,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(272, PlanetSideGUID(8729), 0, DetailedAmmoBoxData(8, 0))))
- ),
- InternalSlot(838, PlanetSideGUID(8467), 9,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(839, PlanetSideGUID(8603), 0, DetailedAmmoBoxData(8, 5))))
- ),
- InternalSlot(272, PlanetSideGUID(3266), 18, DetailedAmmoBoxData(8, 27)),
- InternalSlot(577, PlanetSideGUID(2934), 22,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(111, PlanetSideGUID(4682), 0, DetailedAmmoBoxData(8, 100))))
- ),
- InternalSlot(839, PlanetSideGUID(3271), 90, DetailedAmmoBoxData(8, 15)),
- InternalSlot(839, PlanetSideGUID(7174), 94, DetailedAmmoBoxData(8, 6)),
- InternalSlot(429, PlanetSideGUID(6084), 98,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(272, PlanetSideGUID(5928), 0, DetailedAmmoBoxData(8, 35))))
- ),
- InternalSlot(462, PlanetSideGUID(5000), 108,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(463, PlanetSideGUID(6277), 0, DetailedAmmoBoxData(8, 150))))
- ),
- InternalSlot(429, PlanetSideGUID(4341), 189,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(272, PlanetSideGUID(7043), 0, DetailedAmmoBoxData(8, 35))))
- ),
- InternalSlot(556, PlanetSideGUID(4168), 198,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(28, PlanetSideGUID(8937), 0, DetailedAmmoBoxData(8, 100))))
- ),
- InternalSlot(272, PlanetSideGUID(3173), 207, DetailedAmmoBoxData(8, 50)),
- InternalSlot(462, PlanetSideGUID(3221), 210,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(463, PlanetSideGUID(4031), 0, DetailedAmmoBoxData(8, 150))))
- ),
- InternalSlot(556, PlanetSideGUID(6853), 280,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(29, PlanetSideGUID(8524), 0, DetailedAmmoBoxData(8, 67))))
- ),
- InternalSlot(556, PlanetSideGUID(4569), 290,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(28, PlanetSideGUID(5584), 0, DetailedAmmoBoxData(8, 100))))
- ),
- InternalSlot(462, PlanetSideGUID(9294), 300,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(463, PlanetSideGUID(3118), 0, DetailedAmmoBoxData(8, 150))))
- ),
- InternalSlot(272, PlanetSideGUID(4759), 387, DetailedAmmoBoxData(8, 50)),
- InternalSlot(462, PlanetSideGUID(7377), 390,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(463, PlanetSideGUID(8155), 0, DetailedAmmoBoxData(8, 150))))
- ),
- InternalSlot(843, PlanetSideGUID(6709), 480, DetailedAmmoBoxData(8, 1)),
- InternalSlot(843, PlanetSideGUID(5276), 484, DetailedAmmoBoxData(8, 1)),
- InternalSlot(843, PlanetSideGUID(7769), 488, DetailedAmmoBoxData(8, 1)),
- InternalSlot(844, PlanetSideGUID(5334), 492, DetailedAmmoBoxData(8, 1)),
- InternalSlot(844, PlanetSideGUID(6219), 496, DetailedAmmoBoxData(8, 1)),
- InternalSlot(842, PlanetSideGUID(7279), 500, DetailedAmmoBoxData(8, 1)),
- InternalSlot(842, PlanetSideGUID(5415), 504, DetailedAmmoBoxData(8, 1)),
- InternalSlot(175, PlanetSideGUID(5741), 540,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(5183), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(324, PlanetSideGUID(6208), 541,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(5029), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(324, PlanetSideGUID(8589), 542,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(9217), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(175, PlanetSideGUID(8901), 543,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7633), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(175, PlanetSideGUID(8419), 544,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(6546), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(175, PlanetSideGUID(4715), 545,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(8453), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(324, PlanetSideGUID(3577), 546,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(9202), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(324, PlanetSideGUID(6003), 547,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(3260), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(324, PlanetSideGUID(9140), 548,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(3815),0,DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(324, PlanetSideGUID(4913), 549,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(7222),0,DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(324, PlanetSideGUID(6954), 550,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(2953),0,DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(324, PlanetSideGUID(6405), 551,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(4676),0,DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(324, PlanetSideGUID(8915), 552,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(4018),0,DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(324, PlanetSideGUID(4993), 553,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(6775),0,DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(175, PlanetSideGUID(5053), 554,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(6418),0,DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(324, PlanetSideGUID(9244), 555,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(3327),0,DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(468, PlanetSideGUID(6292), 556,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(6918),0,DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(842, PlanetSideGUID(5357), 558, DetailedAmmoBoxData(8, 1)),
- InternalSlot(844, PlanetSideGUID(4435), 562, DetailedAmmoBoxData(8, 1)),
- InternalSlot(843, PlanetSideGUID(7242), 566, DetailedAmmoBoxData(8, 1)),
- InternalSlot(175, PlanetSideGUID(7330), 570,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(4786), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(468, PlanetSideGUID(7415), 571,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(6536), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(175, PlanetSideGUID(3949), 572,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7526), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(175, PlanetSideGUID(3805), 573,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7358), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(324, PlanetSideGUID(4493), 574,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(6852), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(324, PlanetSideGUID(5762), 575,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(3463), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(175, PlanetSideGUID(3315), 576,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7619), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(324, PlanetSideGUID(6263), 577,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(5912), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(468, PlanetSideGUID(4028), 578,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(8021), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(175, PlanetSideGUID(2843), 579,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7250), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(175, PlanetSideGUID(9143), 580,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(5195), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(468, PlanetSideGUID(5024), 581,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(4287), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(468, PlanetSideGUID(6582), 582,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(4915), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(468, PlanetSideGUID(6425), 583,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(8872), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(468, PlanetSideGUID(4431), 584,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(4191), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(175, PlanetSideGUID(8339), 585,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7317), 0, DetailedAmmoBoxData(8, 1))))
- ),
- InternalSlot(175, PlanetSideGUID(3277), 586,
- DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(6469), 0, DetailedAmmoBoxData(8, 1))))
- )
- ))))
- ),
- InternalSlot(213, PlanetSideGUID(6877), 6, DetailedCommandDetonaterData(4, 8)),
- InternalSlot(755, PlanetSideGUID(6227), 9, DetailedAmmoBoxData(8, 16)),
- InternalSlot(728, PlanetSideGUID(7181), 12, DetailedREKData(4, 16)),
- InternalSlot(536, PlanetSideGUID(4077), 33, DetailedAmmoBoxData(8, 1)),
- InternalSlot(680, PlanetSideGUID(4377), 37,
- DetailedWeaponData(2, 8, 0, List(InternalSlot(681, PlanetSideGUID(8905), 0, DetailedAmmoBoxData(8, 3))))
- ),
- InternalSlot(32, PlanetSideGUID(5523), 39, DetailedACEData(4)),
- InternalSlot(673, PlanetSideGUID(3661), 60,
- DetailedWeaponData(2, 8, 0, List(InternalSlot(674, PlanetSideGUID(8542), 0, DetailedAmmoBoxData(8, 3))))
- )
- )
- )
- ),
- DrawnSlot.None
+ Some(Cosmetics(true, true, true, true, false))
)
+ val inv = InventoryData(
+ List(
+ InternalSlot(531, PlanetSideGUID(4202), 0,
+ DetailedWeaponData(2, 8, 0, List(InternalSlot(389, PlanetSideGUID(3942), 0,DetailedAmmoBoxData(8, 100))))
+ ),
+ InternalSlot(132, PlanetSideGUID(6924), 1,
+ DetailedWeaponData(2, 8, 0, List(InternalSlot(111, PlanetSideGUID(9157), 0, DetailedAmmoBoxData(8, 100))))
+ ),
+ InternalSlot(714, PlanetSideGUID(8498), 2,
+ DetailedWeaponData(2, 8, 0, List(InternalSlot(755, PlanetSideGUID(5356), 0, DetailedAmmoBoxData(8, 16))))
+ ),
+ InternalSlot(468, PlanetSideGUID(7198), 4,
+ DetailedWeaponData(2, 8, 0, List(InternalSlot(540, PlanetSideGUID(5009), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(456, PlanetSideGUID(5374), 5,
+ DetailedLockerContainerData(8, Some(InventoryData(List(
+ InternalSlot(429, PlanetSideGUID(3021), 0,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(272, PlanetSideGUID(8729), 0, DetailedAmmoBoxData(8, 0))))
+ ),
+ InternalSlot(838, PlanetSideGUID(8467), 9,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(839, PlanetSideGUID(8603), 0, DetailedAmmoBoxData(8, 5))))
+ ),
+ InternalSlot(272, PlanetSideGUID(3266), 18, DetailedAmmoBoxData(8, 27)),
+ InternalSlot(577, PlanetSideGUID(2934), 22,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(111, PlanetSideGUID(4682), 0, DetailedAmmoBoxData(8, 100))))
+ ),
+ InternalSlot(839, PlanetSideGUID(3271), 90, DetailedAmmoBoxData(8, 15)),
+ InternalSlot(839, PlanetSideGUID(7174), 94, DetailedAmmoBoxData(8, 6)),
+ InternalSlot(429, PlanetSideGUID(6084), 98,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(272, PlanetSideGUID(5928), 0, DetailedAmmoBoxData(8, 35))))
+ ),
+ InternalSlot(462, PlanetSideGUID(5000), 108,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(463, PlanetSideGUID(6277), 0, DetailedAmmoBoxData(8, 150))))
+ ),
+ InternalSlot(429, PlanetSideGUID(4341), 189,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(272, PlanetSideGUID(7043), 0, DetailedAmmoBoxData(8, 35))))
+ ),
+ InternalSlot(556, PlanetSideGUID(4168), 198,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(28, PlanetSideGUID(8937), 0, DetailedAmmoBoxData(8, 100))))
+ ),
+ InternalSlot(272, PlanetSideGUID(3173), 207, DetailedAmmoBoxData(8, 50)),
+ InternalSlot(462, PlanetSideGUID(3221), 210,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(463, PlanetSideGUID(4031), 0, DetailedAmmoBoxData(8, 150))))
+ ),
+ InternalSlot(556, PlanetSideGUID(6853), 280,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(29, PlanetSideGUID(8524), 0, DetailedAmmoBoxData(8, 67))))
+ ),
+ InternalSlot(556, PlanetSideGUID(4569), 290,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(28, PlanetSideGUID(5584), 0, DetailedAmmoBoxData(8, 100))))
+ ),
+ InternalSlot(462, PlanetSideGUID(9294), 300,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(463, PlanetSideGUID(3118), 0, DetailedAmmoBoxData(8, 150))))
+ ),
+ InternalSlot(272, PlanetSideGUID(4759), 387, DetailedAmmoBoxData(8, 50)),
+ InternalSlot(462, PlanetSideGUID(7377), 390,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(463, PlanetSideGUID(8155), 0, DetailedAmmoBoxData(8, 150))))
+ ),
+ InternalSlot(843, PlanetSideGUID(6709), 480, DetailedAmmoBoxData(8, 1)),
+ InternalSlot(843, PlanetSideGUID(5276), 484, DetailedAmmoBoxData(8, 1)),
+ InternalSlot(843, PlanetSideGUID(7769), 488, DetailedAmmoBoxData(8, 1)),
+ InternalSlot(844, PlanetSideGUID(5334), 492, DetailedAmmoBoxData(8, 1)),
+ InternalSlot(844, PlanetSideGUID(6219), 496, DetailedAmmoBoxData(8, 1)),
+ InternalSlot(842, PlanetSideGUID(7279), 500, DetailedAmmoBoxData(8, 1)),
+ InternalSlot(842, PlanetSideGUID(5415), 504, DetailedAmmoBoxData(8, 1)),
+ InternalSlot(175, PlanetSideGUID(5741), 540,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(5183), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(324, PlanetSideGUID(6208), 541,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(5029), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(324, PlanetSideGUID(8589), 542,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(9217), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(175, PlanetSideGUID(8901), 543,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7633), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(175, PlanetSideGUID(8419), 544,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(6546), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(175, PlanetSideGUID(4715), 545,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(8453), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(324, PlanetSideGUID(3577), 546,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(9202), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(324, PlanetSideGUID(6003), 547,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(3260), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(324, PlanetSideGUID(9140), 548,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(3815),0,DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(324, PlanetSideGUID(4913), 549,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(7222),0,DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(324, PlanetSideGUID(6954), 550,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(2953),0,DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(324, PlanetSideGUID(6405), 551,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(4676),0,DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(324, PlanetSideGUID(8915), 552,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(4018),0,DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(324, PlanetSideGUID(4993), 553,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(6775),0,DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(175, PlanetSideGUID(5053), 554,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(6418),0,DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(324, PlanetSideGUID(9244), 555,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(3327),0,DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(468, PlanetSideGUID(6292), 556,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540,PlanetSideGUID(6918),0,DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(842, PlanetSideGUID(5357), 558, DetailedAmmoBoxData(8, 1)),
+ InternalSlot(844, PlanetSideGUID(4435), 562, DetailedAmmoBoxData(8, 1)),
+ InternalSlot(843, PlanetSideGUID(7242), 566, DetailedAmmoBoxData(8, 1)),
+ InternalSlot(175, PlanetSideGUID(7330), 570,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(4786), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(468, PlanetSideGUID(7415), 571,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(6536), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(175, PlanetSideGUID(3949), 572,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7526), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(175, PlanetSideGUID(3805), 573,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7358), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(324, PlanetSideGUID(4493), 574,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(6852), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(324, PlanetSideGUID(5762), 575,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(3463), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(175, PlanetSideGUID(3315), 576,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7619), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(324, PlanetSideGUID(6263), 577,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(5912), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(468, PlanetSideGUID(4028), 578,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(8021), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(175, PlanetSideGUID(2843), 579,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7250), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(175, PlanetSideGUID(9143), 580,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(5195), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(468, PlanetSideGUID(5024), 581,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(4287), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(468, PlanetSideGUID(6582), 582,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(4915), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(468, PlanetSideGUID(6425), 583,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(8872), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(468, PlanetSideGUID(4431), 584,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(4191), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(175, PlanetSideGUID(8339), 585,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(7317), 0, DetailedAmmoBoxData(8, 1))))
+ ),
+ InternalSlot(175, PlanetSideGUID(3277), 586,
+ DetailedWeaponData(6, 8, 0, List(InternalSlot(540, PlanetSideGUID(6469), 0, DetailedAmmoBoxData(8, 1))))
+ )
+ ))))
+ ),
+ InternalSlot(213, PlanetSideGUID(6877), 6, DetailedCommandDetonaterData(4, 8)),
+ InternalSlot(755, PlanetSideGUID(6227), 9, DetailedAmmoBoxData(8, 16)),
+ InternalSlot(728, PlanetSideGUID(7181), 12, DetailedREKData(4, 16)),
+ InternalSlot(536, PlanetSideGUID(4077), 33, DetailedAmmoBoxData(8, 1)),
+ InternalSlot(680, PlanetSideGUID(4377), 37,
+ DetailedWeaponData(2, 8, 0, List(InternalSlot(681, PlanetSideGUID(8905), 0, DetailedAmmoBoxData(8, 3))))
+ ),
+ InternalSlot(32, PlanetSideGUID(5523), 39, DetailedACEData(4)),
+ InternalSlot(673, PlanetSideGUID(3661), 60,
+ DetailedWeaponData(2, 8, 0, List(InternalSlot(674, PlanetSideGUID(8542), 0, DetailedAmmoBoxData(8, 3))))
+ )
+ )
+ )
+ val obj = DetailedPlayerData(pos, app, char, inv, DrawnSlot.None)
+
val msg = ObjectCreateDetailedMessage(ObjectClass.avatar, PlanetSideGUID(75), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
-
val pkt_bitv = pkt.toBitVector
val ori_bitv = string_testchar_br32.toBitVector
pkt_bitv.take(153) mustEqual ori_bitv.take(153) //skip 1
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 976b74977..33e4ab6e2 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -1828,7 +1828,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
galaxy ! Zone.Lattice.RequestSpawnPoint(u5.toInt, player, u2.toInt)
case msg @ SetChatFilterMessage(send_channel, origin, whitelist) =>
- log.info("SetChatFilters: " + msg)
+ //log.info("SetChatFilters: " + msg)
case msg @ ChatMsg(messagetype, has_wide_contents, recipient, contents, note_contents) =>
var makeReply : Boolean = true
diff --git a/pslogin/src/test/scala/PacketCodingActorTest.scala b/pslogin/src/test/scala/PacketCodingActorTest.scala
index a2096f014..674e0fa33 100644
--- a/pslogin/src/test/scala/PacketCodingActorTest.scala
+++ b/pslogin/src/test/scala/PacketCodingActorTest.scala
@@ -452,45 +452,44 @@ class PacketCodingActorHTest extends ActorTest {
}
class PacketCodingActorITest extends ActorTest {
+ import net.psforever.packet.game.objectcreate._
+ val pos : PlacementData = PlacementData(Vector3.Zero, Vector3.Zero)
+ val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
+ BasicCharacterData("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1),
+ 3,
+ false,
+ false,
+ ExoSuitType.Standard,
+ "",
+ 0,
+ false,
+ 2.8125f, 210.9375f,
+ true,
+ GrenadeState.None,
+ false,
+ false,
+ false,
+ RibbonBars()
+ )
+ var char : (Option[Int])=>DetailedCharacterData = DetailedCharacterData(
+ 0,
+ 0,
+ 100, 100,
+ 50,
+ 1, 7, 7,
+ 100, 100,
+ List(CertificationType.StandardAssault, CertificationType.MediumAssault, CertificationType.ATV, CertificationType.Harasser, CertificationType.StandardExoSuit, CertificationType.AgileExoSuit, CertificationType.ReinforcedExoSuit),
+ List(),
+ List(),
+ List.empty,
+ None
+ )
+ val obj = DetailedPlayerData(pos, app, char, InventoryData(Nil), DrawnSlot.None)
+ val pkt = MultiPacketBundle(List(ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), obj)))
+ val string_hex = hex"000900001879060000bc84b000000000000000000002040000097049006c006c006c004900490049006c006c006c0049006c0049006c006c0049006c006c006c0049006c006c0049008452700000000000000000000000000000002000000fe6a703fffffffffffffffffffffffffffffffc00000000000000000000000000000000000000019001900064000001007ec800c80000000000000000000000000000000000000001c00042c54686c7000000000000000000000000000000000000000000000000000000000000000000000000200700"
+
"PacketCodingActor" should {
"bundle an r-originating packet into an l-facing SlottedMetaPacket byte stream data (SlottedMetaPacket)" in {
- import net.psforever.packet.game.objectcreate._
- val obj = DetailedCharacterData(
- CharacterAppearanceData(
- PlacementData(Vector3.Zero, Vector3.Zero),
- BasicCharacterData("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1),
- 3,
- false,
- false,
- ExoSuitType.Standard,
- "",
- 0,
- false,
- 2.8125f, 210.9375f,
- true,
- GrenadeState.None,
- false,
- false,
- false,
- RibbonBars()
- ),
- 0,
- 0,
- 100, 100,
- 50,
- 1, 7, 7,
- 100, 100,
- List(CertificationType.StandardAssault,CertificationType.MediumAssault,CertificationType.ATV,CertificationType.Harasser,CertificationType.StandardExoSuit,CertificationType.AgileExoSuit,CertificationType.ReinforcedExoSuit),
- List(),
- List(),
- List.empty,
- None,
- Some(InventoryData(Nil)),
- DrawnSlot.None
- )
- val pkt = MultiPacketBundle(List(ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), obj)))
- val string_hex = hex"000900001879060000bc84b000000000000000000002040000097049006c006c006c004900490049006c006c006c0049006c0049006c006c0049006c006c006c0049006c006c0049008452700000000000000000000000000000002000000fe6a703fffffffffffffffffffffffffffffffc00000000000000000000000000000000000000019001900064000001007ec800c80000000000000000000000000000000000000001c00042c54686c7000000000000000000000000000000000000000000000000000000000000000000000000200700"
-
val probe1 = TestProbe()
val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe")
val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca")
@@ -547,25 +546,25 @@ class PacketCodingActorJTest extends ActorTest {
class PacketCodingActorKTest extends ActorTest {
import net.psforever.packet.game.objectcreate._
- val obj = DetailedCharacterData(
- CharacterAppearanceData(
- PlacementData(Vector3.Zero, Vector3.Zero),
- BasicCharacterData("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1),
- 3,
- false,
- false,
- ExoSuitType.Standard,
- "",
- 0,
- false,
- 2.8125f, 210.9375f,
- true,
- GrenadeState.None,
- false,
- false,
- false,
- RibbonBars()
- ),
+ val pos : PlacementData = PlacementData(Vector3.Zero, Vector3.Zero)
+ val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
+ BasicCharacterData("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1),
+ 3,
+ false,
+ false,
+ ExoSuitType.Standard,
+ "",
+ 0,
+ false,
+ 2.8125f, 210.9375f,
+ true,
+ GrenadeState.None,
+ false,
+ false,
+ false,
+ RibbonBars()
+ )
+ var char : (Option[Int])=>DetailedCharacterData = DetailedCharacterData(
0,
0,
100, 100,
@@ -576,10 +575,9 @@ class PacketCodingActorKTest extends ActorTest {
List(),
List("xpe_sanctuary_help", "xpe_th_firemodes", "used_beamer", "map13"),
List.empty,
- None,
- Some(InventoryData(Nil)),
- DrawnSlot.None
+ None
)
+ val obj = DetailedPlayerData(pos, app, char, InventoryData(Nil), DrawnSlot.None)
val list = List(
ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), obj),
ObjectDeleteMessage(PlanetSideGUID(1103), 2),