mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
Utility Vehicle Drivers (#1102)
* recalcalating name offsets for later; primary test is this player-driven AMS (see PSMU for details) * found fields in the ConnectToWorldRequest packet; clarifying field names in a variety of places; enough modifications to make an old packet transcode properly * it works? * giving VehicleFormat its own file; fixing imports
This commit is contained in:
parent
6b77281260
commit
6f4ceaee29
|
|
@ -106,7 +106,7 @@ class LoginActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], conne
|
||||||
|
|
||||||
accountLogin(username, password.getOrElse(""))
|
accountLogin(username, password.getOrElse(""))
|
||||||
|
|
||||||
case ConnectToWorldRequestMessage(name, _, _, _, _, _, _) =>
|
case ConnectToWorldRequestMessage(name, _, _, _, _, _, _, _) =>
|
||||||
log.info(s"Connect to world request for '$name'")
|
log.info(s"Connect to world request for '$name'")
|
||||||
val response = ConnectToWorldMessage(serverName, publicAddress.getAddress.getHostAddress, publicAddress.getPort)
|
val response = ConnectToWorldMessage(serverName, publicAddress.getAddress.getHostAddress, publicAddress.getPort)
|
||||||
middlewareActor ! MiddlewareActor.Send(response)
|
middlewareActor ! MiddlewareActor.Send(response)
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,7 @@ class SessionData(
|
||||||
/* packets */
|
/* packets */
|
||||||
|
|
||||||
def handleConnectToWorldRequest(pkt: ConnectToWorldRequestMessage)(implicit context: ActorContext): Unit = {
|
def handleConnectToWorldRequest(pkt: ConnectToWorldRequestMessage)(implicit context: ActorContext): Unit = {
|
||||||
val ConnectToWorldRequestMessage(_, token, majorVersion, minorVersion, revision, buildDate, _) = pkt
|
val ConnectToWorldRequestMessage(_, token, majorVersion, minorVersion, revision, buildDate, _, _) = pkt
|
||||||
log.trace(
|
log.trace(
|
||||||
s"ConnectToWorldRequestMessage: client with versioning $majorVersion.$minorVersion.$revision, $buildDate has sent a token to the server"
|
s"ConnectToWorldRequestMessage: client with versioning $majorVersion.$minorVersion.$revision, $buildDate has sent a token to the server"
|
||||||
)
|
)
|
||||||
|
|
@ -217,9 +217,9 @@ class SessionData(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fallHeightTracker(pos.z)
|
fallHeightTracker(pos.z)
|
||||||
// if (isCrouching && !player.Crouching) {
|
// if (isCrouching && !player.Crouching) {
|
||||||
// //dev stuff goes here
|
// //dev stuff goes here
|
||||||
// }
|
// }
|
||||||
player.Position = pos
|
player.Position = pos
|
||||||
player.Velocity = vel
|
player.Velocity = vel
|
||||||
player.Orientation = Vector3(player.Orientation.x, pitch, yaw)
|
player.Orientation = Vector3(player.Orientation.x, pitch, yaw)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ package net.psforever.objects.definition.converter
|
||||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||||
import net.psforever.objects.vehicles.VehicleSubsystemEntry
|
import net.psforever.objects.vehicles.VehicleSubsystemEntry
|
||||||
import net.psforever.objects.{PlanetSideGameObject, Vehicle}
|
import net.psforever.objects.{PlanetSideGameObject, Vehicle}
|
||||||
import net.psforever.types.PlanetSideGUID
|
import net.psforever.types.{PlanetSideGUID, VehicleFormat}
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
|
|
||||||
import scala.util.{Failure, Success, Try}
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ package net.psforever.objects.definition.converter
|
||||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||||
import net.psforever.objects.vehicles.VehicleSubsystemEntry
|
import net.psforever.objects.vehicles.VehicleSubsystemEntry
|
||||||
import net.psforever.objects.{PlanetSideGameObject, Vehicle}
|
import net.psforever.objects.{PlanetSideGameObject, Vehicle}
|
||||||
import net.psforever.types.PlanetSideGUID
|
import net.psforever.types.{PlanetSideGUID, VehicleFormat}
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
|
|
||||||
import scala.util.{Failure, Success, Try}
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ object SeatConverter {
|
||||||
AvatarConverter.MakeAppearanceData(player),
|
AvatarConverter.MakeAppearanceData(player),
|
||||||
AvatarConverter.MakeCharacterData(player),
|
AvatarConverter.MakeCharacterData(player),
|
||||||
AvatarConverter.MakeInventoryData(player),
|
AvatarConverter.MakeInventoryData(player),
|
||||||
AvatarConverter.GetDrawnSlot(player),
|
DrawnSlot.None,
|
||||||
offset
|
offset
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,11 @@
|
||||||
package net.psforever.objects.definition.converter
|
package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
import net.psforever.objects.Vehicle
|
import net.psforever.objects.Vehicle
|
||||||
import net.psforever.packet.game.objectcreate.{UtilityVehicleData, VehicleFormat}
|
import net.psforever.packet.game.objectcreate.UtilityVehicleData
|
||||||
|
import net.psforever.types.VehicleFormat
|
||||||
|
|
||||||
class UtilityVehicleConverter extends VehicleConverter {
|
class UtilityVehicleConverter extends VehicleConverter {
|
||||||
override protected def SpecificFormatModifier: VehicleFormat.Value = VehicleFormat.Utility
|
override protected def SpecificFormatModifier: VehicleFormat = VehicleFormat.Utility
|
||||||
|
|
||||||
override protected def SpecificFormatData(obj: Vehicle) = Some(UtilityVehicleData(0))
|
override protected def SpecificFormatData(obj: Vehicle): Some[UtilityVehicleData] = Some(UtilityVehicleData(0))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,13 @@
|
||||||
package net.psforever.objects.definition.converter
|
package net.psforever.objects.definition.converter
|
||||||
|
|
||||||
import net.psforever.objects.Vehicle
|
import net.psforever.objects.Vehicle
|
||||||
import net.psforever.packet.game.objectcreate.{VariantVehicleData, VehicleFormat}
|
import net.psforever.packet.game.objectcreate.VariantVehicleData
|
||||||
|
import net.psforever.types.VehicleFormat
|
||||||
|
|
||||||
class VariantVehicleConverter extends VehicleConverter {
|
class VariantVehicleConverter extends VehicleConverter {
|
||||||
override protected def SpecificFormatModifier: VehicleFormat.Value = VehicleFormat.Variant
|
override protected def SpecificFormatModifier: VehicleFormat = VehicleFormat.Variant
|
||||||
|
|
||||||
override protected def SpecificFormatData(obj: Vehicle) = {
|
override protected def SpecificFormatData(obj: Vehicle): Some[VariantVehicleData] = {
|
||||||
/*
|
/*
|
||||||
landed is 0
|
landed is 0
|
||||||
flying is 7
|
flying is 7
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ package net.psforever.objects.definition.converter
|
||||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||||
import net.psforever.objects.{PlanetSideGameObject, Vehicle}
|
import net.psforever.objects.{PlanetSideGameObject, Vehicle}
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.types.{DriveState, PlanetSideGUID}
|
import net.psforever.types.{DriveState, PlanetSideGUID, VehicleFormat}
|
||||||
|
|
||||||
import scala.util.{Failure, Success, Try}
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
|
|
@ -75,9 +75,9 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private def MakeDriverSeat(obj: Vehicle): List[InventoryItemData.InventoryItem] = {
|
private def MakeDriverSeat(obj: Vehicle): List[InventoryItemData.InventoryItem] = {
|
||||||
val offset: Long = MountableInventory.InitialStreamLengthToSeatEntries(obj.Velocity.nonEmpty, SpecificFormatModifier)
|
|
||||||
obj.Seats(0).occupant match {
|
obj.Seats(0).occupant match {
|
||||||
case Some(player) =>
|
case Some(player) =>
|
||||||
|
val offset: Long = MountableInventory.InitialStreamLengthToSeatEntries(obj.Velocity.nonEmpty, SpecificFormatModifier)
|
||||||
List(InventoryItemData(ObjectClass.avatar, player.GUID, 0, SeatConverter.MakeSeat(player, offset)))
|
List(InventoryItemData(ObjectClass.avatar, player.GUID, 0, SeatConverter.MakeSeat(player, offset)))
|
||||||
case None =>
|
case None =>
|
||||||
Nil
|
Nil
|
||||||
|
|
@ -112,7 +112,7 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def SpecificFormatModifier: VehicleFormat.Value = VehicleFormat.Normal
|
protected def SpecificFormatModifier: VehicleFormat = VehicleFormat.Normal
|
||||||
|
|
||||||
protected def SpecificFormatData(obj: Vehicle): Option[SpecificVehicleData] = None
|
protected def SpecificFormatData(obj: Vehicle): Option[SpecificVehicleData] = None
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ final case class ConnectToWorldRequestMessage(
|
||||||
minorVersion: Long,
|
minorVersion: Long,
|
||||||
revision: Long,
|
revision: Long,
|
||||||
buildDate: String,
|
buildDate: String,
|
||||||
unknown: Int
|
unk1: Int,
|
||||||
|
unk2: Int
|
||||||
) extends PlanetSideGamePacket {
|
) extends PlanetSideGamePacket {
|
||||||
type Packet = ConnectToWorldRequestMessage
|
type Packet = ConnectToWorldRequestMessage
|
||||||
def opcode = GamePacketOpcode.ConnectToWorldRequestMessage
|
def opcode = GamePacketOpcode.ConnectToWorldRequestMessage
|
||||||
|
|
@ -29,6 +30,7 @@ object ConnectToWorldRequestMessage extends Marshallable[ConnectToWorldRequestMe
|
||||||
("minor_version" | uint32L) ::
|
("minor_version" | uint32L) ::
|
||||||
("revision" | uint32L) ::
|
("revision" | uint32L) ::
|
||||||
("build_date" | PacketHelpers.encodedString) ::
|
("build_date" | PacketHelpers.encodedString) ::
|
||||||
("unknown_short" | uint16L)
|
("unk1" | uint8) ::
|
||||||
|
("unk2" | uint8)
|
||||||
).as[ConnectToWorldRequestMessage]
|
).as[ConnectToWorldRequestMessage]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
package net.psforever.packet.game.objectcreate
|
package net.psforever.packet.game.objectcreate
|
||||||
|
|
||||||
import net.psforever.packet.Marshallable
|
import net.psforever.packet.Marshallable
|
||||||
|
import net.psforever.types.VehicleFormat
|
||||||
import scodec.{Attempt, Codec, Err}
|
import scodec.{Attempt, Codec, Err}
|
||||||
import shapeless.HNil
|
import shapeless.HNil
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
|
|
|
||||||
|
|
@ -171,9 +171,9 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
||||||
app.faction,
|
app.faction,
|
||||||
black_ops,
|
black_ops,
|
||||||
altModel,
|
altModel,
|
||||||
false,
|
v1 = false,
|
||||||
None,
|
None,
|
||||||
false,
|
jammered=false,
|
||||||
None,
|
None,
|
||||||
if (jammered) {
|
if (jammered) {
|
||||||
Some(0)
|
Some(0)
|
||||||
|
|
@ -194,20 +194,20 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
||||||
outfit_name.length,
|
outfit_name.length,
|
||||||
outfit_name: String,
|
outfit_name: String,
|
||||||
outfit_logo: Int,
|
outfit_logo: Int,
|
||||||
false,
|
unk1 = false,
|
||||||
backpack,
|
backpack,
|
||||||
false,
|
unk2 = false,
|
||||||
false,
|
unk3 = false,
|
||||||
false,
|
unk4 = false,
|
||||||
facingPitch: Float,
|
facingPitch: Float,
|
||||||
facingYawUpper: Float,
|
facingYawUpper: Float,
|
||||||
lfs: Boolean,
|
lfs: Boolean,
|
||||||
grenade_state: GrenadeState.Value,
|
grenade_state: GrenadeState.Value,
|
||||||
is_cloaking: Boolean,
|
is_cloaking: Boolean,
|
||||||
false,
|
unk5 = false,
|
||||||
false,
|
unk6 = false,
|
||||||
charging_pose: Boolean,
|
charging_pose: Boolean,
|
||||||
false,
|
unk7 = false,
|
||||||
on_zipline
|
on_zipline
|
||||||
)(altModel, name_padding)
|
)(altModel, name_padding)
|
||||||
new CharacterAppearanceData(
|
new CharacterAppearanceData(
|
||||||
|
|
@ -283,11 +283,6 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
||||||
6
|
6
|
||||||
}
|
}
|
||||||
|
|
||||||
private val extra_codec: Codec[ExtraData] = (
|
|
||||||
("unk1" | bool) ::
|
|
||||||
("unk2" | bool)
|
|
||||||
).as[ExtraData]
|
|
||||||
|
|
||||||
private val zipline_codec: Codec[ZiplineData] = (
|
private val zipline_codec: Codec[ZiplineData] = (
|
||||||
("unk1" | uint32L) ::
|
("unk1" | uint32L) ::
|
||||||
("unk2" | bool)
|
("unk2" | bool)
|
||||||
|
|
@ -303,7 +298,7 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
||||||
("data" | CommonFieldData.codec) >>:~ { data =>
|
("data" | CommonFieldData.codec) >>:~ { data =>
|
||||||
("name" | PacketHelpers.encodedWideStringAligned(namePadding(name_padding, data.v2))) ::
|
("name" | PacketHelpers.encodedWideStringAligned(namePadding(name_padding, data.v2))) ::
|
||||||
("exosuit" | ExoSuitType.codec) ::
|
("exosuit" | ExoSuitType.codec) ::
|
||||||
("unk5" | uint2) :: //unknown
|
("unk5" | uint2) ::
|
||||||
("sex" | CharacterSex.codec) ::
|
("sex" | CharacterSex.codec) ::
|
||||||
("head" | uint8L) ::
|
("head" | uint8L) ::
|
||||||
("voice" | CharacterVoice.codec) ::
|
("voice" | CharacterVoice.codec) ::
|
||||||
|
|
@ -398,14 +393,14 @@ object CharacterAppearanceData extends Marshallable[CharacterAppearanceData] {
|
||||||
("outfit_name" | PacketHelpers.encodedWideStringAligned(outfitNamePadding)) ::
|
("outfit_name" | PacketHelpers.encodedWideStringAligned(outfitNamePadding)) ::
|
||||||
("outfit_logo" | uint8L) ::
|
("outfit_logo" | uint8L) ::
|
||||||
("unk1" | bool) :: //unknown
|
("unk1" | bool) :: //unknown
|
||||||
conditional(alt_model, "backpack" | bool) :: //alt_model flag adds this bit; see ps.c:line#1069587
|
("backpack" | conditional(alt_model, bool)) :: //alt_model flag adds this bit; see ps.c:line#1069587
|
||||||
("unk2" | bool) :: //requires alt_model flag (does NOT require health == 0)
|
("unk2" | bool) :: //requires alt_model flag (does NOT require health == 0)
|
||||||
("unk3" | bool) :: //stream misalignment when set
|
("unk3" | bool) :: //stream misalignment when set
|
||||||
("unk4" | bool) :: //unknown
|
("unk4" | bool) :: //unknown
|
||||||
("facingPitch" | Angular.codec_zero_centered) ::
|
("facingPitch" | Angular.codec_zero_centered) ::
|
||||||
("facingYawUpper" | Angular.codec_zero_centered) ::
|
("facingYawUpper" | Angular.codec_zero_centered) ::
|
||||||
("lfs" | uint2) ::
|
("lfs" | uint2) ::
|
||||||
("grenade_state" | GrenadeState.codec_2u) :: //note: bin10 and bin11 are neutral (bin00 is not defined)
|
("grenade_state" | GrenadeState.codec_2u) ::
|
||||||
("is_cloaking" | bool) ::
|
("is_cloaking" | bool) ::
|
||||||
("unk5" | bool) :: //unknown
|
("unk5" | bool) :: //unknown
|
||||||
("unk6" | bool) :: //stream misalignment when set
|
("unk6" | bool) :: //stream misalignment when set
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ object CharacterData extends Marshallable[CharacterData] {
|
||||||
("health" | uint8L) :: //dead state when health == 0
|
("health" | uint8L) :: //dead state when health == 0
|
||||||
("armor" | uint8L) ::
|
("armor" | uint8L) ::
|
||||||
(("uniform_upgrade" | UniformStyle.codec) >>:~ { style =>
|
(("uniform_upgrade" | UniformStyle.codec) >>:~ { style =>
|
||||||
uint(3) :: //uniform_upgrade is actually interpreted as a 6u field, but the lower 3u seems to be discarded
|
uint(bits = 3) :: //uniform_upgrade is actually interpreted as a 6u field, but the lower 3u seems to be discarded
|
||||||
("command_rank" | uintL(3)) ::
|
("command_rank" | uintL(3)) ::
|
||||||
listOfN(uint2, "implant_effects" | ImplantEffects.codec) ::
|
listOfN(uint2, "implant_effects" | ImplantEffects.codec) ::
|
||||||
("cosmetics" | conditional(BattleRank.showCosmetics(style), Cosmetic.codec))
|
("cosmetics" | conditional(BattleRank.showCosmetics(style), Cosmetic.codec))
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
package net.psforever.packet.game.objectcreate
|
package net.psforever.packet.game.objectcreate
|
||||||
|
|
||||||
import net.psforever.packet.PacketHelpers
|
import net.psforever.packet.PacketHelpers
|
||||||
import net.psforever.types.PlanetSideGUID
|
import net.psforever.types.{PlanetSideGUID, VehicleFormat}
|
||||||
import scodec.Attempt.{Failure, Successful}
|
import scodec.Attempt.{Failure, Successful}
|
||||||
import scodec.{Codec, Err}
|
import scodec.{Codec, Err}
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
|
|
@ -23,7 +23,7 @@ object MountableInventory {
|
||||||
* @param format the subtype for this vehicle
|
* @param format the subtype for this vehicle
|
||||||
* @return a `Codec` that translates `InventoryData`
|
* @return a `Codec` that translates `InventoryData`
|
||||||
*/
|
*/
|
||||||
def custom_inventory_codec(hasVelocity: Boolean, format: VehicleFormat.Type): Codec[InventoryData] =
|
def custom_inventory_codec(hasVelocity: Boolean, format: VehicleFormat): Codec[InventoryData] =
|
||||||
custom_inventory_codec(InitialStreamLengthToSeatEntries(hasVelocity, format))
|
custom_inventory_codec(InitialStreamLengthToSeatEntries(hasVelocity, format))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -96,7 +96,7 @@ object MountableInventory {
|
||||||
accumulative: Long
|
accumulative: Long
|
||||||
): Player_Data = {
|
): Player_Data = {
|
||||||
val appearance = basic_appearance(CumulativeSeatedPlayerNamePadding(accumulative))
|
val appearance = basic_appearance(CumulativeSeatedPlayerNamePadding(accumulative))
|
||||||
Player_Data(None, appearance, character_data(appearance.b.backpack, true), Some(inventory), drawn_slot)(false)
|
Player_Data(None, appearance, character_data(false, true), Some(inventory), drawn_slot)(position_defined = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -118,7 +118,7 @@ object MountableInventory {
|
||||||
accumulative: Long
|
accumulative: Long
|
||||||
): Player_Data = {
|
): Player_Data = {
|
||||||
val appearance = basic_appearance(CumulativeSeatedPlayerNamePadding(accumulative))
|
val appearance = basic_appearance(CumulativeSeatedPlayerNamePadding(accumulative))
|
||||||
Player_Data.apply(None, appearance, character_data(appearance.b.backpack, true), None, drawn_slot)(false)
|
Player_Data.apply(None, appearance, character_data(false, true), None, drawn_slot)(position_defined = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -136,16 +136,8 @@ object MountableInventory {
|
||||||
* @param format the subtype for this vehicle
|
* @param format the subtype for this vehicle
|
||||||
* @return the length of the bitstream
|
* @return the length of the bitstream
|
||||||
*/
|
*/
|
||||||
def InitialStreamLengthToSeatEntries(hasVelocity: Boolean, format: VehicleFormat.Type): Long = {
|
def InitialStreamLengthToSeatEntries(hasVelocity: Boolean, format: VehicleFormat): Long = {
|
||||||
198 +
|
198 + (if (hasVelocity) 42 else 0) + format.value
|
||||||
(if (hasVelocity) 42 else 0) +
|
|
||||||
(format match {
|
|
||||||
case VehicleFormat.Utility => 6
|
|
||||||
case VehicleFormat.Variant => 8
|
|
||||||
case VehicleFormat.Battleframe => 1
|
|
||||||
case VehicleFormat.BattleframeFlight => 2
|
|
||||||
case _ => 0
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -156,10 +148,7 @@ object MountableInventory {
|
||||||
* @return the padding value, 0-7 bits
|
* @return the padding value, 0-7 bits
|
||||||
*/
|
*/
|
||||||
def CumulativeSeatedPlayerNamePadding(base: Long, next: Option[StreamBitSize]): Int = {
|
def CumulativeSeatedPlayerNamePadding(base: Long, next: Option[StreamBitSize]): Int = {
|
||||||
CumulativeSeatedPlayerNamePadding(base + (next match {
|
CumulativeSeatedPlayerNamePadding(base + next.map { _.bitsize }.getOrElse(0L))
|
||||||
case Some(o) => o.bitsize
|
|
||||||
case None => 0
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -199,7 +188,7 @@ object MountableInventory {
|
||||||
private def inventory_seat_codec(length: Long, offset: Int): Codec[Option[InventorySeat]] = {
|
private def inventory_seat_codec(length: Long, offset: Int): Codec[Option[InventorySeat]] = {
|
||||||
import shapeless.::
|
import shapeless.::
|
||||||
(
|
(
|
||||||
PacketHelpers.peek(uintL(11)) >>:~ { objClass =>
|
PacketHelpers.peek(uintL(bits = 11)) >>:~ { objClass =>
|
||||||
conditional(objClass == ObjectClass.avatar, seat_codec(offset)) >>:~ { seat =>
|
conditional(objClass == ObjectClass.avatar, seat_codec(offset)) >>:~ { seat =>
|
||||||
conditional(
|
conditional(
|
||||||
objClass == ObjectClass.avatar,
|
objClass == ObjectClass.avatar,
|
||||||
|
|
@ -240,7 +229,7 @@ object MountableInventory {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate data the is verified to involve a player who is seated (mounted) to the parent object at a given slot.
|
* Translate data that is verified to involve a player who is seated (mounted) to the parent object at a given slot.
|
||||||
* The operation performed by this `Codec` is very similar to `InternalSlot.codec`.
|
* The operation performed by this `Codec` is very similar to `InternalSlot.codec`.
|
||||||
* @param pad the padding offset for the player's name;
|
* @param pad the padding offset for the player's name;
|
||||||
* 0-7 bits;
|
* 0-7 bits;
|
||||||
|
|
@ -253,7 +242,7 @@ object MountableInventory {
|
||||||
private def seat_codec(pad: Int): Codec[InternalSlot] = {
|
private def seat_codec(pad: Int): Codec[InternalSlot] = {
|
||||||
import shapeless.::
|
import shapeless.::
|
||||||
(
|
(
|
||||||
("objectClass" | uintL(11)) ::
|
("objectClass" | uintL(bits = 11)) ::
|
||||||
("guid" | PlanetSideGUID.codec) ::
|
("guid" | PlanetSideGUID.codec) ::
|
||||||
("parentSlot" | PacketHelpers.encodedStringSize) ::
|
("parentSlot" | PacketHelpers.encodedStringSize) ::
|
||||||
("obj" | Player_Data.codec(pad))
|
("obj" | Player_Data.codec(pad))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) 2017 PSForever
|
// Copyright (c) 2017 PSForever
|
||||||
package net.psforever.packet.game.objectcreate
|
package net.psforever.packet.game.objectcreate
|
||||||
|
|
||||||
|
import net.psforever.types.VehicleFormat
|
||||||
import scodec.{Attempt, Codec, Err}
|
import scodec.{Attempt, Codec, Err}
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,21 +6,12 @@ import scodec.Attempt.{Failure, Successful}
|
||||||
import scodec.{Attempt, Codec, Err}
|
import scodec.{Attempt, Codec, Err}
|
||||||
import shapeless.HNil
|
import shapeless.HNil
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
import net.psforever.types.DriveState
|
import net.psforever.types.{DriveState, VehicleFormat}
|
||||||
|
|
||||||
/**
|
|
||||||
* An `Enumeration` of the various formats that known structures that the stream of bits for `VehicleData` can assume.
|
|
||||||
*/
|
|
||||||
object VehicleFormat extends Enumeration {
|
|
||||||
type Type = Value
|
|
||||||
|
|
||||||
val Battleframe, BattleframeFlight, Normal, Utility, Variant = Value
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic `Trait` connecting all of the vehicle data formats (excepting `Normal`/`None`).
|
* A basic `Trait` connecting all of the vehicle data formats (excepting `Normal`/`None`).
|
||||||
*/
|
*/
|
||||||
sealed abstract class SpecificVehicleData(val format: VehicleFormat.Value) extends StreamBitSize
|
sealed abstract class SpecificVehicleData(val format: VehicleFormat) extends StreamBitSize
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The format of vehicle data for the type of vehicles that are considered "utility."
|
* The format of vehicle data for the type of vehicles that are considered "utility."
|
||||||
|
|
@ -84,7 +75,7 @@ final case class VehicleData(
|
||||||
cloak: Boolean,
|
cloak: Boolean,
|
||||||
vehicle_format_data: Option[SpecificVehicleData],
|
vehicle_format_data: Option[SpecificVehicleData],
|
||||||
inventory: Option[InventoryData] = None
|
inventory: Option[InventoryData] = None
|
||||||
)(val vehicle_type: VehicleFormat.Value = VehicleFormat.Normal)
|
)(val vehicle_type: VehicleFormat = VehicleFormat.Normal)
|
||||||
extends ConstructorData {
|
extends ConstructorData {
|
||||||
override def bitsize: Long = {
|
override def bitsize: Long = {
|
||||||
//factor guard bool values into the base size, not its corresponding optional field
|
//factor guard bool values into the base size, not its corresponding optional field
|
||||||
|
|
@ -174,7 +165,7 @@ object VehicleData extends Marshallable[VehicleData] {
|
||||||
*/
|
*/
|
||||||
private val utility_data_codec: Codec[SpecificVehicleData] = {
|
private val utility_data_codec: Codec[SpecificVehicleData] = {
|
||||||
import shapeless.::
|
import shapeless.::
|
||||||
uintL(6).hlist.exmap[SpecificVehicleData](
|
uintL(VehicleFormat.Utility.value).hlist.exmap[SpecificVehicleData](
|
||||||
{
|
{
|
||||||
case n :: HNil =>
|
case n :: HNil =>
|
||||||
Successful(UtilityVehicleData(n).asInstanceOf[SpecificVehicleData])
|
Successful(UtilityVehicleData(n).asInstanceOf[SpecificVehicleData])
|
||||||
|
|
@ -193,7 +184,7 @@ object VehicleData extends Marshallable[VehicleData] {
|
||||||
*/
|
*/
|
||||||
private val variant_data_codec: Codec[SpecificVehicleData] = {
|
private val variant_data_codec: Codec[SpecificVehicleData] = {
|
||||||
import shapeless.::
|
import shapeless.::
|
||||||
uint8L.hlist.exmap[SpecificVehicleData](
|
uintL(VehicleFormat.Variant.value).hlist.exmap[SpecificVehicleData](
|
||||||
{
|
{
|
||||||
case n :: HNil =>
|
case n :: HNil =>
|
||||||
Successful(VariantVehicleData(n).asInstanceOf[SpecificVehicleData])
|
Successful(VariantVehicleData(n).asInstanceOf[SpecificVehicleData])
|
||||||
|
|
@ -212,7 +203,7 @@ object VehicleData extends Marshallable[VehicleData] {
|
||||||
* @param vehicleFormat the requested format
|
* @param vehicleFormat the requested format
|
||||||
* @return the appropriate `Codec` for parsing that format
|
* @return the appropriate `Codec` for parsing that format
|
||||||
*/
|
*/
|
||||||
private def selectFormatReader(vehicleFormat: VehicleFormat.Value): Codec[SpecificVehicleData] =
|
private def selectFormatReader(vehicleFormat: VehicleFormat): Codec[SpecificVehicleData] =
|
||||||
vehicleFormat match {
|
vehicleFormat match {
|
||||||
case VehicleFormat.Utility =>
|
case VehicleFormat.Utility =>
|
||||||
utility_data_codec
|
utility_data_codec
|
||||||
|
|
@ -223,7 +214,7 @@ object VehicleData extends Marshallable[VehicleData] {
|
||||||
.asInstanceOf[Codec[SpecificVehicleData]]
|
.asInstanceOf[Codec[SpecificVehicleData]]
|
||||||
}
|
}
|
||||||
|
|
||||||
def codec(vehicle_type: VehicleFormat.Value): Codec[VehicleData] = {
|
def codec(vehicle_type: VehicleFormat): Codec[VehicleData] = {
|
||||||
import shapeless.::
|
import shapeless.::
|
||||||
(
|
(
|
||||||
("pos" | PlacementData.codec) >>:~ { pos =>
|
("pos" | PlacementData.codec) >>:~ { pos =>
|
||||||
|
|
@ -237,7 +228,7 @@ object VehicleData extends Marshallable[VehicleData] {
|
||||||
("unk6" | bool) ::
|
("unk6" | bool) ::
|
||||||
("cloak" | bool) :: //cloak as wraith, phantasm
|
("cloak" | bool) :: //cloak as wraith, phantasm
|
||||||
conditional(vehicle_type != VehicleFormat.Normal,"vehicle_format_data" | selectFormatReader(vehicle_type)) ::
|
conditional(vehicle_type != VehicleFormat.Normal,"vehicle_format_data" | selectFormatReader(vehicle_type)) ::
|
||||||
optional(bool, target = "inventory" | MountableInventory.custom_inventory_codec(pos.vel.isDefined, VehicleFormat.Normal))
|
optional(bool, target = "inventory" | MountableInventory.custom_inventory_codec(pos.vel.isDefined, vehicle_type))
|
||||||
}
|
}
|
||||||
).exmap[VehicleData](
|
).exmap[VehicleData](
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -22,5 +22,5 @@ object CharacterVoice extends Enumeration {
|
||||||
Voice5 //daredevil, calculating
|
Voice5 //daredevil, calculating
|
||||||
= Value
|
= Value
|
||||||
|
|
||||||
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint(3))
|
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint(bits = 3))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,5 +12,5 @@ object ExoSuitType extends Enumeration {
|
||||||
type Type = Value
|
type Type = Value
|
||||||
val Agile, Reinforced, MAX, Infiltration, Standard = Value
|
val Agile, Reinforced, MAX, Infiltration, Standard = Value
|
||||||
|
|
||||||
implicit val codec: Codec[ExoSuitType.Value] = PacketHelpers.createEnumerationCodec(this, uint(3))
|
implicit val codec: Codec[ExoSuitType.Value] = PacketHelpers.createEnumerationCodec(this, uint(bits = 3))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,14 @@ import scodec.codecs._
|
||||||
/**
|
/**
|
||||||
* An `Enumeration` of the kinds of states applicable to the grenade animation.
|
* An `Enumeration` of the kinds of states applicable to the grenade animation.
|
||||||
*/
|
*/
|
||||||
object GrenadeState extends Enumeration(1) {
|
object GrenadeState extends Enumeration {
|
||||||
type Type = Value
|
type Type = Value
|
||||||
|
|
||||||
val Primed, //avatars and other depicted player characters
|
val
|
||||||
Thrown, //avatars only
|
Non, //non-actionable state of rest
|
||||||
None //non-actionable state of rest
|
Primed, //avatars and other depicted player characters
|
||||||
|
Thrown, //avatars only
|
||||||
|
None //non-actionable state of rest
|
||||||
= Value
|
= Value
|
||||||
|
|
||||||
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint8L)
|
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint8L)
|
||||||
|
|
|
||||||
19
src/main/scala/net/psforever/types/VehicleFormat.scala
Normal file
19
src/main/scala/net/psforever/types/VehicleFormat.scala
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright (c) 2023 PSForever
|
||||||
|
package net.psforever.types
|
||||||
|
|
||||||
|
import enumeratum.values.{IntEnum, IntEnumEntry}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An enumeration of the various formats that known structures that the stream of bits for `VehicleData` can assume.
|
||||||
|
*/
|
||||||
|
sealed abstract class VehicleFormat(val value: Int) extends IntEnumEntry
|
||||||
|
|
||||||
|
object VehicleFormat extends IntEnum[VehicleFormat] {
|
||||||
|
val values: IndexedSeq[VehicleFormat] = findValues
|
||||||
|
|
||||||
|
case object Normal extends VehicleFormat(value = 0)
|
||||||
|
case object Battleframe extends VehicleFormat(value = 1)
|
||||||
|
case object BattleframeFlight extends VehicleFormat(value = 2)
|
||||||
|
case object Utility extends VehicleFormat(value = 6)
|
||||||
|
case object Variant extends VehicleFormat(value = 8)
|
||||||
|
}
|
||||||
|
|
@ -12,21 +12,22 @@ class ConnectToWorldRequestMessageTest extends Specification {
|
||||||
|
|
||||||
"decode" in {
|
"decode" in {
|
||||||
PacketCoding.decodePacket(string).require match {
|
PacketCoding.decodePacket(string).require match {
|
||||||
case ConnectToWorldRequestMessage(serverName, token, majorVersion, minorVersion, revision, buildDate, unk) =>
|
case ConnectToWorldRequestMessage(serverName, token, majorVersion, minorVersion, revision, buildDate, unk1, unk2) =>
|
||||||
serverName mustEqual "gemini"
|
serverName mustEqual "gemini"
|
||||||
token mustEqual ""
|
token mustEqual ""
|
||||||
majorVersion mustEqual 0
|
majorVersion mustEqual 0
|
||||||
minorVersion mustEqual 0
|
minorVersion mustEqual 0
|
||||||
revision mustEqual 0
|
revision mustEqual 0
|
||||||
buildDate mustEqual ""
|
buildDate mustEqual ""
|
||||||
unk mustEqual 0
|
unk1 mustEqual 0
|
||||||
|
unk2 mustEqual 0
|
||||||
case _ =>
|
case _ =>
|
||||||
ko
|
ko
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"encode" in {
|
"encode" in {
|
||||||
val msg = ConnectToWorldRequestMessage("gemini", "", 0, 0, 0, "", 0)
|
val msg = ConnectToWorldRequestMessage("gemini", "", 0, 0, 0, "", 0, 0)
|
||||||
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
|
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
pkt mustEqual string
|
pkt mustEqual string
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
package game.objectcreatevehicle
|
package game.objectcreatevehicle
|
||||||
|
|
||||||
import net.psforever.packet._
|
import net.psforever.packet._
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate.{CharacterAppearanceA, _}
|
||||||
import net.psforever.packet.game.ObjectCreateMessage
|
import net.psforever.packet.game.ObjectCreateMessage
|
||||||
import net.psforever.types._
|
import net.psforever.types._
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
|
|
@ -12,8 +12,8 @@ class UtilityVehiclesTest extends Specification {
|
||||||
val string_ant = hex"17 C2000000 9E0 7C01 6C2D7 65535 CA16 00 00 00 4400003FC000000"
|
val string_ant = hex"17 C2000000 9E0 7C01 6C2D7 65535 CA16 00 00 00 4400003FC000000"
|
||||||
val string_ams =
|
val string_ams =
|
||||||
hex"17 B8010000 970 3D10 002D765535CA16000000 402285BB0037E4100749E1D03000000620D83A0A00000195798741C00000332E40D84800000"
|
hex"17 B8010000 970 3D10 002D765535CA16000000 402285BB0037E4100749E1D03000000620D83A0A00000195798741C00000332E40D84800000"
|
||||||
// val string_ams_seated =
|
val string_ams_seated =
|
||||||
// hex"17ec060000970fe0f030898abda28127f007ff9c1f2f80c0001e18ff00001051e40786400000008c50004c0041006d0069006e006700790075006500540052007c00000304217c859e8080000000000000002503420022c02a002a002a002a0050004c0041002a002a002a002a00010027e300940000016c0400023c040002285a086c2f00c80000000000300210288740800000004046f17423018000002c4d6190400000001010704a86406000002bc770842000000004041c5f21d01800000e075821902000000623e84208000001950588c1800000332ea0f840000000"
|
hex"17 ec060000 970 fe0f 6C2D765535CA16000013 f9c1f2f80c000 1e18ff0000 105 1e4078640000000 8c50004c0041006d0069006e00670079007500650054005200 04217c859e808000000000000000250342002 2c02a002a002a002a0050004c0041002a002a002a002a00 010027e3007c000003940000016c0400023c040002285a086c2f00c80000000000300210288740800000004046f17423018000002c4d6190400000001010704a86406000002bc770842000000004041c5f21d01800000e0 75821902000000 623e8420800000 1950588c1800000 332ea0f840000000"
|
||||||
|
|
||||||
"Utility vehicles" should {
|
"Utility vehicles" should {
|
||||||
"decode (ant)" in {
|
"decode (ant)" in {
|
||||||
|
|
@ -95,6 +95,152 @@ class UtilityVehiclesTest extends Specification {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"decode (ams, seated)" in {
|
||||||
|
PacketCoding.decodePacket(string_ams_seated).require match {
|
||||||
|
case ObjectCreateMessage(len, cls, guid, None, data) =>
|
||||||
|
len mustEqual 1772
|
||||||
|
cls mustEqual ObjectClass.ams
|
||||||
|
guid mustEqual PlanetSideGUID(4094)
|
||||||
|
data match {
|
||||||
|
case ams: VehicleData =>
|
||||||
|
ams.pos.coord mustEqual Vector3(3674.8438f, 2726.789f, 91.15625f)
|
||||||
|
ams.pos.orient mustEqual Vector3(0f, 0f, 36.5625f)
|
||||||
|
ams.pos.vel.contains(Vector3(27.3375f, -0.78749996f, 0.1125f)) mustEqual true //contains does not work
|
||||||
|
ams.data match {
|
||||||
|
case data: CommonFieldData =>
|
||||||
|
data.faction mustEqual PlanetSideEmpire.TR
|
||||||
|
data.bops mustEqual false
|
||||||
|
data.alternate mustEqual false
|
||||||
|
data.v1 mustEqual false
|
||||||
|
data.v2.isEmpty mustEqual true
|
||||||
|
data.jammered mustEqual false
|
||||||
|
data.v4.contains(false) mustEqual true
|
||||||
|
data.v5.isEmpty mustEqual true
|
||||||
|
data.guid mustEqual ValidPlanetSideGUID(3087)
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
ams.unk3 mustEqual false
|
||||||
|
ams.health mustEqual 255
|
||||||
|
ams.unk4 mustEqual false
|
||||||
|
ams.no_mount_points mustEqual false
|
||||||
|
ams.driveState mustEqual DriveState.Mobile
|
||||||
|
ams.unk5 mustEqual false
|
||||||
|
ams.unk6 mustEqual false
|
||||||
|
ams.cloak mustEqual false
|
||||||
|
ams.vehicle_format_data.contains(UtilityVehicleData(0)) mustEqual true
|
||||||
|
val inv = ams.inventory match {
|
||||||
|
case Some(inv: InventoryData) => inv.contents
|
||||||
|
case _ => Nil
|
||||||
|
}
|
||||||
|
//0
|
||||||
|
val inv0 = inv.head
|
||||||
|
inv0.objectClass mustEqual ObjectClass.avatar
|
||||||
|
inv0.guid mustEqual PlanetSideGUID(3087)
|
||||||
|
inv0.parentSlot mustEqual 0
|
||||||
|
inv0.obj match {
|
||||||
|
case PlayerData(None, CharacterAppearanceData(a, b, r), char, Some(InventoryData(pinv)), DrawnSlot.None) =>
|
||||||
|
a.app.name mustEqual "PLAmingyueTR"
|
||||||
|
a.app.faction mustEqual PlanetSideEmpire.TR
|
||||||
|
a.app.sex mustEqual CharacterSex.Female
|
||||||
|
a.app.voice mustEqual CharacterVoice.Voice5
|
||||||
|
a.app.head mustEqual 16
|
||||||
|
a.data match {
|
||||||
|
case data: CommonFieldData =>
|
||||||
|
data.faction mustEqual PlanetSideEmpire.TR
|
||||||
|
data.bops mustEqual false
|
||||||
|
data.alternate mustEqual false
|
||||||
|
data.v1 mustEqual false
|
||||||
|
data.v2.isEmpty mustEqual true
|
||||||
|
data.jammered mustEqual false
|
||||||
|
data.v4.isEmpty mustEqual true
|
||||||
|
data.v5.isEmpty mustEqual true
|
||||||
|
data.guid mustEqual ValidPlanetSideGUID(0)
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
a.char_id mustEqual 41555698L
|
||||||
|
a.exosuit mustEqual ExoSuitType.Agile
|
||||||
|
a.unk5 mustEqual 0
|
||||||
|
a.unk7 mustEqual 0
|
||||||
|
a.unk8 mustEqual 0
|
||||||
|
a.unk9 mustEqual 0
|
||||||
|
a.unkA mustEqual 0
|
||||||
|
b.outfit_id mustEqual 527764
|
||||||
|
b.outfit_name mustEqual "****PLA****"
|
||||||
|
b.outfit_logo mustEqual 1
|
||||||
|
b.lfs mustEqual false
|
||||||
|
b.backpack mustEqual false
|
||||||
|
b.charging_pose mustEqual false
|
||||||
|
b.grenade_state mustEqual GrenadeState.None
|
||||||
|
b.on_zipline.isEmpty mustEqual true
|
||||||
|
b.facingPitch mustEqual -5.625f
|
||||||
|
b.facingYawUpper mustEqual 5.625f
|
||||||
|
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
|
||||||
|
r mustEqual RibbonBars(
|
||||||
|
MeritCommendation.AMSSupport2,
|
||||||
|
MeritCommendation.HeavyAssault1,
|
||||||
|
MeritCommendation.ScavengerTR1,
|
||||||
|
MeritCommendation.ThreeYearTR
|
||||||
|
)
|
||||||
|
char.health mustEqual 100
|
||||||
|
char.armor mustEqual 0
|
||||||
|
char.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
|
||||||
|
char.command_rank mustEqual 4
|
||||||
|
char.implant_effects mustEqual Nil
|
||||||
|
char.unk mustEqual 2
|
||||||
|
char.cosmetics.contains(Set(Cosmetic.Earpiece, Cosmetic.Sunglasses, Cosmetic.NoHelmet)) mustEqual true
|
||||||
|
pinv mustEqual List(
|
||||||
|
InternalSlot(728, ValidPlanetSideGUID(3312), 0, REKData(CommonFieldData(PlanetSideEmpire.TR,false,false,false,None,false,Some(false),None,ValidPlanetSideGUID(0)),3,0)),
|
||||||
|
InternalSlot(132, ValidPlanetSideGUID(3665), 1, WeaponData(CommonFieldData(PlanetSideEmpire.TR,false,false,false,None,false,None,None,ValidPlanetSideGUID(0)),0,List(
|
||||||
|
InternalSlot(111,ValidPlanetSideGUID(4538),0,CommonFieldData(PlanetSideEmpire.NEUTRAL,false,false,false,None,false,Some(false),None,ValidPlanetSideGUID(0)))),false)
|
||||||
|
),
|
||||||
|
InternalSlot(556, ValidPlanetSideGUID(3179), 2, WeaponData(CommonFieldData(PlanetSideEmpire.TR,false,false,false,None,false,None,None,ValidPlanetSideGUID(0)),0,List(
|
||||||
|
InternalSlot(28,ValidPlanetSideGUID(3221),0,CommonFieldData(PlanetSideEmpire.NEUTRAL,false,false,false,None,false,Some(false),None,ValidPlanetSideGUID(0)))),false)
|
||||||
|
),
|
||||||
|
InternalSlot(175,ValidPlanetSideGUID(4334),4,WeaponData(CommonFieldData(PlanetSideEmpire.TR,false,false,false,None,false,None,None,ValidPlanetSideGUID(0)),0,List(InternalSlot(540,ValidPlanetSideGUID(3833),0,CommonFieldData(PlanetSideEmpire.NEUTRAL,false,false,false,None,false,Some(false),None,ValidPlanetSideGUID(0)))),false))
|
||||||
|
)
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
//1
|
||||||
|
val inv1 = inv(1)
|
||||||
|
inv1.objectClass mustEqual ObjectClass.matrix_terminalc
|
||||||
|
inv1.guid mustEqual PlanetSideGUID(3265)
|
||||||
|
inv1.parentSlot mustEqual 1
|
||||||
|
inv1.obj.isInstanceOf[CommonFieldData] mustEqual true
|
||||||
|
//2
|
||||||
|
val inv2 = inv(2)
|
||||||
|
inv2.objectClass mustEqual ObjectClass.ams_respawn_tube
|
||||||
|
inv2.guid mustEqual PlanetSideGUID(4346)
|
||||||
|
inv2.parentSlot mustEqual 2
|
||||||
|
inv2.obj.isInstanceOf[CommonFieldData] mustEqual true
|
||||||
|
//3
|
||||||
|
val inv3 = inv(3)
|
||||||
|
inv3.objectClass mustEqual ObjectClass.order_terminala
|
||||||
|
inv3.guid mustEqual PlanetSideGUID(4363)
|
||||||
|
inv3.parentSlot mustEqual 3
|
||||||
|
inv3.obj.isInstanceOf[CommonFieldData] mustEqual true
|
||||||
|
//4
|
||||||
|
val inv4 = inv(4)
|
||||||
|
inv4.objectClass mustEqual ObjectClass.order_terminalb
|
||||||
|
inv4.guid mustEqual PlanetSideGUID(4074)
|
||||||
|
inv4.parentSlot mustEqual 4
|
||||||
|
inv4.obj.isInstanceOf[CommonFieldData] mustEqual true
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"encode (ant)" in {
|
"encode (ant)" in {
|
||||||
val obj = VehicleData(
|
val obj = VehicleData(
|
||||||
PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
|
PlacementData(3674.8438f, 2726.789f, 91.15625f, 0f, 0f, 90.0f),
|
||||||
|
|
@ -165,5 +311,124 @@ class UtilityVehiclesTest extends Specification {
|
||||||
|
|
||||||
pkt mustEqual string_ams
|
pkt mustEqual string_ams
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"encode (ams, seated)" in {
|
||||||
|
val obj = VehicleData(
|
||||||
|
PlacementData(
|
||||||
|
Vector3(3674.8438f, 2726.789f, 91.15625f),
|
||||||
|
Vector3(0f, 0f, 36.5625f),
|
||||||
|
Some(Vector3(27.3375f, -0.78749996f, 0.1125f))
|
||||||
|
),
|
||||||
|
CommonFieldData(PlanetSideEmpire.TR, false, false, false, None, false, Some(false), None, PlanetSideGUID(3087)),
|
||||||
|
unk3 = false,
|
||||||
|
health = 255,
|
||||||
|
unk4 = false,
|
||||||
|
no_mount_points = false,
|
||||||
|
DriveState.Mobile,
|
||||||
|
unk5 = false,
|
||||||
|
unk6 = false,
|
||||||
|
cloak = false,
|
||||||
|
Some(UtilityVehicleData(0)),
|
||||||
|
Some(InventoryData(
|
||||||
|
List(
|
||||||
|
InternalSlot(
|
||||||
|
ObjectClass.avatar, PlanetSideGUID(3087), 0, {
|
||||||
|
val a: Int => CharacterAppearanceA = CharacterAppearanceA(
|
||||||
|
BasicCharacterData(
|
||||||
|
"PLAmingyueTR",
|
||||||
|
PlanetSideEmpire.TR,
|
||||||
|
CharacterSex.Female,
|
||||||
|
head = 16,
|
||||||
|
CharacterVoice.Voice5
|
||||||
|
),
|
||||||
|
CommonFieldData(PlanetSideEmpire.TR, false, false, false, None, false, None, None, PlanetSideGUID(0)),
|
||||||
|
ExoSuitType.Agile,
|
||||||
|
unk5 = 0,
|
||||||
|
char_id = 41555698L,
|
||||||
|
unk7 = 0,
|
||||||
|
unk8 = 0,
|
||||||
|
unk9 = 0,
|
||||||
|
unkA = 0
|
||||||
|
)
|
||||||
|
val b: (Boolean, Int) => CharacterAppearanceB = CharacterAppearanceB(
|
||||||
|
outfit_id = 527764L,
|
||||||
|
outfit_name = "****PLA****",
|
||||||
|
outfit_logo = 1,
|
||||||
|
unk1 = false,
|
||||||
|
backpack = false,
|
||||||
|
unk2 = false,
|
||||||
|
unk3 = false,
|
||||||
|
unk4 = false,
|
||||||
|
facingPitch = -5.625f,
|
||||||
|
facingYawUpper = 5.625f,
|
||||||
|
lfs = false,
|
||||||
|
GrenadeState.None,
|
||||||
|
is_cloaking = false,
|
||||||
|
unk5 = false,
|
||||||
|
unk6 = false,
|
||||||
|
charging_pose = false,
|
||||||
|
unk7 = false,
|
||||||
|
on_zipline = None
|
||||||
|
)
|
||||||
|
val app: Int => CharacterAppearanceData = CharacterAppearanceData(
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
RibbonBars(
|
||||||
|
MeritCommendation.AMSSupport2,
|
||||||
|
MeritCommendation.HeavyAssault1,
|
||||||
|
MeritCommendation.ScavengerTR1,
|
||||||
|
MeritCommendation.ThreeYearTR
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val char: (Boolean, Boolean) => CharacterData = CharacterData(
|
||||||
|
health = 100,
|
||||||
|
armor = 0,
|
||||||
|
UniformStyle.ThirdUpgrade,
|
||||||
|
unk = 2,
|
||||||
|
command_rank = 4,
|
||||||
|
implant_effects = List(),
|
||||||
|
Some(Set(Cosmetic.Earpiece, Cosmetic.Sunglasses, Cosmetic.NoHelmet))
|
||||||
|
)
|
||||||
|
val inv = InventoryData(
|
||||||
|
List(
|
||||||
|
InternalSlot(728, ValidPlanetSideGUID(3312), 0, REKData(CommonFieldData(PlanetSideEmpire.TR,false,false,false,None,false,Some(false),None,ValidPlanetSideGUID(0)),3,0)),
|
||||||
|
InternalSlot(132, ValidPlanetSideGUID(3665), 1, WeaponData(CommonFieldData(PlanetSideEmpire.TR,false,false,false,None,false,None,None,ValidPlanetSideGUID(0)),0,List(
|
||||||
|
InternalSlot(111, ValidPlanetSideGUID(4538), 0, CommonFieldData(PlanetSideEmpire.NEUTRAL,false,false,false,None,false,Some(false),None,ValidPlanetSideGUID(0)))),false)
|
||||||
|
),
|
||||||
|
InternalSlot(556, ValidPlanetSideGUID(3179), 2, WeaponData(CommonFieldData(PlanetSideEmpire.TR,false,false,false,None,false,None,None,ValidPlanetSideGUID(0)),0,List(
|
||||||
|
InternalSlot(28, ValidPlanetSideGUID(3221), 0, CommonFieldData(PlanetSideEmpire.NEUTRAL,false,false,false,None,false,Some(false),None,ValidPlanetSideGUID(0)))),false)
|
||||||
|
),
|
||||||
|
InternalSlot(175, ValidPlanetSideGUID(4334), 4, WeaponData(CommonFieldData(PlanetSideEmpire.TR,false,false,false,None,false,None,None,ValidPlanetSideGUID(0)),0,List(InternalSlot(540,ValidPlanetSideGUID(3833),0,CommonFieldData(PlanetSideEmpire.NEUTRAL,false,false,false,None,false,Some(false),None,ValidPlanetSideGUID(0)))),false))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
MountableInventory.PlayerData(
|
||||||
|
app,
|
||||||
|
char,
|
||||||
|
inv,
|
||||||
|
DrawnSlot.None,
|
||||||
|
MountableInventory.InitialStreamLengthToSeatEntries(hasVelocity=true, VehicleFormat.Utility)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
InternalSlot(
|
||||||
|
ObjectClass.matrix_terminalc, PlanetSideGUID(3265), 1, CommonFieldData(PlanetSideEmpire.TR)(flag = false)
|
||||||
|
),
|
||||||
|
InternalSlot(
|
||||||
|
ObjectClass.ams_respawn_tube, PlanetSideGUID(4346), 2, CommonFieldData(PlanetSideEmpire.TR)(flag = false)
|
||||||
|
),
|
||||||
|
InternalSlot(
|
||||||
|
ObjectClass.order_terminala, PlanetSideGUID(4363), 3, CommonFieldData(PlanetSideEmpire.TR)(flag = false)
|
||||||
|
),
|
||||||
|
InternalSlot(
|
||||||
|
ObjectClass.order_terminalb, PlanetSideGUID(4074), 4, CommonFieldData(PlanetSideEmpire.TR)(flag = false)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
)(VehicleFormat.Utility)
|
||||||
|
val msg = ObjectCreateMessage(ObjectClass.ams, PlanetSideGUID(4094), obj)
|
||||||
|
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string_ams_seated
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,7 @@ class Client(username: String, password: String) {
|
||||||
case _ => ???
|
case _ => ???
|
||||||
}
|
}
|
||||||
setupConnection()
|
setupConnection()
|
||||||
send(ConnectToWorldRequestMessage("", state.token.get, 0, 0, 0, "", 0)).require
|
send(ConnectToWorldRequestMessage("", state.token.get, 0, 0, 0, "", 0, 0)).require
|
||||||
while (true) {
|
while (true) {
|
||||||
val r = waitFor[CharacterInfoMessage]().require
|
val r = waitFor[CharacterInfoMessage]().require
|
||||||
if (r.finished) {
|
if (r.finished) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue