PSF-LoginServer/common/src/test/scala/game/ObjectCreateMessageTest.scala

1203 lines
50 KiB
Scala
Raw Normal View History

2017-03-07 00:30:45 +00:00
// Copyright (c) 2017 PSForever
package game
import net.psforever.packet._
import net.psforever.packet.game.{ObjectCreateMessage, _}
import net.psforever.packet.game.objectcreate._
import net.psforever.types._
import org.specs2.mutable._
import scodec.bits._
class ObjectCreateMessageTest extends Specification {
val string_striker_projectile = hex"17 C5000000 A4B 009D 4C129 0CB0A 9814 00 F5 E3 040000666686400"
val string_implant_interface = hex"17 6C000000 01014C93304818000000"
val string_order_terminala = hex"17 A5000000 B2AF30EACF1889F7A3D1200007D2000000"
val string_ace_held = hex"17 76000000 0406900650C80480000000"
val string_boomertrigger = hex"17 76000000 58084A8100E80C00000000" //reconstructed from an inventory entry
val string_detonater_held = hex"17 76000000 1A886A8421080400000000"
val string_lasher_held = hex"17 BB000000 1688569D90B83 880000008082077036032000000"
val string_punisher_held = hex"17 F6000000 0A06612331083 88000000810381383E03200003793287C0E400000"
val string_rek_held = hex"17 86000000 27086C2350F800800000000000"
val string_captureflag = hex"17 E5000000 CE8EA10 04A47 B818A FE0E 00 00 0F 24000015000400160B09000" //LLU for Qumu on Amerish
val string_ace_dropped = hex"17 AF000000 90024113B329C5D5A2D1200005B440000000"
val string_detonater_dropped = hex"17 AF000000 EA8620ED1549B4B6A741500001B000000000"
val string_shotgunshell_dropped = hex"17 A5000000 F9A7D0D 5E269 BED5A F114 0000596000000"
val string_lasher_dropped = hex"17 F4000000 D69020C 99299 85D0A 5F10 00 00 20 400000004041038819018000000"
val string_punisher_dropped = hex"17 2F010000 E12A20B 915A9 28C9A 1412 00 00 33 200000004081C1901B01800001BCB5C2E07000000"
val string_rek_dropped = hex"17 BF000000 EC20311 85219 7AC1A 2D12 00 00 4E 4000000001800"
val string_boomer = hex"17 A5000000 CA0000F1630938D5A8F1400003F0031100"
val string_spitfire_short = hex"17 BB000000 9D37010 E4F08 6AFCA 0312 00 7F 42 2C1F0F0000F00"
val string_spitfire = hex"17 4F010000 9D3A910 D1D78 AE3FC 9111 00 00 69 4488107F80F2021DBF80B80C80000008086EDB83A03200000"
val string_trap = hex"17 BB000000 A8B630A 39FA6 FD666 801C 00 00 00 44C6097F80F00"
val string_aegis = hex"17 10010000 F80FC09 9DF96 0C676 801C 00 00 00 443E09FF0000000000000000000000000"
val string_orion = hex"17 5E010000 D82640B 92F76 01D65 F611 00 00 5E 4400006304BFC1E4041826E1503900000010104CE704C06400000"
val string_locker_container = hex"17 AF010000 E414C0C00000000000000000000600000818829DC2E030000000202378620D80C00000378FA0FADC000006F1FC199D800000"
val string_character = hex"17 73070000 BC8 3E0F 6C2D7 65535 CA16 00 00 09 9741E4F804000000 234530063007200610077006E00790052006F006E006E0069006500 220B7 E67B540404001000000000022B50100 268042006C00610063006B002000420065007200650074002000410072006D006F007500720065006400200043006F00720070007300 1700E0030050040003BC00000234040001A004000 3FFF67A8F A0A5424E0E800000000080952A9C3A03000001081103E040000000A023782F1080C0000016244108200000000808382403A030000014284C3A0C0000000202512F00B80C00000578F80F840000000280838B3C320300000080"
val string_character_backpack = hex"17 9C030000 BC8 340D F20A9 3956C AF0D 00 00 73 480000 87041006E00670065006C006C006F00 4A148 0000000000000000000000005C54200 24404F0072006900670069006E0061006C00200044006900730074007200690063007400 1740180181E8000000C202000042000000D202000000010A3C00"
"decode (striker projectile)" in {
PacketCoding.DecodePacket(string_striker_projectile).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 197
cls mustEqual ObjectClass.striker_missile_targeting_projectile
guid mustEqual PlanetSideGUID(40192)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[TrackedProjectileData] mustEqual true
val projectile = data.get.asInstanceOf[TrackedProjectileData]
projectile.pos.coord.x mustEqual 4644.5938f
projectile.pos.coord.y mustEqual 5472.0938f
projectile.pos.coord.z mustEqual 82.375f
projectile.pos.orient.x mustEqual 0f
projectile.pos.orient.y mustEqual 30.9375f
projectile.pos.orient.z mustEqual 171.5625f
projectile.unk1 mustEqual 0
projectile.unk2 mustEqual TrackedProjectileData.striker_missile_targetting_projectile_data
case _ =>
ko
}
}
"decode (implant interface)" in {
PacketCoding.DecodePacket(string_implant_interface).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 108
cls mustEqual 0x199
guid mustEqual PlanetSideGUID(1075)
parent.isDefined mustEqual true
parent.get.guid mustEqual PlanetSideGUID(514)
parent.get.slot mustEqual 1
data.isDefined mustEqual true
data.get.isInstanceOf[CommonTerminalData] mustEqual true
data.get.asInstanceOf[CommonTerminalData].faction mustEqual PlanetSideEmpire.VS
case _ =>
ko
}
}
"decode (order terminal a)" in {
PacketCoding.DecodePacket(string_order_terminala).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 165
cls mustEqual ObjectClass.order_terminala
guid mustEqual PlanetSideGUID(3827)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[DroppedItemData[_]] mustEqual true
val drop = data.get.asInstanceOf[DroppedItemData[_]]
drop.pos.coord.x mustEqual 4579.3438f
drop.pos.coord.y mustEqual 5615.0703f
drop.pos.coord.z mustEqual 72.953125f
drop.pos.orient.x mustEqual 0f
drop.pos.orient.y mustEqual 0f
drop.pos.orient.z mustEqual 98.4375f
drop.obj.isInstanceOf[CommonTerminalData] mustEqual true
val term = drop.obj.asInstanceOf[CommonTerminalData]
term.faction mustEqual PlanetSideEmpire.NC
term.unk mustEqual 0
case _ =>
ko
}
}
"decode (ace, held)" in {
PacketCoding.DecodePacket(string_ace_held).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 118
cls mustEqual ObjectClass.ace
guid mustEqual PlanetSideGUID(3173)
parent.isDefined mustEqual true
parent.get.guid mustEqual PlanetSideGUID(3336)
parent.get.slot mustEqual 0
data.isDefined mustEqual true
data.get.isInstanceOf[ACEData] mustEqual true
val ace = data.get.asInstanceOf[ACEData]
ace.unk1 mustEqual 4
ace.unk2 mustEqual 8
ace.unk3 mustEqual 0
case _ =>
ko
}
}
"decode (boomer trigger, held)" in {
PacketCoding.DecodePacket(string_boomertrigger).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 118
cls mustEqual ObjectClass.boomer_trigger
guid mustEqual PlanetSideGUID(3600)
parent.isDefined mustEqual true
parent.get.guid mustEqual PlanetSideGUID(4272)
parent.get.slot mustEqual 0
data.isDefined mustEqual true
data.get.isInstanceOf[BoomerTriggerData] mustEqual true
data.get.asInstanceOf[BoomerTriggerData].unk mustEqual 0
case _ =>
ko
}
}
"decode (detonator, held)" in {
PacketCoding.DecodePacket(string_detonater_held).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 118
cls mustEqual ObjectClass.command_detonater
guid mustEqual PlanetSideGUID(4162)
parent.isDefined mustEqual true
parent.get.guid mustEqual PlanetSideGUID(4149)
parent.get.slot mustEqual 0
data.isDefined mustEqual true
data.get.isInstanceOf[CommandDetonaterData] mustEqual true
val cud = data.get.asInstanceOf[CommandDetonaterData]
cud.unk1 mustEqual 4
cud.unk2 mustEqual 0
case _ =>
ko
}
}
"decode (lasher, held)" in {
PacketCoding.DecodePacket(string_lasher_held).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 187
cls mustEqual ObjectClass.lasher
guid mustEqual PlanetSideGUID(3033)
parent.isDefined mustEqual true
parent.get.guid mustEqual PlanetSideGUID(4141)
parent.get.slot mustEqual 3
data.isDefined mustEqual true
data.get.isInstanceOf[WeaponData] mustEqual true
val wep = data.get.asInstanceOf[WeaponData]
wep.unk1 mustEqual 4
wep.unk2 mustEqual 8
wep.fire_mode mustEqual 0
wep.ammo.head.objectClass mustEqual ObjectClass.energy_cell
wep.ammo.head.guid mustEqual PlanetSideGUID(3548)
wep.ammo.head.parentSlot mustEqual 0
wep.ammo.head.obj.isInstanceOf[AmmoBoxData] mustEqual true
val ammo = wep.ammo.head.obj.asInstanceOf[AmmoBoxData]
ammo.unk mustEqual 8
case _ =>
ko
}
}
"decode (punisher, held)" in {
PacketCoding.DecodePacket(string_punisher_held).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 246
cls mustEqual ObjectClass.punisher
guid mustEqual PlanetSideGUID(4147)
parent.isDefined mustEqual true
parent.get.guid mustEqual PlanetSideGUID(3092)
parent.get.slot mustEqual 3
data.isDefined mustEqual true
data.get.isInstanceOf[WeaponData] mustEqual true
val wep = data.get.asInstanceOf[WeaponData]
wep.unk1 mustEqual 4
wep.unk2 mustEqual 8
wep.fire_mode mustEqual 0
val ammo = wep.ammo
ammo.size mustEqual 2
//0
ammo.head.objectClass mustEqual ObjectClass.bullet_9mm
ammo.head.guid mustEqual PlanetSideGUID(3918)
ammo.head.parentSlot mustEqual 0
ammo.head.obj.isInstanceOf[AmmoBoxData] mustEqual true
ammo.head.obj.asInstanceOf[AmmoBoxData].unk mustEqual 8
//1
ammo(1).objectClass mustEqual ObjectClass.rocket
ammo(1).guid mustEqual PlanetSideGUID(3941)
ammo(1).parentSlot mustEqual 1
ammo(1).obj.isInstanceOf[AmmoBoxData] mustEqual true
ammo(1).obj.asInstanceOf[AmmoBoxData].unk mustEqual 8
case _ =>
ko
}
}
"decode (REK, held)" in {
PacketCoding.DecodePacket(string_rek_held).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 134
cls mustEqual ObjectClass.remote_electronics_kit
guid mustEqual PlanetSideGUID(3893)
parent.isDefined mustEqual true
parent.get.guid mustEqual PlanetSideGUID(4174)
parent.get.slot mustEqual 0
data.isDefined mustEqual true
data.get.isInstanceOf[REKData] mustEqual true
val rek = data.get.asInstanceOf[REKData]
rek.unk1 mustEqual 0
rek.unk2 mustEqual 8
rek.unk3 mustEqual 0
case _ =>
ko
}
}
"decode (capture flag)" in {
PacketCoding.DecodePacket(string_captureflag).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 229
cls mustEqual ObjectClass.capture_flag
guid mustEqual PlanetSideGUID(4330)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[CaptureFlagData] mustEqual true
val flag = data.get.asInstanceOf[CaptureFlagData]
flag.pos.coord.x mustEqual 3912.0312f
flag.pos.coord.y mustEqual 5169.4375f
flag.pos.coord.z mustEqual 59.96875f
flag.pos.orient.x mustEqual 0f
flag.pos.orient.y mustEqual 0f
flag.pos.orient.z mustEqual 47.8125f
flag.faction mustEqual PlanetSideEmpire.NC
flag.unk1 mustEqual 21
flag.unk2 mustEqual 4
flag.unk3 mustEqual 2838
flag.unk4 mustEqual 9
case _ =>
ko
}
}
"decode (ace, dropped)" in {
PacketCoding.DecodePacket(string_ace_dropped).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 175
cls mustEqual ObjectClass.ace
guid mustEqual PlanetSideGUID(4388)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[DroppedItemData[_]] mustEqual true
val drop = data.get.asInstanceOf[DroppedItemData[_]]
drop.pos.coord.x mustEqual 4708.461f
drop.pos.coord.y mustEqual 5547.539f
drop.pos.coord.z mustEqual 72.703125f
drop.pos.orient.x mustEqual 0f
drop.pos.orient.y mustEqual 0f
drop.pos.orient.z mustEqual 194.0625f
drop.obj.isInstanceOf[ACEData] mustEqual true
val ace = drop.obj.asInstanceOf[ACEData]
ace.unk1 mustEqual 8
ace.unk2 mustEqual 8
case _ =>
ko
}
}
"decode (detonator, dropped)" in {
PacketCoding.DecodePacket(string_detonater_dropped).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 175
cls mustEqual ObjectClass.command_detonater
guid mustEqual PlanetSideGUID(3682)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[DroppedItemData[_]] mustEqual true
val drop = data.get.asInstanceOf[DroppedItemData[_]]
drop.pos.coord.x mustEqual 4777.633f
drop.pos.coord.y mustEqual 5485.4062f
drop.pos.coord.z mustEqual 85.8125f
drop.pos.orient.x mustEqual 0f
drop.pos.orient.y mustEqual 0f
drop.pos.orient.z mustEqual 14.0625f
drop.obj.isInstanceOf[CommandDetonaterData] mustEqual true
case _ =>
ko
}
}
"decode (shotgun shells, dropped)" in {
PacketCoding.DecodePacket(string_shotgunshell_dropped).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 165
cls mustEqual ObjectClass.shotgun_shell
guid mustEqual PlanetSideGUID(3453)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[DroppedItemData[_]] mustEqual true
val drop = data.get.asInstanceOf[DroppedItemData[_]]
drop.pos.coord.x mustEqual 4684.7344f
drop.pos.coord.y mustEqual 5547.4844f
drop.pos.coord.z mustEqual 83.765625f
drop.pos.orient.x mustEqual 0f
drop.pos.orient.y mustEqual 0f
drop.pos.orient.z mustEqual 199.6875f
drop.obj.isInstanceOf[AmmoBoxData] mustEqual true
val box = drop.obj.asInstanceOf[AmmoBoxData]
box.unk mustEqual 0
case _ =>
ko
}
}
"decode (lasher, dropped)" in {
PacketCoding.DecodePacket(string_lasher_dropped).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 244
cls mustEqual ObjectClass.lasher
guid mustEqual PlanetSideGUID(3074)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[DroppedItemData[_]] mustEqual true
val drop = data.get.asInstanceOf[DroppedItemData[_]]
drop.pos.coord.x mustEqual 4691.1953f
drop.pos.coord.y mustEqual 5537.039f
drop.pos.coord.z mustEqual 65.484375f
drop.pos.orient.x mustEqual 0f
drop.pos.orient.y mustEqual 0f
drop.pos.orient.z mustEqual 0f
drop.obj.isInstanceOf[WeaponData] mustEqual true
val wep = drop.obj.asInstanceOf[WeaponData]
wep.unk1 mustEqual 4
wep.unk2 mustEqual 0
wep.fire_mode mustEqual 0
wep.ammo.head.objectClass mustEqual ObjectClass.energy_cell
wep.ammo.head.guid mustEqual PlanetSideGUID(3268)
wep.ammo.head.parentSlot mustEqual 0
wep.ammo.head.obj.isInstanceOf[AmmoBoxData] mustEqual true
val ammo = wep.ammo.head.obj.asInstanceOf[AmmoBoxData]
ammo.unk mustEqual 0
case _ =>
ko
}
}
"decode (punisher, dropped)" in {
PacketCoding.DecodePacket(string_punisher_dropped).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 303
cls mustEqual ObjectClass.punisher
guid mustEqual PlanetSideGUID(2978)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[DroppedItemData[_]] mustEqual true
val drop = data.get.asInstanceOf[DroppedItemData[_]]
drop.pos.coord.x mustEqual 4789.133f
drop.pos.coord.y mustEqual 5522.3125f
drop.pos.coord.z mustEqual 72.3125f
drop.pos.orient.x mustEqual 0f
drop.pos.orient.y mustEqual 0f
drop.pos.orient.z mustEqual 306.5625f
drop.obj.isInstanceOf[WeaponData] mustEqual true
val wep = drop.obj.asInstanceOf[WeaponData]
wep.unk1 mustEqual 2
wep.unk2 mustEqual 0
wep.fire_mode mustEqual 0
val ammo = wep.ammo
ammo.size mustEqual 2
//0
ammo.head.objectClass mustEqual ObjectClass.bullet_9mm
ammo.head.guid mustEqual PlanetSideGUID(3528)
ammo.head.parentSlot mustEqual 0
ammo.head.obj.isInstanceOf[AmmoBoxData] mustEqual true
ammo.head.obj.asInstanceOf[AmmoBoxData].unk mustEqual 0
//1
ammo(1).objectClass mustEqual ObjectClass.rocket
ammo(1).guid mustEqual PlanetSideGUID(3031)
ammo(1).parentSlot mustEqual 1
ammo(1).obj.isInstanceOf[AmmoBoxData] mustEqual true
ammo(1).obj.asInstanceOf[AmmoBoxData].unk mustEqual 0
case _ =>
ko
}
}
"decode (REK, dropped)" in {
PacketCoding.DecodePacket(string_rek_dropped).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 191
cls mustEqual ObjectClass.remote_electronics_kit
guid mustEqual PlanetSideGUID(4355)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[DroppedItemData[_]] mustEqual true
val dropped = data.get.asInstanceOf[DroppedItemData[_]]
dropped.pos.coord.x mustEqual 4675.039f
dropped.pos.coord.y mustEqual 5506.953f
dropped.pos.coord.z mustEqual 72.703125f
dropped.pos.orient.x mustEqual 0f
dropped.pos.orient.y mustEqual 0f
dropped.pos.orient.z mustEqual 230.625f
dropped.obj.isInstanceOf[REKData] mustEqual true
val rek = dropped.obj.asInstanceOf[REKData]
rek.unk1 mustEqual 8
rek.unk2 mustEqual 0
rek.unk3 mustEqual 3
case _ =>
ko
}
}
"decode (boomer)" in {
PacketCoding.DecodePacket(string_boomer).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 165
cls mustEqual ObjectClass.boomer
guid mustEqual PlanetSideGUID(3840)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[SmallDeployableData] mustEqual true
val boomer = data.get.asInstanceOf[SmallDeployableData]
boomer.deploy.pos.coord.x mustEqual 4704.172f
boomer.deploy.pos.coord.y mustEqual 5546.4375f
boomer.deploy.pos.coord.z mustEqual 82.234375f
boomer.deploy.pos.orient.x mustEqual 0f
boomer.deploy.pos.orient.y mustEqual 0f
boomer.deploy.pos.orient.z mustEqual 272.8125f
boomer.deploy.unk mustEqual 0
boomer.deploy.player_guid mustEqual PlanetSideGUID(4145)
case _ =>
ko
}
}
"decode (spitfire, short)" in {
PacketCoding.DecodePacket(string_spitfire_short).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 187
cls mustEqual ObjectClass.spitfire_turret
guid mustEqual PlanetSideGUID(4208)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[SmallTurretData] mustEqual true
val turret = data.get.asInstanceOf[SmallTurretData]
turret.deploy.pos.coord.x mustEqual 4577.7812f
turret.deploy.pos.coord.y mustEqual 5624.828f
turret.deploy.pos.coord.z mustEqual 72.046875f
turret.deploy.pos.orient.x mustEqual 0f
turret.deploy.pos.orient.y mustEqual 2.8125f
turret.deploy.pos.orient.z mustEqual 264.375f
turret.deploy.faction mustEqual PlanetSideEmpire.NC
turret.deploy.unk mustEqual 12
turret.deploy.player_guid mustEqual PlanetSideGUID(3871)
turret.health mustEqual 0
turret.internals.isDefined mustEqual false
case _ =>
ko
}
}
"decode (spitfire)" in {
PacketCoding.DecodePacket(string_spitfire).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 335
cls mustEqual ObjectClass.spitfire_turret
guid mustEqual PlanetSideGUID(4265)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[SmallTurretData] mustEqual true
val turret = data.get.asInstanceOf[SmallTurretData]
turret.deploy.pos.coord.x mustEqual 4527.633f
turret.deploy.pos.coord.y mustEqual 6271.3594f
turret.deploy.pos.coord.z mustEqual 70.265625f
turret.deploy.pos.orient.x mustEqual 0f
turret.deploy.pos.orient.y mustEqual 0f
turret.deploy.pos.orient.z mustEqual 154.6875f
turret.deploy.faction mustEqual PlanetSideEmpire.VS
turret.deploy.unk mustEqual 4
turret.deploy.player_guid mustEqual PlanetSideGUID(4232)
turret.health mustEqual 255
turret.internals.isDefined mustEqual true
val internals = turret.internals.get
internals.objectClass mustEqual ObjectClass.spitfire_weapon
internals.guid mustEqual PlanetSideGUID(3064)
internals.parentSlot mustEqual 0
internals.obj.isInstanceOf[WeaponData] mustEqual true
val wep = internals.obj.asInstanceOf[WeaponData]
wep.unk1 mustEqual 0x6
wep.unk2 mustEqual 0x8
wep.fire_mode mustEqual 0
val ammo = wep.ammo.head
ammo.objectClass mustEqual ObjectClass.spitfire_ammo
ammo.guid mustEqual PlanetSideGUID(3694)
ammo.parentSlot mustEqual 0
ammo.obj.isInstanceOf[AmmoBoxData] mustEqual true
ammo.obj.asInstanceOf[AmmoBoxData].unk mustEqual 8
case _ =>
ko
}
}
"decode (trap)" in {
PacketCoding.DecodePacket(string_trap).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 187
cls mustEqual ObjectClass.tank_traps
guid mustEqual PlanetSideGUID(2659)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[TRAPData] mustEqual true
val trap = data.get.asInstanceOf[TRAPData]
trap.deploy.pos.coord.x mustEqual 3572.4453f
trap.deploy.pos.coord.y mustEqual 3277.9766f
trap.deploy.pos.coord.z mustEqual 114.0f
trap.deploy.pos.orient.x mustEqual 0f
trap.deploy.pos.orient.y mustEqual 0f
trap.deploy.pos.orient.z mustEqual 90.0f
trap.deploy.faction mustEqual PlanetSideEmpire.VS
trap.deploy.unk mustEqual 4
trap.health mustEqual 255
trap.deploy.player_guid mustEqual PlanetSideGUID(2502)
case _ =>
ko
}
}
"decode (aegis)" in {
PacketCoding.DecodePacket(string_aegis).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 272
cls mustEqual ObjectClass.deployable_shield_generator
guid mustEqual PlanetSideGUID(2556)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[AegisShieldGeneratorData] mustEqual true
val aegis = data.get.asInstanceOf[AegisShieldGeneratorData]
aegis.deploy.pos.coord.x mustEqual 3571.2266f
aegis.deploy.pos.coord.y mustEqual 3278.0938f
aegis.deploy.pos.coord.z mustEqual 114.0f
aegis.deploy.pos.orient.x mustEqual 0f
aegis.deploy.pos.orient.y mustEqual 0f
aegis.deploy.pos.orient.z mustEqual 90.0f
aegis.deploy.faction mustEqual PlanetSideEmpire.VS
aegis.deploy.unk mustEqual 4
aegis.health mustEqual 255
aegis.deploy.player_guid mustEqual PlanetSideGUID(2366)
case _ =>
ko
}
}
"decode (orion)" in {
PacketCoding.DecodePacket(string_orion).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 350
cls mustEqual ObjectClass.portable_manned_turret_vs
guid mustEqual PlanetSideGUID(2916)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[OneMannedFieldTurretData] mustEqual true
val omft = data.get.asInstanceOf[OneMannedFieldTurretData]
omft.deploy.pos.coord.x mustEqual 3567.1406f
omft.deploy.pos.coord.y mustEqual 2988.0078f
omft.deploy.pos.coord.z mustEqual 71.84375f
omft.deploy.pos.orient.x mustEqual 0f
omft.deploy.pos.orient.y mustEqual 0f
omft.deploy.pos.orient.z mustEqual 185.625f
omft.deploy.faction mustEqual PlanetSideEmpire.VS
omft.deploy.unk mustEqual 4
omft.deploy.player_guid mustEqual PlanetSideGUID(2502)
omft.health mustEqual 255
omft.internals.isDefined mustEqual true
val internals = omft.internals.get
internals.objectClass mustEqual ObjectClass.energy_gun_vs
internals.guid mustEqual PlanetSideGUID(2615)
internals.parentSlot mustEqual 1
internals.obj.isInstanceOf[WeaponData] mustEqual true
val wep = internals.obj.asInstanceOf[WeaponData]
wep.unk1 mustEqual 0x6
wep.unk2 mustEqual 0x8
wep.fire_mode mustEqual 0
val ammo = wep.ammo.head
ammo.objectClass mustEqual ObjectClass.energy_gun_ammo
ammo.guid mustEqual PlanetSideGUID(2510)
ammo.parentSlot mustEqual 0
ammo.obj.isInstanceOf[AmmoBoxData] mustEqual true
ammo.obj.asInstanceOf[AmmoBoxData].unk mustEqual 8
case _ =>
ko
}
}
"decode (locker container)" in {
PacketCoding.DecodePacket(string_locker_container).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 431
cls mustEqual ObjectClass.locker_container
guid mustEqual PlanetSideGUID(3148)
parent.isDefined mustEqual false
data.isDefined mustEqual true
data.get.isInstanceOf[LockerContainerData] mustEqual true
val locker = data.get.asInstanceOf[LockerContainerData]
val contents = locker.inventory.contents
contents.size mustEqual 3
//0
contents.head.objectClass mustEqual ObjectClass.nano_dispenser
contents.head.guid mustEqual PlanetSideGUID(2935)
contents.head.parentSlot mustEqual 0
contents.head.obj.isInstanceOf[WeaponData] mustEqual true
val dispenser = contents.head.obj.asInstanceOf[WeaponData]
dispenser.unk1 mustEqual 0x6
dispenser.unk2 mustEqual 0x0
dispenser.ammo.head.objectClass mustEqual ObjectClass.armor_canister
dispenser.ammo.head.guid mustEqual PlanetSideGUID(3426)
dispenser.ammo.head.parentSlot mustEqual 0
dispenser.ammo.head.obj.isInstanceOf[AmmoBoxData] mustEqual true
dispenser.ammo.head.obj.asInstanceOf[AmmoBoxData].unk mustEqual 0
//1
contents(1).objectClass mustEqual ObjectClass.armor_canister
contents(1).guid mustEqual PlanetSideGUID(4090)
contents(1).parentSlot mustEqual 45
contents(1).obj.isInstanceOf[AmmoBoxData] mustEqual true
contents(1).obj.asInstanceOf[AmmoBoxData].unk mustEqual 0
//2
contents(2).objectClass mustEqual ObjectClass.armor_canister
contents(2).guid mustEqual PlanetSideGUID(3326)
contents(2).parentSlot mustEqual 78
contents(2).obj.isInstanceOf[AmmoBoxData] mustEqual true
contents(2).obj.asInstanceOf[AmmoBoxData].unk mustEqual 0
case _ =>
ko
}
}
"decode (character, alive)" in {
PacketCoding.DecodePacket(string_character).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 1907
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.Loser4
pc.appearance.ribbons.middle mustEqual MeritCommendation.HeavyInfantry3
pc.appearance.ribbons.lower mustEqual MeritCommendation.TankBuster6
pc.appearance.ribbons.tos mustEqual MeritCommendation.SixYearNC
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
case _ =>
ko
}
}
"decode (character, backpack)" in {
PacketCoding.DecodePacket(string_character_backpack).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 924L
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.Jacking
pc.appearance.ribbons.middle mustEqual MeritCommendation.ScavengerTR6
pc.appearance.ribbons.lower mustEqual MeritCommendation.AMSSupport4
pc.appearance.ribbons.tos mustEqual MeritCommendation.SixYearTR
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
case _ =>
ko
}
}
"encode (striker projectile)" in {
val obj = TrackedProjectileData.striker(
PlacementData(4644.5938f, 5472.0938f, 82.375f, 0f, 30.9375f, 171.5625f),
0
)
val msg = ObjectCreateMessage(ObjectClass.striker_missile_targeting_projectile, PlanetSideGUID(40192), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt.toBitVector.take(132) mustEqual string_striker_projectile.toBitVector.take(132)
pkt.toBitVector.drop(133).take(7) mustEqual string_striker_projectile.toBitVector.drop(133).take(7)
pkt.toBitVector.drop(141) mustEqual string_striker_projectile.toBitVector.drop(141)
}
"encode (implant interface)" in {
val obj = CommonTerminalData(PlanetSideEmpire.VS)
val msg = ObjectCreateMessage(0x199, PlanetSideGUID(1075), ObjectCreateMessageParent(PlanetSideGUID(514), 1), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_implant_interface
}
"encode (order terminal a)" in {
val obj = DroppedItemData(
PlacementData(4579.3438f, 5615.0703f, 72.953125f, 0f, 0f, 98.4375f),
CommonTerminalData(PlanetSideEmpire.NC)
)
val msg = ObjectCreateMessage(ObjectClass.order_terminala, PlanetSideGUID(3827), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_order_terminala
}
"encode (ace, held)" in {
val obj = ACEData(4, 8)
val msg = ObjectCreateMessage(ObjectClass.ace, PlanetSideGUID(3173), ObjectCreateMessageParent(PlanetSideGUID(3336), 0), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_ace_held
}
"encode (boomer trigger, held)" in {
val obj = BoomerTriggerData(0)
val msg = ObjectCreateMessage(ObjectClass.boomer_trigger, PlanetSideGUID(3600), ObjectCreateMessageParent(PlanetSideGUID(4272), 0), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_boomertrigger
}
"encode (detonater, held)" in {
val obj = CommandDetonaterData(4)
val msg = ObjectCreateMessage(ObjectClass.command_detonater, PlanetSideGUID(4162), ObjectCreateMessageParent(PlanetSideGUID(4149), 0), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_detonater_held
}
"encode (lasher, held)" in {
val obj = WeaponData(4, 8, ObjectClass.energy_cell, PlanetSideGUID(3548), 0, AmmoBoxData(8))
val msg = ObjectCreateMessage(ObjectClass.lasher, PlanetSideGUID(3033), ObjectCreateMessageParent(PlanetSideGUID(4141), 3), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_lasher_held
}
"encode (punisher, held)" in {
val obj =
WeaponData(4, 8, 0,
AmmoBoxData(ObjectClass.bullet_9mm, PlanetSideGUID(3918), 0, AmmoBoxData(8)) ::
AmmoBoxData(ObjectClass.rocket, PlanetSideGUID(3941), 1, AmmoBoxData(8)) ::
Nil
)(2)
val msg = ObjectCreateMessage(ObjectClass.punisher, PlanetSideGUID(4147), ObjectCreateMessageParent(PlanetSideGUID(3092), 3), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_punisher_held
}
"encode (REK, held)" in {
val obj = REKData(0, 8)
val msg = ObjectCreateMessage(ObjectClass.remote_electronics_kit, PlanetSideGUID(3893), ObjectCreateMessageParent(PlanetSideGUID(4174), 0), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_rek_held
}
"encode (capture flag)" in {
val obj = CaptureFlagData(PlacementData(3912.0312f, 5169.4375f, 59.96875f, 0f, 0f, 47.8125f), PlanetSideEmpire.NC, 21, 4, 2838, 9)
val msg = ObjectCreateMessage(ObjectClass.capture_flag, PlanetSideGUID(4330), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_captureflag
}
"encode (ace, dropped)" in {
val obj = DroppedItemData(
PlacementData(4708.461f, 5547.539f, 72.703125f, 0f, 0f, 194.0625f),
ACEData(8, 8)
)
val msg = ObjectCreateMessage(ObjectClass.ace, PlanetSideGUID(4388), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_ace_dropped
}
"encode (detonator, dropped)" in {
val obj = DroppedItemData(
PlacementData(4777.633f, 5485.4062f, 85.8125f, 0f, 0f, 14.0625f),
CommandDetonaterData()
)
val msg = ObjectCreateMessage(ObjectClass.command_detonater, PlanetSideGUID(3682), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_detonater_dropped
}
"encode (shotgun shells, dropped)" in {
val obj = DroppedItemData(
PlacementData(4684.7344f, 5547.4844f, 83.765625f, 0f, 0f, 199.6875f),
AmmoBoxData()
)
val msg = ObjectCreateMessage(ObjectClass.shotgun_shell, PlanetSideGUID(3453), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_shotgunshell_dropped
}
"encode (lasher, dropped)" in {
val obj = DroppedItemData(
PlacementData(4691.1953f, 5537.039f, 65.484375f, 0.0f, 0.0f, 0.0f),
WeaponData(4, 0, ObjectClass.energy_cell, PlanetSideGUID(3268), 0, AmmoBoxData())
)
val msg = ObjectCreateMessage(ObjectClass.lasher, PlanetSideGUID(3074), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_lasher_dropped
}
"encode (punisher, dropped)" in {
val obj = DroppedItemData(
PlacementData(4789.133f, 5522.3125f, 72.3125f, 0f, 0f, 306.5625f),
WeaponData(2, 0, 0,
AmmoBoxData(ObjectClass.bullet_9mm, PlanetSideGUID(3528), 0, AmmoBoxData()) ::
AmmoBoxData(ObjectClass.rocket, PlanetSideGUID(3031), 1, AmmoBoxData()) ::
Nil
)(2)
)
val msg = ObjectCreateMessage(ObjectClass.punisher, PlanetSideGUID(2978), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_punisher_dropped
}
"encode (REK, dropped)" in {
val obj = DroppedItemData(
PlacementData(4675.039f, 5506.953f, 72.703125f, 0f, 0f, 230.625f),
REKData(8, 0, 3)
)
val msg = ObjectCreateMessage(ObjectClass.remote_electronics_kit, PlanetSideGUID(4355), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_rek_dropped
}
"encode (boomer)" in {
val obj = SmallDeployableData(
CommonFieldData(
PlacementData(4704.172f, 5546.4375f, 82.234375f, 0f, 0f, 272.8125f),
PlanetSideEmpire.TR, 0, PlanetSideGUID(4145)
)
)
val msg = ObjectCreateMessage(ObjectClass.boomer, PlanetSideGUID(3840), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_boomer
}
"encode (spitfire, short)" in {
val obj = SmallTurretData(
CommonFieldData(
PlacementData(4577.7812f, 5624.828f, 72.046875f, 0f, 2.8125f, 264.375f),
PlanetSideEmpire.NC, 12, PlanetSideGUID(3871)
),
255 //sets to 0
)
val msg = ObjectCreateMessage(ObjectClass.spitfire_turret, PlanetSideGUID(4208), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
val pkt_bitv = pkt.toBitVector
val ori_bitv = string_spitfire_short.toBitVector
pkt_bitv.take(173) mustEqual ori_bitv.take(173)
pkt_bitv.drop(185) mustEqual ori_bitv.drop(185)
//TODO work on SmallTurretData to make this pass as a single stream
}
"encode (spitfire)" in {
val obj = SmallTurretData(
CommonFieldData(
PlacementData(4527.633f, 6271.3594f, 70.265625f, 0f, 0f, 154.6875f),
PlanetSideEmpire.VS, 4, PlanetSideGUID(4232)
),
255,
SmallTurretData.spitfire(PlanetSideGUID(3064), 0x6, 0x8, PlanetSideGUID(3694), 8)
)
val msg = ObjectCreateMessage(ObjectClass.spitfire_turret, PlanetSideGUID(4265), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
val pkt_bitv = pkt.toBitVector
val ori_bitv = string_spitfire.toBitVector
pkt_bitv.take(173) mustEqual ori_bitv.take(173)
pkt_bitv.drop(185) mustEqual ori_bitv.drop(185)
//TODO work on SmallTurretData to make this pass as a single stream
}
"encode (trap)" in {
val obj = TRAPData(
CommonFieldData(
PlacementData(3572.4453f, 3277.9766f, 114.0f, 0f, 0f, 90.0f),
PlanetSideEmpire.VS, 4, PlanetSideGUID(2502)
),
255
)
val msg = ObjectCreateMessage(ObjectClass.tank_traps, PlanetSideGUID(2659), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
val pkt_bitv = pkt.toBitVector
val ori_bitv = string_trap.toBitVector
pkt_bitv.take(173) mustEqual ori_bitv.take(173)
pkt_bitv.drop(185) mustEqual ori_bitv.drop(185)
//TODO work on TRAPData to make this pass as a single stream
}
"encode (aegis)" in {
val obj = AegisShieldGeneratorData(
CommonFieldData(
PlacementData(3571.2266f, 3278.0938f, 114.0f, 0f, 0f, 90.0f),
PlanetSideEmpire.VS, 4, PlanetSideGUID(2366)
),
255
)
val msg = ObjectCreateMessage(ObjectClass.deployable_shield_generator, PlanetSideGUID(2556), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_aegis
}
"encode (orion)" in {
val obj = OneMannedFieldTurretData(
CommonFieldData(
PlacementData(3567.1406f, 2988.0078f, 71.84375f, 0f, 0f, 185.625f),
PlanetSideEmpire.VS, 4, PlanetSideGUID(2502)
),
255,
OneMannedFieldTurretData.orion(PlanetSideGUID(2615), 0x6, 0x8, PlanetSideGUID(2510), 8)
)
val msg = ObjectCreateMessage(ObjectClass.portable_manned_turret_vs, PlanetSideGUID(2916), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
val pkt_bitv = pkt.toBitVector
val ori_bitv = string_orion.toBitVector
pkt_bitv.take(189) mustEqual ori_bitv.take(189)
pkt_bitv.drop(200) mustEqual ori_bitv.drop(200)
//TODO work on OneMannedFieldTurretData to make this pass as a single stream
}
"encode (locker container)" in {
val obj = LockerContainerData(
InventoryData(
new stuff for player server classes; this update is not yet complete adjusted sample Reload code and added insertion and removal functions for inventory more work on player classes; moving PacketResolution to another branch decoupling GUIDs from objects; introduced Ammo enum; minor adjustments to inventory system; different object/class hierarchy transferring basic files from another branch converted from get/set to accessor/mutator; resolved conflict from name changes refactored basic components such as GUID and location/orientation utilities kludge; more fields are given accessor and mutators; create package for vehicle-specific classes GUID assurance, now with less object creation test files; changes to how AmmoBox initializes sorry, a little bit of everything, so much I forgot to write it all down switched to a unified fire mode object internal to a Tool importing a heavily modified version of my GUID manager objects from the laternate branch; not finished or tested yet created a Trait to make Key private, sources and selectors to allow NumberPools to exist independent of a NumberSource; placed Ascending into a misc folder swapped the Return methods for selectors so that the more primitive logic is the one that needs to be overriden; renamed a selector to be more common; had to update some copyright messages fixed major logic issue with NumberPool; added comments to NumberSource files NumberSource tests simplified and made more consistent the method naming convention of NumberSources comments for NumberSelectors starting on NumberSelector tests modifications that should better support number pools; added a pool hub that acts on a predefined source adjustment to how Tools and FireModeDefintion keep track of ammunition and ammunition slots; I don't think this is sufficient small additions to Tools; filled out simple tests for other three Selectors added object lookup notation for the pool hub added more NumberSelector tests; removed the word 'Number' from subclass names re-named classes, re-packaged classes, re-named packages; created new Selector, split pools to create a fallback for the NumberPoolHub changes to NumberPool classes; tests on NumberPool classes changes to NumberPool classes; tests on NumberPool classes2 some robust testing for NumberPoolHub, plus necessary modifications to other files register and unregister functions now use Success and Failure conditions, save for one true thrown Exception reduced the flow of certain algorithm chains, mainly by adding match statements and removing Exceptions error message text the same thing as the last commit, but with NumberPools rather than NumberPoolHub various types of freeform registration added sorting functions to Selectors to allow for hotswapping for number pools, especially to and from SpecificSelector; tests for NumberPoolHub get numbers from an Array of indices, not the list of Numbers, in SimplePool added a class to represent the four types of recovery kits comments on Kit files created package for supporting equipment classes; renamed /definition/ package adding class for items that construct deployables, the router telepad included added SimpleItem, classes for game Equipment that do not have internal state; re-organized ObjectDefinition files and the game objects they create to more naturally move around EquipmentSize and InventoryTile (size) added SimpleItem tests (what they are...); removed example code that has hogging an import from AmmoBox auto-sort for loading and fitting former inventory content back into the inventory method of finding first available position to fit an certain size block in the inventory changed CheckCollision return type to provide Try[List[Int]; fixed all existing references and tests wrote comments for GridInventory methods; changed insertion param to be of form 'key -> element' adding features to Player; created definitions for Player class; re-grouped ConstructionItem enumerations initial work on implants; shuffled classes to better accommodate the new implant system, I think wrote some tests for Implants; fixing Implant logic wrote tests for Player class and made adjustments where necessary basic initialization during Player creation based on exo-suit type three wrapper Actors for the normal classes comments on code modified tests to improve accountability; added Resolver class to deal with multiple tasks that contribute to a larger task changed Tools to an internal AmmoBox; don't have to def -= symbol if I def _= symbol LivePlayerList -> MasterPlayerList, and added a Fit def for Player that checks holsters as well as inventory example of packet conversion can be found with AmmoBoxDefinition added conversion for ToolDefinition added all Equipment packet conversion functionality; started working on Avatar-related conversions continued effort towards a working Player packet conversion test subclasses of Equipment apparently do not need to overide the type of the PacketConverter for generics the logical conclusion: it doesn't matter what generics Packet returns so long as it returns an ObjectCreateConverter[] type separated converters from definitions into files changed some configuration information to final; added a bunch of converters, not fully tested though changed function names in converters replaced WSA packet-driven OCDM with Player object OCDM; upgrade to Float angular data added partial support for LockerContainer; changed Equipment defaults to a common value changes to AvatarConverter to include 5th slot; changes to VehicleConverter to make work; implementation of Fury in Vehicle->packet example in WSA added a seat definition and renovated how the weapon controlled from a seat can be found comments to files mainly; non-essential functionality to some classes, mostly access determination moved converter tests to their own test file write more of this test added ServiceManager, as it is useful pool range changes added AvatarService, as it is useful straightened out the GUID actors; added the static method for adding AmmoBoxes (to be converted later) chnages to task resolution operation complicated Task and TaskResolver logic is now possible; for example, you can schedule giving an AmmoBox a GUID, before giving a Tool a GUID, before placing the Tool in a player's hand; see Suppressor example in WSA separated the Task trait and the TaskResolver actor into their own classes, moving the former RegistrationTaskResolver class into the /misc/ folder; deleted old backup copy of HubActor; modifications for PoC and supported tests added better support and protection against putting things in the wrong hand when using inventories and the Player.Slot(n) function GlobalDefinitions file; added laze pointer as an SItem, and gave it the command detonater management code; additionally fixed spelling of 'detonat[o]r' in Codec; early Terminal class work updated tests to GlobalDefinitions entries; Terminal works but I don't like it played with GUID pooling workflow, though to little avail; modifications to Terminal purchasing workflow, but still very volatile modified NumberPoolActor and NumberPoolAccessor to make them more straightforward and not use akka ask as a go-between fixed recovery options so that they do not cause more messages trailing newline InventoryItem (packet data) renamed InventoryItemData to remove ambiguity; Terminal functionality improved, allowing for swapping of exo-suits and the restoration of equipment positions remove yet-unsupported Terminal messaging made Terminal message more specific; can now put equipment into empty slot on exo-suit change; should report changes better re-organized function calls to preserved items removed from holster slots on exo-suit change moved predicate to the end of the list of params for recoverInventory so that repetition can be eliminated and a default value can be assigned issues with making Tool; committing changes before revert of NumberPoolActor and NumberPoolAccessorActor to see if those broke it a necessary evil, the reverting of these two Actors; subtask resolution does not work unless I do so, for now restored the registration portion of tasking back to where it previously was (and better?) NumberPoolActor and the ...AccessorActor are back to a comfortable place (and better?) re-draw object in hand when switching exo-suits; build AmmoBoxes for Tool during Terminal-controlled creation, not Tool-controlled creation order of task cleanup reversed to avoid index mismatch; added itsm to TerminalDefinition common 5x5 AmmoBox size; added vehicle weapon ammo boxes to terminal added error catching messages; stopped odd double-registering issue early resolved issue where multiple subtasks started their main task multiple times; added checks that an object does not register a new GUID when it already has one wrote unregistration code for Selling items back through the Terminal, repairing logic along the way; also, wrote a top-level GUID find for the Player for use of MoveItem added framework for starting on Loadouts; managed issue with parent tasks starting before being summoned by child subtasks, often resulting in the complete skip of the execution phase of the parent; refactored registration tasks in WSA modified Tool structure, exposing the AmmoSlot list a bit more stuff stuff Tool ammo slot changes to default and comments basic loadout framework for Infantry; need to integrate initial work on FavoritesRequest packet tests for FavoritesRequest packet increased size of number pool for testing; wrote an algorithm that translates to and from the simplified version of objects stored in loadouts refactored the tasking for adding Equipment and removing Equipment updated the inventory so the Map of items does not have to rely on the GUID of an item being set before the item is inserted untested routine for registering a player character; pushing all changes before making significant changes to the client init code structure added to comments of BeginZoningMessage; transitioned player through and initial step of a more proper login GUID association the current avatar is properly registered and there is something of a workflow with the messages and packets corrected another bit of logic where inventories used to be indexed by object GUID in AvatarConverter; reversed unregister-remove task sequence such that GUID-less object is not allowed to exist in a stable object hierarchy working Loadout loading added identification functions to GlobalDefinitions; echo ObjectDelete back to client accidentally got rid of something in WSA, but now restored; adding extra details to Terminal operations separated Terminal into separate files and moved files into their own package under \objects\ for now; can delete loadouts now in WSA better handling of ReloadMessage and MoveItemMessage framework for better support involving dropping and picking up items code comments and small modifications, such as the location and structure of the Terminal Equipment definitions wrote comments in GlobalDefinitions; modified code so that a primitive form of player synchronization now occurs for future testing added code to display already-dropped Equipment on the ground; limitations are explained; moved TaskResolver to more a global location, though I don't know if that helps modified avatar unregister logic to ensure vacating player is deleted from other clients 'properly' more comments; improved checks for MoveItemMessage; squared distances as necessary subtle changes to login scripting so that test character is always offered re-organizing the functions in WSA so that only the local objects separate the two message processing blocks
2017-05-30 22:46:01 +00:00
InventoryItemData(ObjectClass.nano_dispenser, PlanetSideGUID(2935), 0, WeaponData(0x6, 0x0, ObjectClass.armor_canister, PlanetSideGUID(3426), 0, AmmoBoxData())) ::
InventoryItemData(ObjectClass.armor_canister, PlanetSideGUID(4090), 45, AmmoBoxData()) ::
InventoryItemData(ObjectClass.armor_canister, PlanetSideGUID(3326), 78, AmmoBoxData()) ::
Nil
)
)
val msg = ObjectCreateMessage(ObjectClass.locker_container, PlanetSideGUID(3148), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_locker_container
}
"encode (character, alive)" 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.Loser4,
MeritCommendation.HeavyInfantry3,
MeritCommendation.TankBuster6,
MeritCommendation.SixYearNC
)
),
255, 253,
UniformStyle.ThirdUpgrade,
5,
Some(ImplantEffects.NoEffects),
Some(Cosmetics(true, true, true, true, false)),
InventoryData(
new stuff for player server classes; this update is not yet complete adjusted sample Reload code and added insertion and removal functions for inventory more work on player classes; moving PacketResolution to another branch decoupling GUIDs from objects; introduced Ammo enum; minor adjustments to inventory system; different object/class hierarchy transferring basic files from another branch converted from get/set to accessor/mutator; resolved conflict from name changes refactored basic components such as GUID and location/orientation utilities kludge; more fields are given accessor and mutators; create package for vehicle-specific classes GUID assurance, now with less object creation test files; changes to how AmmoBox initializes sorry, a little bit of everything, so much I forgot to write it all down switched to a unified fire mode object internal to a Tool importing a heavily modified version of my GUID manager objects from the laternate branch; not finished or tested yet created a Trait to make Key private, sources and selectors to allow NumberPools to exist independent of a NumberSource; placed Ascending into a misc folder swapped the Return methods for selectors so that the more primitive logic is the one that needs to be overriden; renamed a selector to be more common; had to update some copyright messages fixed major logic issue with NumberPool; added comments to NumberSource files NumberSource tests simplified and made more consistent the method naming convention of NumberSources comments for NumberSelectors starting on NumberSelector tests modifications that should better support number pools; added a pool hub that acts on a predefined source adjustment to how Tools and FireModeDefintion keep track of ammunition and ammunition slots; I don't think this is sufficient small additions to Tools; filled out simple tests for other three Selectors added object lookup notation for the pool hub added more NumberSelector tests; removed the word 'Number' from subclass names re-named classes, re-packaged classes, re-named packages; created new Selector, split pools to create a fallback for the NumberPoolHub changes to NumberPool classes; tests on NumberPool classes changes to NumberPool classes; tests on NumberPool classes2 some robust testing for NumberPoolHub, plus necessary modifications to other files register and unregister functions now use Success and Failure conditions, save for one true thrown Exception reduced the flow of certain algorithm chains, mainly by adding match statements and removing Exceptions error message text the same thing as the last commit, but with NumberPools rather than NumberPoolHub various types of freeform registration added sorting functions to Selectors to allow for hotswapping for number pools, especially to and from SpecificSelector; tests for NumberPoolHub get numbers from an Array of indices, not the list of Numbers, in SimplePool added a class to represent the four types of recovery kits comments on Kit files created package for supporting equipment classes; renamed /definition/ package adding class for items that construct deployables, the router telepad included added SimpleItem, classes for game Equipment that do not have internal state; re-organized ObjectDefinition files and the game objects they create to more naturally move around EquipmentSize and InventoryTile (size) added SimpleItem tests (what they are...); removed example code that has hogging an import from AmmoBox auto-sort for loading and fitting former inventory content back into the inventory method of finding first available position to fit an certain size block in the inventory changed CheckCollision return type to provide Try[List[Int]; fixed all existing references and tests wrote comments for GridInventory methods; changed insertion param to be of form 'key -> element' adding features to Player; created definitions for Player class; re-grouped ConstructionItem enumerations initial work on implants; shuffled classes to better accommodate the new implant system, I think wrote some tests for Implants; fixing Implant logic wrote tests for Player class and made adjustments where necessary basic initialization during Player creation based on exo-suit type three wrapper Actors for the normal classes comments on code modified tests to improve accountability; added Resolver class to deal with multiple tasks that contribute to a larger task changed Tools to an internal AmmoBox; don't have to def -= symbol if I def _= symbol LivePlayerList -> MasterPlayerList, and added a Fit def for Player that checks holsters as well as inventory example of packet conversion can be found with AmmoBoxDefinition added conversion for ToolDefinition added all Equipment packet conversion functionality; started working on Avatar-related conversions continued effort towards a working Player packet conversion test subclasses of Equipment apparently do not need to overide the type of the PacketConverter for generics the logical conclusion: it doesn't matter what generics Packet returns so long as it returns an ObjectCreateConverter[] type separated converters from definitions into files changed some configuration information to final; added a bunch of converters, not fully tested though changed function names in converters replaced WSA packet-driven OCDM with Player object OCDM; upgrade to Float angular data added partial support for LockerContainer; changed Equipment defaults to a common value changes to AvatarConverter to include 5th slot; changes to VehicleConverter to make work; implementation of Fury in Vehicle->packet example in WSA added a seat definition and renovated how the weapon controlled from a seat can be found comments to files mainly; non-essential functionality to some classes, mostly access determination moved converter tests to their own test file write more of this test added ServiceManager, as it is useful pool range changes added AvatarService, as it is useful straightened out the GUID actors; added the static method for adding AmmoBoxes (to be converted later) chnages to task resolution operation complicated Task and TaskResolver logic is now possible; for example, you can schedule giving an AmmoBox a GUID, before giving a Tool a GUID, before placing the Tool in a player's hand; see Suppressor example in WSA separated the Task trait and the TaskResolver actor into their own classes, moving the former RegistrationTaskResolver class into the /misc/ folder; deleted old backup copy of HubActor; modifications for PoC and supported tests added better support and protection against putting things in the wrong hand when using inventories and the Player.Slot(n) function GlobalDefinitions file; added laze pointer as an SItem, and gave it the command detonater management code; additionally fixed spelling of 'detonat[o]r' in Codec; early Terminal class work updated tests to GlobalDefinitions entries; Terminal works but I don't like it played with GUID pooling workflow, though to little avail; modifications to Terminal purchasing workflow, but still very volatile modified NumberPoolActor and NumberPoolAccessor to make them more straightforward and not use akka ask as a go-between fixed recovery options so that they do not cause more messages trailing newline InventoryItem (packet data) renamed InventoryItemData to remove ambiguity; Terminal functionality improved, allowing for swapping of exo-suits and the restoration of equipment positions remove yet-unsupported Terminal messaging made Terminal message more specific; can now put equipment into empty slot on exo-suit change; should report changes better re-organized function calls to preserved items removed from holster slots on exo-suit change moved predicate to the end of the list of params for recoverInventory so that repetition can be eliminated and a default value can be assigned issues with making Tool; committing changes before revert of NumberPoolActor and NumberPoolAccessorActor to see if those broke it a necessary evil, the reverting of these two Actors; subtask resolution does not work unless I do so, for now restored the registration portion of tasking back to where it previously was (and better?) NumberPoolActor and the ...AccessorActor are back to a comfortable place (and better?) re-draw object in hand when switching exo-suits; build AmmoBoxes for Tool during Terminal-controlled creation, not Tool-controlled creation order of task cleanup reversed to avoid index mismatch; added itsm to TerminalDefinition common 5x5 AmmoBox size; added vehicle weapon ammo boxes to terminal added error catching messages; stopped odd double-registering issue early resolved issue where multiple subtasks started their main task multiple times; added checks that an object does not register a new GUID when it already has one wrote unregistration code for Selling items back through the Terminal, repairing logic along the way; also, wrote a top-level GUID find for the Player for use of MoveItem added framework for starting on Loadouts; managed issue with parent tasks starting before being summoned by child subtasks, often resulting in the complete skip of the execution phase of the parent; refactored registration tasks in WSA modified Tool structure, exposing the AmmoSlot list a bit more stuff stuff Tool ammo slot changes to default and comments basic loadout framework for Infantry; need to integrate initial work on FavoritesRequest packet tests for FavoritesRequest packet increased size of number pool for testing; wrote an algorithm that translates to and from the simplified version of objects stored in loadouts refactored the tasking for adding Equipment and removing Equipment updated the inventory so the Map of items does not have to rely on the GUID of an item being set before the item is inserted untested routine for registering a player character; pushing all changes before making significant changes to the client init code structure added to comments of BeginZoningMessage; transitioned player through and initial step of a more proper login GUID association the current avatar is properly registered and there is something of a workflow with the messages and packets corrected another bit of logic where inventories used to be indexed by object GUID in AvatarConverter; reversed unregister-remove task sequence such that GUID-less object is not allowed to exist in a stable object hierarchy working Loadout loading added identification functions to GlobalDefinitions; echo ObjectDelete back to client accidentally got rid of something in WSA, but now restored; adding extra details to Terminal operations separated Terminal into separate files and moved files into their own package under \objects\ for now; can delete loadouts now in WSA better handling of ReloadMessage and MoveItemMessage framework for better support involving dropping and picking up items code comments and small modifications, such as the location and structure of the Terminal Equipment definitions wrote comments in GlobalDefinitions; modified code so that a primitive form of player synchronization now occurs for future testing added code to display already-dropped Equipment on the ground; limitations are explained; moved TaskResolver to more a global location, though I don't know if that helps modified avatar unregister logic to ensure vacating player is deleted from other clients 'properly' more comments; improved checks for MoveItemMessage; squared distances as necessary subtle changes to login scripting so that test character is always offered re-organizing the functions in WSA so that only the local objects separate the two message processing blocks
2017-05-30 22:46:01 +00:00
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
)
val msg = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(3902), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
val pkt_bitv = pkt.toBitVector
val ori_bitv = string_character.toBitVector
pkt_bitv.take(452) mustEqual ori_bitv.take(452) //skip 126
pkt_bitv.drop(578).take(438) mustEqual ori_bitv.drop(578).take(438) //skip 2
pkt_bitv.drop(1018).take(17) mustEqual ori_bitv.drop(1018).take(17) //skip 11
pkt_bitv.drop(1046).take(147) mustEqual ori_bitv.drop(1046).take(147) //skip 3
pkt_bitv.drop(1196) mustEqual ori_bitv.drop(1196)
//TODO work on CharacterData to make this pass as a single stream
}
"encode (character, 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.Jacking,
MeritCommendation.ScavengerTR6,
MeritCommendation.AMSSupport4,
MeritCommendation.SixYearTR
)
),
0, 0,
UniformStyle.ThirdUpgrade,
2,
None,
Some(Cosmetics(true, true, true, true, false)),
None,
DrawnSlot.Pistol1
)
val msg = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(3380), obj)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
val pkt_bitv = pkt.toBitVector
val ori_bitv = string_character_backpack.toBitVector
pkt_bitv.take(300) mustEqual ori_bitv.take(300) //skip 2
pkt_bitv.drop(302).take(14) mustEqual ori_bitv.drop(302).take(14) //skip 126
pkt_bitv.drop(442).take(305) mustEqual ori_bitv.drop(442).take(305) //skip 1
pkt_bitv.drop(748).take(9) mustEqual ori_bitv.drop(748).take(9) // skip 2
pkt_bitv.drop(759).take(157) mustEqual ori_bitv.drop(759).take(157) //skip 1
pkt_bitv.drop(917) mustEqual ori_bitv.drop(917)
//TODO work on CharacterData to make this pass as a single stream
}
}