PSF-BotServer/src/test/scala/game/objectcreate/CharacterDataTest.scala
Fate-JH 7e899e9ef3
No Uniform, No Helmet, No Service (#1040)
* added extra checks to eliminate cosmetics from the packet transcoder where having them defined would be considered harmful to the data

* new uniform options; moved cosmetics class file

* assurance that the cosmetics settings are accurate during transitory points
2023-03-16 13:12:54 -04:00

601 lines
22 KiB
Scala

// Copyright (c) 2017 PSForever
package game.objectcreate
import net.psforever.packet.PacketCoding
import net.psforever.packet.game.ObjectCreateMessage
import net.psforever.packet.game.objectcreate._
import net.psforever.types._
import org.specs2.mutable._
import scodec.bits._
class CharacterDataTest extends Specification {
val string =
hex"17 73070000 BC8 3E0F 6C2D7 65535 CA16 00 00 09 9741E4F804000000 234530063007200610077006E00790052006F006E006E0069006500 220B7 E67B540404001000000000022B50100 268042006C00610063006B002000420065007200650074002000410072006D006F007500720065006400200043006F00720070007300 1700E0030050040003BC00000234040001A004000 3FFF67A8F A0A5424E0E800000000080952A9C3A03000001081103E040000000A023782F1080C0000016244108200000000808382403A030000014284C3A0C0000000202512F00B80C00000578F80F840000000280838B3C320300000080"
//string seated was intentionally-produced test data
val string_seated =
hex"170307000069023c83e0f800000011a0530063007200610077006e00790052006f006e006e0069006500220b700000000000000000000000" ++
hex"06800000268042006c00610063006b002000420065007200650074002000410072006d006f007500720065006400200043006f0072007000" ++
hex"73001700e0030050040003bc00000234040001a00400020a8fa0a5424e0e800000000080952a9c3a03000001081103e040000000a023782f" ++
hex"1080c0000016244108200000000808382403a030000014284c3a0c0000000202512f00b80c00000578f80f840000000280838b3c320300000080"
val string_backpack =
hex"17 9C030000 BC8 340D F20A9 3956C AF0D 00 00 73 480000 87041006E00670065006C006C006F00 4A148 0000000000000000000000005C54200 24404F0072006900670069006E0061006C00200044006900730074007200690063007400 1740180181E8000000C202000042000000D202000000010A3C00"
"CharacterData" should {
"decode" in {
PacketCoding.decodePacket(string).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 match {
case 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(5.1749997f, -1.5749999f, 0.0f)
basic match {
case CharacterAppearanceData(a, b, ribbons) =>
a.app.name mustEqual "ScrawnyRonnie"
a.app.faction mustEqual PlanetSideEmpire.TR
a.app.sex mustEqual CharacterSex.Male
a.app.head mustEqual 5
a.app.voice mustEqual CharacterVoice.Voice5
a.data.bops mustEqual false
a.data.v1 mustEqual false
a.data.v2.isEmpty mustEqual true
a.data.jammered mustEqual false
a.data.v4.isEmpty mustEqual true
a.data.v5.isEmpty mustEqual true
a.exosuit mustEqual ExoSuitType.Reinforced
a.unk5 mustEqual 0
a.char_id mustEqual 30777081L
a.unk7 mustEqual 1
a.unk8 mustEqual 4
a.unk9 mustEqual 0
a.unkA mustEqual 0
b.outfit_id mustEqual 316554L
b.outfit_name mustEqual "Black Beret Armoured Corps"
b.outfit_logo mustEqual 23
b.backpack mustEqual false
b.facingPitch mustEqual -39.375f
b.facingYawUpper mustEqual 0
b.lfs mustEqual false
b.grenade_state mustEqual GrenadeState.None
b.is_cloaking mustEqual false
b.charging_pose mustEqual false
b.on_zipline.isEmpty mustEqual true
b.unk1 mustEqual false
b.unk2 mustEqual false
b.unk3 mustEqual false
b.unk4 mustEqual false
b.unk5 mustEqual false
b.unk6 mustEqual false
b.unk7 mustEqual false
ribbons.upper mustEqual MeritCommendation.MarkovVeteran
ribbons.middle mustEqual MeritCommendation.HeavyInfantry4
ribbons.lower mustEqual MeritCommendation.TankBuster7
ribbons.tos mustEqual MeritCommendation.SixYearTR
case _ =>
ko
}
char.health mustEqual 255
char.armor mustEqual 253
char.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
char.command_rank mustEqual 5
char.implant_effects.length mustEqual 1
char.implant_effects.head mustEqual ImplantEffects.NoEffects
char.cosmetics must beSome(
beEqualTo(Set(Cosmetic.NoHelmet, Cosmetic.Beret, Cosmetic.Sunglasses, Cosmetic.Earpiece))
)
char.unk mustEqual 7
//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
}
}
//
"decode (seated)" in {
PacketCoding.decodePacket(string_seated).require match {
case ObjectCreateMessage(len, cls, guid, parent, data) =>
len mustEqual 1795
cls mustEqual ObjectClass.avatar
guid mustEqual PlanetSideGUID(3902)
parent.contains(ObjectCreateMessageParent(PlanetSideGUID(1234), 0)) mustEqual true
data match {
case PlayerData(None, basic, _, _, _) =>
basic match {
case CharacterAppearanceData(a, b, ribbons) =>
a.app.name mustEqual "ScrawnyRonnie"
a.app.faction mustEqual PlanetSideEmpire.TR
a.app.sex mustEqual CharacterSex.Male
a.app.head mustEqual 5
a.app.voice mustEqual CharacterVoice.Voice5
a.data.bops mustEqual false
a.data.v1 mustEqual false
a.data.v2.isEmpty mustEqual true
a.data.jammered mustEqual false
a.data.v4.isEmpty mustEqual true
a.data.v5.isEmpty mustEqual true
a.exosuit mustEqual ExoSuitType.Reinforced
a.unk5 mustEqual 0
a.char_id mustEqual 192L
a.unk7 mustEqual 0
a.unk8 mustEqual 0
a.unk9 mustEqual 0
a.unkA mustEqual 0
b.outfit_id mustEqual 26L
b.outfit_name mustEqual "Black Beret Armoured Corps"
b.outfit_logo mustEqual 23
b.backpack mustEqual false
b.facingPitch mustEqual -39.375f
b.facingYawUpper mustEqual 0
b.lfs mustEqual false
b.grenade_state mustEqual GrenadeState.None
b.is_cloaking mustEqual false
b.charging_pose mustEqual false
b.on_zipline.isEmpty mustEqual true
b.unk1 mustEqual false
b.unk2 mustEqual false
b.unk3 mustEqual false
b.unk4 mustEqual false
b.unk5 mustEqual false
b.unk6 mustEqual false
b.unk7 mustEqual false
ribbons.upper mustEqual MeritCommendation.MarkovVeteran
ribbons.middle mustEqual MeritCommendation.HeavyInfantry4
ribbons.lower mustEqual MeritCommendation.TankBuster7
ribbons.tos mustEqual MeritCommendation.SixYearTR
//etc..
case _ =>
ko
}
case _ =>
ko
}
case _ =>
ko
}
}
"decode (backpack)" in {
PacketCoding.decodePacket(string_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 match {
case 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 match {
case CharacterAppearanceData(a, b, ribbons) =>
a.app.name mustEqual "Angello"
a.app.faction mustEqual PlanetSideEmpire.VS
a.app.sex mustEqual CharacterSex.Male
a.app.head mustEqual 10
a.app.voice mustEqual CharacterVoice.Voice2
a.data.bops mustEqual false
a.data.v1 mustEqual false
a.data.v2.isEmpty mustEqual true
a.data.jammered mustEqual false
a.data.v4.isEmpty mustEqual true
a.data.v5.isEmpty mustEqual true
a.exosuit mustEqual ExoSuitType.MAX
a.unk5 mustEqual 1
a.char_id mustEqual 0L
a.unk7 mustEqual 0
a.unk8 mustEqual 0
a.unk9 mustEqual 0
a.unkA mustEqual 0
b.outfit_id mustEqual 529687L
b.outfit_name mustEqual "Original District"
b.outfit_logo mustEqual 23
b.backpack mustEqual true
b.facingPitch mustEqual -8.4375f
b.facingYawUpper mustEqual 0
b.lfs mustEqual false
b.grenade_state mustEqual GrenadeState.None
b.is_cloaking mustEqual false
b.charging_pose mustEqual false
b.on_zipline.isEmpty mustEqual true
b.unk1 mustEqual false
b.unk2 mustEqual false
b.unk3 mustEqual false
b.unk4 mustEqual false
b.unk5 mustEqual false
b.unk6 mustEqual false
b.unk7 mustEqual false
ribbons.upper mustEqual MeritCommendation.Jacking2
ribbons.middle mustEqual MeritCommendation.ScavengerVS1
ribbons.lower mustEqual MeritCommendation.AMSSupport4
ribbons.tos mustEqual MeritCommendation.SixYearVS
//etc..
case _ =>
ko
}
char.health mustEqual 0
char.armor mustEqual 0
char.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
char.command_rank mustEqual 2
char.implant_effects.isEmpty mustEqual true
char.cosmetics must beSome(
beEqualTo(Set(Cosmetic.NoHelmet, Cosmetic.Beret, Cosmetic.Sunglasses, Cosmetic.Earpiece))
)
char.unk mustEqual 1
hand mustEqual DrawnSlot.Pistol1
case _ =>
ko
}
case _ =>
ko
}
}
"encode" in {
val pos: PlacementData = PlacementData(
Vector3(3674.8438f, 2726.789f, 91.15625f),
Vector3(0f, 0f, 64.6875f),
Some(Vector3(5.1749997f, -1.5749999f, 0.0f))
)
val a: Int => CharacterAppearanceA = CharacterAppearanceA(
BasicCharacterData(
"ScrawnyRonnie",
PlanetSideEmpire.TR,
CharacterSex.Male,
5,
CharacterVoice.Voice5
),
CommonFieldData(
PlanetSideEmpire.TR,
false,
false,
false,
None,
false,
None,
None,
PlanetSideGUID(0)
),
ExoSuitType.Reinforced,
0,
30777081L,
1,
4,
0,
0
)
val b: (Boolean, Int) => CharacterAppearanceB = CharacterAppearanceB(
316554L,
"Black Beret Armoured Corps",
23,
false,
false,
false,
false,
false,
-39.375f,
0f,
false,
GrenadeState.None,
false,
false,
false,
false,
false,
None
)
val app: Int => CharacterAppearanceData = CharacterAppearanceData(
a,
b,
RibbonBars(
MeritCommendation.MarkovVeteran,
MeritCommendation.HeavyInfantry4,
MeritCommendation.TankBuster7,
MeritCommendation.SixYearTR
)
)
val char: (Boolean, Boolean) => CharacterData = CharacterData(
255,
253,
UniformStyle.ThirdUpgrade,
7,
5,
List(ImplantEffects.NoEffects),
Some(Set(Cosmetic.NoHelmet, Cosmetic.Beret, Cosmetic.Sunglasses, Cosmetic.Earpiece))
)
val inv = InventoryData(
InventoryItemData(
ObjectClass.plasma_grenade,
PlanetSideGUID(3662),
0,
WeaponData(0, 0, ObjectClass.plasma_grenade_ammo, PlanetSideGUID(3751), 0, CommonFieldData()(false))
) ::
InventoryItemData(
ObjectClass.bank,
PlanetSideGUID(3908),
1,
WeaponData(0, 0, 1, ObjectClass.armor_canister, PlanetSideGUID(4143), 0, CommonFieldData()(false))
) ::
InventoryItemData(
ObjectClass.mini_chaingun,
PlanetSideGUID(4164),
2,
WeaponData(0, 0, ObjectClass.bullet_9mm, PlanetSideGUID(3728), 0, CommonFieldData()(false))
) ::
InventoryItemData(
ObjectClass.phoenix,
PlanetSideGUID(3603),
3,
WeaponData(0, 0, ObjectClass.phoenix_missile, PlanetSideGUID(3056), 0, CommonFieldData()(false))
) ::
InventoryItemData(
ObjectClass.chainblade,
PlanetSideGUID(4088),
4,
WeaponData(0, 0, 1, ObjectClass.melee_ammo, PlanetSideGUID(3279), 0, CommonFieldData()(false))
) ::
Nil
)
val obj = PlayerData(pos, app, char, inv, DrawnSlot.Rifle1)
val msg = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(3902), obj)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual string
}
"encode (seated)" in {
val a: Int => CharacterAppearanceA = CharacterAppearanceA(
BasicCharacterData(
"ScrawnyRonnie",
PlanetSideEmpire.TR,
CharacterSex.Male,
5,
CharacterVoice.Voice5
),
CommonFieldData(
PlanetSideEmpire.TR,
false,
false,
false,
None,
false,
None,
None,
PlanetSideGUID(0)
),
ExoSuitType.Reinforced,
0,
192L,
0,
0,
0,
0
)
val b: (Boolean, Int) => CharacterAppearanceB = CharacterAppearanceB(
26L,
"Black Beret Armoured Corps",
23,
false,
false,
false,
false,
false,
-39.375f,
0f,
false,
GrenadeState.None,
false,
false,
false,
false,
false,
None
)
val app: Int => CharacterAppearanceData = CharacterAppearanceData(
a,
b,
RibbonBars(
MeritCommendation.MarkovVeteran,
MeritCommendation.HeavyInfantry4,
MeritCommendation.TankBuster7,
MeritCommendation.SixYearTR
)
)
val char: (Boolean, Boolean) => CharacterData = CharacterData(
255,
253,
UniformStyle.ThirdUpgrade,
5,
List(ImplantEffects.NoEffects),
Some(Set(Cosmetic.NoHelmet, Cosmetic.Beret, Cosmetic.Sunglasses, Cosmetic.Earpiece))
)
val inv = InventoryData(
InventoryItemData(
ObjectClass.plasma_grenade,
PlanetSideGUID(3662),
0,
WeaponData(0, 0, ObjectClass.plasma_grenade_ammo, PlanetSideGUID(3751), 0, CommonFieldData()(false))
) ::
InventoryItemData(
ObjectClass.bank,
PlanetSideGUID(3908),
1,
WeaponData(0, 0, 1, ObjectClass.armor_canister, PlanetSideGUID(4143), 0, CommonFieldData()(false))
) ::
InventoryItemData(
ObjectClass.mini_chaingun,
PlanetSideGUID(4164),
2,
WeaponData(0, 0, ObjectClass.bullet_9mm, PlanetSideGUID(3728), 0, CommonFieldData()(false))
) ::
InventoryItemData(
ObjectClass.phoenix,
PlanetSideGUID(3603),
3,
WeaponData(0, 0, ObjectClass.phoenix_missile, PlanetSideGUID(3056), 0, CommonFieldData()(false))
) ::
InventoryItemData(
ObjectClass.chainblade,
PlanetSideGUID(4088),
4,
WeaponData(0, 0, 1, ObjectClass.melee_ammo, PlanetSideGUID(3279), 0, CommonFieldData()(false))
) ::
Nil
)
val obj = PlayerData(app, char, inv, DrawnSlot.Rifle1)
val msg = ObjectCreateMessage(
ObjectClass.avatar,
PlanetSideGUID(3902),
ObjectCreateMessageParent(PlanetSideGUID(1234), 0),
obj
)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual string_seated
}
"encode (backpack)" in {
val pos = PlacementData(
Vector3(4629.8906f, 6316.4453f, 54.734375f),
Vector3(0, 0, 126.5625f)
)
val a: Int => CharacterAppearanceA = CharacterAppearanceA(
BasicCharacterData(
"Angello",
PlanetSideEmpire.VS,
CharacterSex.Male,
10,
CharacterVoice.Voice2
),
CommonFieldData(
PlanetSideEmpire.VS,
false,
true,
false,
None,
false,
None,
None,
PlanetSideGUID(0)
),
ExoSuitType.MAX,
1,
0L,
0,
0,
0,
0
)
val b: (Boolean, Int) => CharacterAppearanceB = CharacterAppearanceB(
529687L,
"Original District",
23,
false, //unk1
true, //backpack
false, //unk2
false, //unk3
false, //unk4
351.5625f,
0f, //also: -8.4375f, 0f
false, //lfs
GrenadeState.None,
false, //is_cloaking
false, //unk5
false, //unk6
false, //charging_pose
false, //unk7
None
)
val app: Int => CharacterAppearanceData = CharacterAppearanceData(
a,
b,
RibbonBars(
MeritCommendation.Jacking2,
MeritCommendation.ScavengerVS1,
MeritCommendation.AMSSupport4,
MeritCommendation.SixYearVS
)
)
val char: (Boolean, Boolean) => CharacterData = CharacterData(
0,
0,
UniformStyle.ThirdUpgrade,
1,
List(),
Some(Set(Cosmetic.NoHelmet, Cosmetic.Beret, Cosmetic.Sunglasses, Cosmetic.Earpiece))
)
val obj = PlayerData(pos, app, char, DrawnSlot.Pistol1)
val msg = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(3380), obj)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
//granular test
val pkt_bitv = pkt.toBitVector
val ori_bitv = string_backpack.toBitVector
pkt_bitv.take(916) mustEqual pkt_bitv.take(916) //skip 4
pkt_bitv.drop(920) mustEqual pkt_bitv.drop(920)
//TODO work on CharacterData to make this pass as a single stream
}
}
}