mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-14 07:55:07 +00:00
Merge branch 'master' into minor-updates
This commit is contained in:
commit
f87edd7057
23 changed files with 1474 additions and 42 deletions
|
|
@ -363,7 +363,7 @@ object GamePacketOpcode extends Enumeration {
|
||||||
case 0x23 => noDecoder(ActionCancelAcknowledgeMessage)
|
case 0x23 => noDecoder(ActionCancelAcknowledgeMessage)
|
||||||
case 0x24 => game.SetEmpireMessage.decode
|
case 0x24 => game.SetEmpireMessage.decode
|
||||||
case 0x25 => game.EmoteMsg.decode
|
case 0x25 => game.EmoteMsg.decode
|
||||||
case 0x26 => noDecoder(UnuseItemMessage)
|
case 0x26 => game.UnuseItemMessage.decode
|
||||||
case 0x27 => game.ObjectDetachMessage.decode
|
case 0x27 => game.ObjectDetachMessage.decode
|
||||||
// 0x28
|
// 0x28
|
||||||
case 0x28 => game.CreateShortcutMessage.decode
|
case 0x28 => game.CreateShortcutMessage.decode
|
||||||
|
|
@ -392,7 +392,7 @@ object GamePacketOpcode extends Enumeration {
|
||||||
case 0x3c => game.GenericCollisionMsg.decode
|
case 0x3c => game.GenericCollisionMsg.decode
|
||||||
case 0x3d => game.QuantityUpdateMessage.decode
|
case 0x3d => game.QuantityUpdateMessage.decode
|
||||||
case 0x3e => game.ArmorChangedMessage.decode
|
case 0x3e => game.ArmorChangedMessage.decode
|
||||||
case 0x3f => noDecoder(ProjectileStateMessage)
|
case 0x3f => game.ProjectileStateMessage.decode
|
||||||
|
|
||||||
// OPCODES 0x40-4f
|
// OPCODES 0x40-4f
|
||||||
case 0x40 => noDecoder(MountVehicleCargoMsg)
|
case 0x40 => noDecoder(MountVehicleCargoMsg)
|
||||||
|
|
@ -415,7 +415,7 @@ object GamePacketOpcode extends Enumeration {
|
||||||
|
|
||||||
// OPCODES 0x50-5f
|
// OPCODES 0x50-5f
|
||||||
case 0x50 => game.TargetingInfoMessage.decode
|
case 0x50 => game.TargetingInfoMessage.decode
|
||||||
case 0x51 => noDecoder(TriggerEffectMessage)
|
case 0x51 => game.TriggerEffectMessage.decode
|
||||||
case 0x52 => game.WeaponDryFireMessage.decode
|
case 0x52 => game.WeaponDryFireMessage.decode
|
||||||
case 0x53 => noDecoder(DroppodLaunchRequestMessage)
|
case 0x53 => noDecoder(DroppodLaunchRequestMessage)
|
||||||
case 0x54 => noDecoder(HackMessage)
|
case 0x54 => noDecoder(HackMessage)
|
||||||
|
|
@ -425,7 +425,7 @@ object GamePacketOpcode extends Enumeration {
|
||||||
// 0x58
|
// 0x58
|
||||||
case 0x58 => game.AvatarImplantMessage.decode
|
case 0x58 => game.AvatarImplantMessage.decode
|
||||||
case 0x59 => noDecoder(UnknownMessage89)
|
case 0x59 => noDecoder(UnknownMessage89)
|
||||||
case 0x5a => noDecoder(DelayedPathMountMsg)
|
case 0x5a => game.DelayedPathMountMsg.decode
|
||||||
case 0x5b => noDecoder(OrbitalShuttleTimeMsg)
|
case 0x5b => noDecoder(OrbitalShuttleTimeMsg)
|
||||||
case 0x5c => noDecoder(AIDamage)
|
case 0x5c => noDecoder(AIDamage)
|
||||||
case 0x5d => game.DeployObjectMessage.decode
|
case 0x5d => game.DeployObjectMessage.decode
|
||||||
|
|
@ -461,7 +461,7 @@ object GamePacketOpcode extends Enumeration {
|
||||||
case 0x76 => game.DeployableObjectsInfoMessage.decode
|
case 0x76 => game.DeployableObjectsInfoMessage.decode
|
||||||
case 0x77 => noDecoder(SquadState)
|
case 0x77 => noDecoder(SquadState)
|
||||||
// 0x78
|
// 0x78
|
||||||
case 0x78 => noDecoder(OxygenStateMessage)
|
case 0x78 => game.OxygenStateMessage.decode
|
||||||
case 0x79 => noDecoder(TradeMessage)
|
case 0x79 => noDecoder(TradeMessage)
|
||||||
case 0x7a => noDecoder(UnknownMessage122)
|
case 0x7a => noDecoder(UnknownMessage122)
|
||||||
case 0x7b => noDecoder(DamageFeedbackMessage)
|
case 0x7b => noDecoder(DamageFeedbackMessage)
|
||||||
|
|
@ -510,7 +510,7 @@ object GamePacketOpcode extends Enumeration {
|
||||||
|
|
||||||
// OPCODES 0xa0-af
|
// OPCODES 0xa0-af
|
||||||
case 0xa0 => game.BuildingInfoUpdateMessage.decode
|
case 0xa0 => game.BuildingInfoUpdateMessage.decode
|
||||||
case 0xa1 => noDecoder(FireHintMessage)
|
case 0xa1 => game.FireHintMessage.decode
|
||||||
case 0xa2 => noDecoder(UplinkRequest)
|
case 0xa2 => noDecoder(UplinkRequest)
|
||||||
case 0xa3 => noDecoder(UplinkResponse)
|
case 0xa3 => noDecoder(UplinkResponse)
|
||||||
case 0xa4 => game.WarpgateRequest.decode
|
case 0xa4 => game.WarpgateRequest.decode
|
||||||
|
|
@ -533,7 +533,7 @@ object GamePacketOpcode extends Enumeration {
|
||||||
case 0xb2 => game.VoiceHostInfo.decode
|
case 0xb2 => game.VoiceHostInfo.decode
|
||||||
case 0xb3 => game.BattleplanMessage.decode
|
case 0xb3 => game.BattleplanMessage.decode
|
||||||
case 0xb4 => game.BattleExperienceMessage.decode
|
case 0xb4 => game.BattleExperienceMessage.decode
|
||||||
case 0xb5 => noDecoder(TargetingImplantRequest)
|
case 0xb5 => game.TargetingImplantRequest.decode
|
||||||
case 0xb6 => game.ZonePopulationUpdateMessage.decode
|
case 0xb6 => game.ZonePopulationUpdateMessage.decode
|
||||||
case 0xb7 => game.DisconnectMessage.decode
|
case 0xb7 => game.DisconnectMessage.decode
|
||||||
// 0xb8
|
// 0xb8
|
||||||
|
|
@ -567,7 +567,7 @@ object GamePacketOpcode extends Enumeration {
|
||||||
|
|
||||||
// OPCODES 0xd0-df
|
// OPCODES 0xd0-df
|
||||||
case 0xd0 => noDecoder(UnknownMessage208)
|
case 0xd0 => noDecoder(UnknownMessage208)
|
||||||
case 0xd1 => noDecoder(DisplayedAwardMessage)
|
case 0xd1 => game.DisplayedAwardMessage.decode
|
||||||
case 0xd2 => noDecoder(RespawnAMSInfoMessage)
|
case 0xd2 => noDecoder(RespawnAMSInfoMessage)
|
||||||
case 0xd3 => noDecoder(ComponentDamageMessage)
|
case 0xd3 => noDecoder(ComponentDamageMessage)
|
||||||
case 0xd4 => noDecoder(GenericObjectActionAtPositionMessage)
|
case 0xd4 => noDecoder(GenericObjectActionAtPositionMessage)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright (c) 2016 PSForever.net to present
|
||||||
|
package net.psforever.packet.game
|
||||||
|
|
||||||
|
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||||
|
import scodec.Codec
|
||||||
|
import scodec.codecs._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* na
|
||||||
|
* @param player_guid na
|
||||||
|
* @param vehicle_guid vehicle ?, turret ? Found a HART GUID for now. Need more search.
|
||||||
|
* @param u1 na - maybe a delay ?
|
||||||
|
* @param u2 na
|
||||||
|
*/
|
||||||
|
final case class DelayedPathMountMsg(player_guid : PlanetSideGUID,
|
||||||
|
vehicle_guid : PlanetSideGUID,
|
||||||
|
u1 : Int,
|
||||||
|
u2 : Boolean)
|
||||||
|
extends PlanetSideGamePacket {
|
||||||
|
type Packet = DelayedPathMountMsg
|
||||||
|
def opcode = GamePacketOpcode.DelayedPathMountMsg
|
||||||
|
def encode = DelayedPathMountMsg.encode(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
object DelayedPathMountMsg extends Marshallable[DelayedPathMountMsg] {
|
||||||
|
implicit val codec : Codec[DelayedPathMountMsg] = (
|
||||||
|
("player_guid" | PlanetSideGUID.codec) ::
|
||||||
|
("vehicle_guid" | PlanetSideGUID.codec) ::
|
||||||
|
("u1" | uint8L) ::
|
||||||
|
("u2" | bool)
|
||||||
|
).as[DelayedPathMountMsg]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.packet.game
|
||||||
|
|
||||||
|
import net.psforever.types.MeritCommendation
|
||||||
|
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||||
|
import scodec.Codec
|
||||||
|
import scodec.codecs._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An `Enumeration` of the slots for award ribbons on a player's `RibbonBars`.
|
||||||
|
*/
|
||||||
|
object RibbonBarsSlot extends Enumeration {
|
||||||
|
type Type = Value
|
||||||
|
|
||||||
|
val Top,
|
||||||
|
Middle,
|
||||||
|
Bottom,
|
||||||
|
TermOfService //technically,the slot above "Top"
|
||||||
|
= Value
|
||||||
|
|
||||||
|
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint4L)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatched to configure a player's merit commendation ribbons.<br>
|
||||||
|
* <br>
|
||||||
|
* Normally, this packet is dispatched by the client when managing merit commendations through the "Character Info/Achievements" tab.
|
||||||
|
* On Gemini Live, this packet was also always dispatched once by the server during character login.
|
||||||
|
* It set the term of service ribbon explicitly.
|
||||||
|
* Generally, this was unnecessary, as the encoded character data maintains information about displayed ribbons.
|
||||||
|
* This behavior was probably a routine that ensured that correct yearly progression was tracked if the player earned it while offline.
|
||||||
|
* It never set any of the other ribbon slot positions during login.<br>
|
||||||
|
* <br>
|
||||||
|
* A specific ribbon may only be set once to one slot.
|
||||||
|
* The last set slot is considered the valid position to which that ribbon will be placed/moved.
|
||||||
|
* @param player_guid the player
|
||||||
|
* @param ribbon the award to be displayed;
|
||||||
|
* defaults to `MeritCommendation.None`;
|
||||||
|
* use `MeritCommendation.None` when indicating "no ribbon"
|
||||||
|
* @param bar any of the four positions where the award ribbon is to be displayed;
|
||||||
|
* defaults to `TermOfService`
|
||||||
|
* @see `RibbonBars`
|
||||||
|
* @see `MeritCommendation`
|
||||||
|
*/
|
||||||
|
final case class DisplayedAwardMessage(player_guid : PlanetSideGUID,
|
||||||
|
ribbon : MeritCommendation.Value = MeritCommendation.None,
|
||||||
|
bar : RibbonBarsSlot.Value = RibbonBarsSlot.TermOfService)
|
||||||
|
extends PlanetSideGamePacket {
|
||||||
|
type Packet = DisplayedAwardMessage
|
||||||
|
def opcode = GamePacketOpcode.DisplayedAwardMessage
|
||||||
|
def encode = DisplayedAwardMessage.encode(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
object DisplayedAwardMessage extends Marshallable[DisplayedAwardMessage] {
|
||||||
|
implicit val codec : Codec[DisplayedAwardMessage] = (
|
||||||
|
("player_guid" | PlanetSideGUID.codec) ::
|
||||||
|
("ribbon" | MeritCommendation.codec) ::
|
||||||
|
("bar" | RibbonBarsSlot.codec)
|
||||||
|
).as[DisplayedAwardMessage]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.packet.game
|
||||||
|
|
||||||
|
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||||
|
import net.psforever.types.Vector3
|
||||||
|
import scodec.Codec
|
||||||
|
import scodec.codecs._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* not sure for u1 / u2 / u3, maybe need a real brain ...
|
||||||
|
* @param weapon_guid na
|
||||||
|
* @param pos na; pos of what ?!
|
||||||
|
* @param u1 na
|
||||||
|
* @param u2 na
|
||||||
|
* @param u3 na
|
||||||
|
* @param u4 na
|
||||||
|
* @param u5 na; vel of what ?!
|
||||||
|
*/
|
||||||
|
|
||||||
|
final case class FireHintMessage(weapon_guid : PlanetSideGUID,
|
||||||
|
pos : Vector3,
|
||||||
|
u1 : Int,
|
||||||
|
u2 : Int,
|
||||||
|
u3 : Int,
|
||||||
|
u4 : Int,
|
||||||
|
u5 : Option[Vector3] = None)
|
||||||
|
extends PlanetSideGamePacket {
|
||||||
|
type Packet = FireHintMessage
|
||||||
|
def opcode = GamePacketOpcode.FireHintMessage
|
||||||
|
def encode = FireHintMessage.encode(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
object FireHintMessage extends Marshallable[FireHintMessage] {
|
||||||
|
|
||||||
|
implicit val codec : Codec[FireHintMessage] = (
|
||||||
|
("weapon_guid" | PlanetSideGUID.codec) ::
|
||||||
|
("pos" | Vector3.codec_pos) ::
|
||||||
|
("u1" | uint16L) ::
|
||||||
|
("u2" | uint16L) ::
|
||||||
|
("u3" | uint16L) ::
|
||||||
|
("u4" | uintL(3)) ::
|
||||||
|
optional(bool, "u5" | Vector3.codec_vel)
|
||||||
|
).as[FireHintMessage]
|
||||||
|
}
|
||||||
|
|
@ -55,8 +55,10 @@ object ObjectCreateDetailedMessage extends Marshallable[ObjectCreateDetailedMess
|
||||||
* @param data the data used to construct this type of object
|
* @param data the data used to construct this type of object
|
||||||
* @return an ObjectCreateMessage
|
* @return an ObjectCreateMessage
|
||||||
*/
|
*/
|
||||||
def apply(objectClass : Int, guid : PlanetSideGUID, parentInfo : ObjectCreateMessageParent, data : ConstructorData) : ObjectCreateDetailedMessage =
|
def apply(objectClass : Int, guid : PlanetSideGUID, parentInfo : ObjectCreateMessageParent, data : ConstructorData) : ObjectCreateDetailedMessage = {
|
||||||
ObjectCreateDetailedMessage(0L, objectClass, guid, Some(parentInfo), Some(data))
|
val parentInfoOpt : Option[ObjectCreateMessageParent] = Some(parentInfo)
|
||||||
|
ObjectCreateDetailedMessage(ObjectCreateBase.streamLen(parentInfoOpt, data), objectClass, guid, parentInfoOpt, Some(data))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abbreviated constructor for creating `ObjectCreateMessages`, ignoring `parentInfo`.
|
* An abbreviated constructor for creating `ObjectCreateMessages`, ignoring `parentInfo`.
|
||||||
|
|
@ -65,8 +67,9 @@ object ObjectCreateDetailedMessage extends Marshallable[ObjectCreateDetailedMess
|
||||||
* @param data the data used to construct this type of object
|
* @param data the data used to construct this type of object
|
||||||
* @return an ObjectCreateMessage
|
* @return an ObjectCreateMessage
|
||||||
*/
|
*/
|
||||||
def apply(objectClass : Int, guid : PlanetSideGUID, data : ConstructorData) : ObjectCreateDetailedMessage =
|
def apply(objectClass : Int, guid : PlanetSideGUID, data : ConstructorData) : ObjectCreateDetailedMessage = {
|
||||||
ObjectCreateDetailedMessage(0L, objectClass, guid, None, Some(data))
|
ObjectCreateDetailedMessage(ObjectCreateBase.streamLen(None, data), objectClass, guid, None, Some(data))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take the important information of a game piece and transform it into bit data.
|
* Take the important information of a game piece and transform it into bit data.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.packet.game
|
||||||
|
|
||||||
|
import net.psforever.newcodecs.newcodecs
|
||||||
|
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||||
|
import scodec.{Attempt, Codec}
|
||||||
|
import scodec.codecs._
|
||||||
|
import shapeless.{::, HNil}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alert the condition of a vehicle the player is using when going too far underwater.
|
||||||
|
* The player must be mounted in/on this vehicle at start time for this countdown to display.
|
||||||
|
* @param vehicle_guid the player's mounted vehicle
|
||||||
|
* @param progress the remaining countdown;
|
||||||
|
* for vehicle waterlog condition, the progress per second rate is very high
|
||||||
|
* @param active show a new countdown if `true` (resets any active countdown);
|
||||||
|
* clear any active countdowns if `false`;
|
||||||
|
* defaults to `true`
|
||||||
|
*/
|
||||||
|
final case class WaterloggedVehicleState(vehicle_guid : PlanetSideGUID,
|
||||||
|
progress : Float,
|
||||||
|
active : Boolean = true)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatched by the server to cause the player to slowly drown.
|
||||||
|
* If the player is mounted in a vehicle at the time, alert the player that the vehicle may be disabled.<br>
|
||||||
|
* <br>
|
||||||
|
* When a player walks too far underwater, a borderless red progress bar with a countdown from 100 (98) is displayed across the screen.
|
||||||
|
* The countdown proceeds to zero at a fixed rate and is timed with the depleting progress bar.
|
||||||
|
* When it reaches zero, the player will be killed.
|
||||||
|
* If the player is in a vehicle after a certain depth, a blue bar and countdown pair will superimpose the red indicators.
|
||||||
|
* It depletes much more rapidly than the red indicators.
|
||||||
|
* When it reaches zero, the vehicle will become disabled.
|
||||||
|
* All players in the vehicle's seats will be kicked and they will not be allowed back in.<br>
|
||||||
|
* <br>
|
||||||
|
* Normally, the countdowns should be set to begin at 100 (100.0).
|
||||||
|
* This is the earliest the drowning GUI will appear for either blue or red indicators.
|
||||||
|
* Passing greater intervals - up to 204.8 - will start the countdown silently but the GUI will be hidden until 100.0.
|
||||||
|
* (The progress indicators will actually appear to start counting from 98.)
|
||||||
|
* Managing the secondary vehicle countdown independent of the primary player countdown requires updating with the correct levels.
|
||||||
|
* The countdown can be cancelled by instructing it to be `active = false`.<br>
|
||||||
|
* <br>
|
||||||
|
* Except for updating the indicators, all other functionality of "drowning" is automated by the server.
|
||||||
|
* @param player_guid the player
|
||||||
|
* @param progress the remaining countdown;
|
||||||
|
* for character oxygen, the progress per second rate is about 1
|
||||||
|
* @param active show a new countdown if `true` (resets any active countdown);
|
||||||
|
* clear any active countdowns if `false`
|
||||||
|
* @param vehicle_state optional state of the vehicle the player is driving
|
||||||
|
*/
|
||||||
|
final case class OxygenStateMessage(player_guid : PlanetSideGUID,
|
||||||
|
progress : Float,
|
||||||
|
active : Boolean,
|
||||||
|
vehicle_state : Option[WaterloggedVehicleState] = None)
|
||||||
|
extends PlanetSideGamePacket {
|
||||||
|
type Packet = OxygenStateMessage
|
||||||
|
def opcode = GamePacketOpcode.OxygenStateMessage
|
||||||
|
def encode = OxygenStateMessage.encode(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
object OxygenStateMessage extends Marshallable[OxygenStateMessage] {
|
||||||
|
/**
|
||||||
|
* Overloaded constructor that removes the optional state of the `WaterloggedVehicleState` parameter.
|
||||||
|
* @param player_guid the player
|
||||||
|
* @param progress the remaining countdown
|
||||||
|
* @param active show or clear the countdown
|
||||||
|
* @param vehicle_state state of the vehicle the player is driving
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
def apply(player_guid : PlanetSideGUID, progress : Float, active : Boolean, vehicle_state : WaterloggedVehicleState) : OxygenStateMessage =
|
||||||
|
OxygenStateMessage(player_guid, progress, active, Some(vehicle_state))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple pattern that expands the datatypes of the packet's basic `Codec`.
|
||||||
|
*/
|
||||||
|
private type basePattern = PlanetSideGUID :: Float :: Boolean :: HNil
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A `Codec` for the repeated processing of three values.
|
||||||
|
* This `Codec` is the basis for the packet's data.
|
||||||
|
*/
|
||||||
|
private val base_codec : Codec[basePattern] =
|
||||||
|
PlanetSideGUID.codec ::
|
||||||
|
newcodecs.q_float(0.0f, 204.8f, 11) :: //hackish: 2^11 == 2047, so it should be 204.7; but, 204.8 allows decode == encode
|
||||||
|
bool
|
||||||
|
|
||||||
|
implicit val codec : Codec[OxygenStateMessage] = (
|
||||||
|
base_codec.exmap[basePattern] (
|
||||||
|
{
|
||||||
|
case guid :: time :: active :: HNil =>
|
||||||
|
Attempt.successful(guid :: time :: active :: HNil)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case guid :: time :: active :: HNil =>
|
||||||
|
Attempt.successful(guid :: time :: active :: HNil)
|
||||||
|
}
|
||||||
|
) :+
|
||||||
|
optional(bool,
|
||||||
|
"vehicle_state" | base_codec.exmap[WaterloggedVehicleState] (
|
||||||
|
{
|
||||||
|
case guid :: time :: active :: HNil =>
|
||||||
|
Attempt.successful(WaterloggedVehicleState(guid, time, active))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case WaterloggedVehicleState(guid, time, active) =>
|
||||||
|
Attempt.successful(guid :: time :: active :: HNil)
|
||||||
|
}
|
||||||
|
).as[WaterloggedVehicleState]
|
||||||
|
)
|
||||||
|
).as[OxygenStateMessage]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.packet.game
|
||||||
|
|
||||||
|
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||||
|
import net.psforever.types.Vector3
|
||||||
|
import scodec.Codec
|
||||||
|
import scodec.codecs._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatched to deliberately render certain projectiles of a weapon on other players' clients.<br>
|
||||||
|
* <br>
|
||||||
|
* This packet is generated by firing specific weapons in specific fire modes.
|
||||||
|
* For example, the Phoenix (`hunterseeker`) discharged in its primary fire mode generates this packet;
|
||||||
|
* but, the Phoenix in secondary fire mode does not.
|
||||||
|
* The Striker (`striker`) discharged in its primary fire mode generates this packet;
|
||||||
|
* but, the Striker in secondary fire mode does not.
|
||||||
|
* The chosen fire mode(s) are not a straight-fire projectile but one that has special control asserted over it.
|
||||||
|
* For the Phoenix, it is user-operated.
|
||||||
|
* For the Striker, it tracks towards a target while the weapon's reticle hovers over that target.<br>
|
||||||
|
* <br>
|
||||||
|
* This packet will continue to be dispatched by the client for as long as the projectile being tracked is in the air.
|
||||||
|
* All projectiles have a maximum lifespan before they will lose control and either despawn and/or explode.
|
||||||
|
* This number is tracked in the packet for simplicity.
|
||||||
|
* If the projectile strikes a valid target, the count will jump to a significantly enormous value beyond its normal lifespan.
|
||||||
|
* This ensures that the projectile - locally and the shared model - will despawn.
|
||||||
|
* @param projectile_guid the projectile
|
||||||
|
* @param shot_pos the position of the projectile
|
||||||
|
* @param shot_vel the velocity of the projectile
|
||||||
|
* @param unk1 na;
|
||||||
|
* usually 0
|
||||||
|
* @param unk2 na;
|
||||||
|
* will remain consistent for the lifespan of a given projectile in most cases
|
||||||
|
* @param unk3 na;
|
||||||
|
* will remain consistent for the lifespan of a given projectile in most cases
|
||||||
|
* @param unk4 na;
|
||||||
|
* usually false
|
||||||
|
* @param time_alive how long the projectile has been in the air;
|
||||||
|
* often expressed in multiples of 2
|
||||||
|
*/
|
||||||
|
final case class ProjectileStateMessage(projectile_guid : PlanetSideGUID,
|
||||||
|
shot_pos : Vector3,
|
||||||
|
shot_vel : Vector3,
|
||||||
|
unk1 : Int,
|
||||||
|
unk2 : Int,
|
||||||
|
unk3 : Int,
|
||||||
|
unk4 : Boolean,
|
||||||
|
time_alive : Int)
|
||||||
|
extends PlanetSideGamePacket {
|
||||||
|
type Packet = ProjectileStateMessage
|
||||||
|
def opcode = GamePacketOpcode.ProjectileStateMessage
|
||||||
|
def encode = ProjectileStateMessage.encode(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
object ProjectileStateMessage extends Marshallable[ProjectileStateMessage] {
|
||||||
|
implicit val codec : Codec[ProjectileStateMessage] = (
|
||||||
|
("projectile_guid" | PlanetSideGUID.codec) ::
|
||||||
|
("shot_pos" | Vector3.codec_pos) ::
|
||||||
|
("shot_vel" | Vector3.codec_float) ::
|
||||||
|
("unk1" | uint8L) ::
|
||||||
|
("unk2" | uint8L) ::
|
||||||
|
("unk3" | uint8L) ::
|
||||||
|
("unk4" | bool) ::
|
||||||
|
("time_alive" | uint16L)
|
||||||
|
).as[ProjectileStateMessage]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.packet.game
|
||||||
|
|
||||||
|
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||||
|
import scodec.Codec
|
||||||
|
import scodec.codecs._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An entry regarding a specific target.
|
||||||
|
* @param target_guid the target
|
||||||
|
* @param unk na
|
||||||
|
*/
|
||||||
|
final case class TargetRequest(target_guid : PlanetSideGUID,
|
||||||
|
unk : Boolean)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatched by the client when the advanced targeting implant activates to collect status information from the server.<br>
|
||||||
|
* <br>
|
||||||
|
* This packet is answered by a `TargetingInfoMessage` with `List` entries of thed corresponding UIDs.
|
||||||
|
* @param target_list a `List` of targets
|
||||||
|
*/
|
||||||
|
final case class TargetingImplantRequest(target_list : List[TargetRequest])
|
||||||
|
extends PlanetSideGamePacket {
|
||||||
|
type Packet = TargetingImplantRequest
|
||||||
|
def opcode = GamePacketOpcode.TargetingImplantRequest
|
||||||
|
def encode = TargetingImplantRequest.encode(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
object TargetingImplantRequest extends Marshallable[TargetingImplantRequest] {
|
||||||
|
private val request_codec : Codec[TargetRequest] = (
|
||||||
|
("target_guid" | PlanetSideGUID.codec) ::
|
||||||
|
("unk" | bool)
|
||||||
|
).as[TargetRequest]
|
||||||
|
|
||||||
|
implicit val codec : Codec[TargetingImplantRequest] = ("target_list" | listOfN(intL(6), request_codec)).as[TargetingImplantRequest]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.packet.game
|
||||||
|
|
||||||
|
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
|
||||||
|
import net.psforever.types.Vector3
|
||||||
|
import scodec.Codec
|
||||||
|
import scodec.codecs._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* na
|
||||||
|
* @param unk1 na;
|
||||||
|
* `true` to apply the effect usually
|
||||||
|
* @param unk2 na
|
||||||
|
*/
|
||||||
|
final case class TriggeredEffect(unk1 : Boolean,
|
||||||
|
unk2 : Long)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activate an effect that is not directly associated with an existing game object.
|
||||||
|
* Without a game object from which to inherit position and orientation, those explicit parameters must be provided.
|
||||||
|
* @param pos the position in the game world
|
||||||
|
* @param roll the amount of roll that affects orientation
|
||||||
|
* @param pitch the amount of pitch that affects orientation
|
||||||
|
* @param yaw the amount of yaw that affects orientation
|
||||||
|
*/
|
||||||
|
final case class TriggeredEffectLocation(pos : Vector3,
|
||||||
|
roll : Int,
|
||||||
|
pitch : Int,
|
||||||
|
yaw : Int)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatched by the server to cause a client to display a special graphical effect.<br>
|
||||||
|
* <br>
|
||||||
|
* The effect being triggered can be based around a specific game object or replayed freely, absent of an anchoring object.
|
||||||
|
* If object-based then the kinds of effects that can be activated are specific to the object.
|
||||||
|
* If unbound, then a wider range of effects can be displayed.
|
||||||
|
* Regardless, one category will rarely ever be activated under the same valid conditions of the other category.
|
||||||
|
* For example, the effect "on" will only work on objects that accept "on" normally, like a deployed `motionalarmsensor`.
|
||||||
|
* The effect "spawn_object_effect" can be applied anywhere in the environment;
|
||||||
|
* but, it can not be activated in conjunction with an existing object.
|
||||||
|
* @param obj an object that accepts the effect
|
||||||
|
* @param effect the name of the effect
|
||||||
|
* @param unk na;
|
||||||
|
* when activating an effect on an existing object
|
||||||
|
* @param location an optional position where the effect will be displayed;
|
||||||
|
* when activating an effect independently
|
||||||
|
*/
|
||||||
|
final case class TriggerEffectMessage(obj : PlanetSideGUID,
|
||||||
|
effect : String,
|
||||||
|
unk : Option[TriggeredEffect] = None,
|
||||||
|
location : Option[TriggeredEffectLocation] = None
|
||||||
|
) extends PlanetSideGamePacket {
|
||||||
|
type Packet = TriggerEffectMessage
|
||||||
|
def opcode = GamePacketOpcode.TriggerEffectMessage
|
||||||
|
def encode = TriggerEffectMessage.encode(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
object TriggerEffectMessage extends Marshallable[TriggerEffectMessage] {
|
||||||
|
/**
|
||||||
|
* A `Codec` for `TriggeredEffect` data.
|
||||||
|
*/
|
||||||
|
private val effect_codec : Codec[TriggeredEffect] = (
|
||||||
|
("unk1" | bool) ::
|
||||||
|
("unk2" | uint32L)
|
||||||
|
).as[TriggeredEffect]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A `Codec` for `TriggeredEffectLocation` data.
|
||||||
|
*/
|
||||||
|
private val effect_location_codec : Codec[TriggeredEffectLocation] = (
|
||||||
|
("pos" | Vector3.codec_pos) ::
|
||||||
|
("roll" | uint8L) ::
|
||||||
|
("pitch" | uint8L) ::
|
||||||
|
("yaw" | uint8L)
|
||||||
|
).as[TriggeredEffectLocation]
|
||||||
|
|
||||||
|
implicit val codec : Codec[TriggerEffectMessage] = (
|
||||||
|
("obj" | PlanetSideGUID.codec) >>:~ { obj =>
|
||||||
|
("effect" | PacketHelpers.encodedString) ::
|
||||||
|
optional(bool, "unk" | effect_codec) ::
|
||||||
|
conditional(obj.guid == 0, "location" | effect_location_codec)
|
||||||
|
}).as[TriggerEffectMessage]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.packet.game
|
||||||
|
|
||||||
|
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||||
|
import scodec.Codec
|
||||||
|
import scodec.codecs._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatched by the client when its player is done using something.<br>
|
||||||
|
* <br>
|
||||||
|
* The common example is sifting through backpacks, an activity that only one player is allowed to do at a time.
|
||||||
|
* When a backpack is accessed by one player, other players are blocked.
|
||||||
|
* When the first player is done accessing the backpack, this packet informs the server so other players may be allowed access.
|
||||||
|
* @param player_guid the player
|
||||||
|
* @param item_guid the item
|
||||||
|
*/
|
||||||
|
final case class UnuseItemMessage(player_guid : PlanetSideGUID,
|
||||||
|
item_guid : PlanetSideGUID)
|
||||||
|
extends PlanetSideGamePacket {
|
||||||
|
type Packet = UnuseItemMessage
|
||||||
|
def opcode = GamePacketOpcode.UnuseItemMessage
|
||||||
|
def encode = UnuseItemMessage.encode(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
object UnuseItemMessage extends Marshallable[UnuseItemMessage] {
|
||||||
|
implicit val codec : Codec[UnuseItemMessage] = (
|
||||||
|
("player_guid" | PlanetSideGUID.codec) ::
|
||||||
|
("item_guid" | PlanetSideGUID.codec)
|
||||||
|
).as[UnuseItemMessage]
|
||||||
|
}
|
||||||
|
|
@ -2,35 +2,33 @@
|
||||||
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.MeritCommendation
|
||||||
import scodec.Codec
|
import scodec.Codec
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumerate the player-displayed merit commendation awards granted for excellence (or tenacity) in combat.
|
* Enumerate the player-displayed merit commendation awards granted for excellence (or tenacity) in combat.
|
||||||
* These are the medals players wish to brandish on their left pauldron.<br>
|
* These are the medals players wish to brandish on their left pauldron.
|
||||||
* <br>
|
|
||||||
* All merit commendation ribbons are represented by a 32-bit signature.
|
|
||||||
* The default "no-ribbon" value is `0xFFFFFFFF`, although some illegal values will also work.
|
|
||||||
* The term of service ribbon can not be modified by the user and will apply itself to its slot automatically when valid.
|
|
||||||
* @param upper the "top" configurable merit ribbon
|
* @param upper the "top" configurable merit ribbon
|
||||||
* @param middle the central configurable merit ribbon
|
* @param middle the central configurable merit ribbon
|
||||||
* @param lower the lower configurable merit ribbon
|
* @param lower the lower configurable merit ribbon
|
||||||
* @param tos the top-most term of service merit ribbon
|
* @param tos the top-most term of service merit ribbon
|
||||||
|
* @see `MeritCommendation`
|
||||||
|
* @see `DisplayedAwardMessage`
|
||||||
*/
|
*/
|
||||||
final case class RibbonBars(upper : Long = RibbonBars.noRibbon,
|
final case class RibbonBars(upper : MeritCommendation.Value = MeritCommendation.None,
|
||||||
middle : Long = RibbonBars.noRibbon,
|
middle : MeritCommendation.Value = MeritCommendation.None,
|
||||||
lower : Long = RibbonBars.noRibbon,
|
lower : MeritCommendation.Value = MeritCommendation.None,
|
||||||
tos : Long = RibbonBars.noRibbon) extends StreamBitSize {
|
tos : MeritCommendation.Value = MeritCommendation.None
|
||||||
|
) extends StreamBitSize {
|
||||||
override def bitsize : Long = 128L
|
override def bitsize : Long = 128L
|
||||||
}
|
}
|
||||||
|
|
||||||
object RibbonBars extends Marshallable[RibbonBars] {
|
object RibbonBars extends Marshallable[RibbonBars] {
|
||||||
val noRibbon : Long = 0xFFFFFFFFL
|
|
||||||
|
|
||||||
implicit val codec : Codec[RibbonBars] = (
|
implicit val codec : Codec[RibbonBars] = (
|
||||||
("upper" | uint32L) ::
|
("upper" | MeritCommendation.codec) ::
|
||||||
("middle" | uint32L) ::
|
("middle" | MeritCommendation.codec) ::
|
||||||
("lower" | uint32L) ::
|
("lower" | MeritCommendation.codec) ::
|
||||||
("tos" | uint32L)
|
("tos" | MeritCommendation.codec)
|
||||||
).as[RibbonBars]
|
).as[RibbonBars]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,522 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package net.psforever.types
|
||||||
|
|
||||||
|
import scodec.{Attempt, Err}
|
||||||
|
import scodec.codecs._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An `Enumeration` of all merit commendation award categories organized into associated ribbons.
|
||||||
|
* By astonishing coincidence, with exception of the first ten special awards, the rest of list is in alphabetical order.
|
||||||
|
*/
|
||||||
|
object MeritCommendation extends Enumeration {
|
||||||
|
type Type = Value
|
||||||
|
|
||||||
|
val //0
|
||||||
|
FanFaire2005Commander,
|
||||||
|
FanFaire2005Soldier,
|
||||||
|
FanFaire2006Atlanta,
|
||||||
|
HalloweenMassacre2006NC,
|
||||||
|
HalloweenMassacre2006TR,
|
||||||
|
HalloweenMassacre2006VS,
|
||||||
|
FanFaire2007,
|
||||||
|
FanFaire2008,
|
||||||
|
FanFaire2009,
|
||||||
|
AdvancedMedic1,
|
||||||
|
//10
|
||||||
|
AdvancedMedic2,
|
||||||
|
AdvancedMedic3,
|
||||||
|
AdvancedMedic4,
|
||||||
|
AdvancedMedic5,
|
||||||
|
AdvancedMedic6,
|
||||||
|
AdvancedMedic7,
|
||||||
|
AdvancedMedicAssists1,
|
||||||
|
AdvancedMedicAssists2,
|
||||||
|
AdvancedMedicAssists3,
|
||||||
|
AdvancedMedicAssists4,
|
||||||
|
//20
|
||||||
|
AdvancedMedicAssists5,
|
||||||
|
AdvancedMedicAssists6,
|
||||||
|
AdvancedMedicAssists7,
|
||||||
|
AirDefender1,
|
||||||
|
AirDefender2,
|
||||||
|
AirDefender3,
|
||||||
|
AirDefender4,
|
||||||
|
AirDefender5,
|
||||||
|
AirDefender6,
|
||||||
|
AirDefender7,
|
||||||
|
//30
|
||||||
|
AMSSupport1,
|
||||||
|
AMSSupport2,
|
||||||
|
AMSSupport3,
|
||||||
|
AMSSupport4,
|
||||||
|
AMSSupport5,
|
||||||
|
AMSSupport6,
|
||||||
|
AMSSupport7,
|
||||||
|
AntiVehicular1,
|
||||||
|
AntiVehicular2,
|
||||||
|
AntiVehicular3,
|
||||||
|
//40
|
||||||
|
AntiVehicular4,
|
||||||
|
AntiVehicular5,
|
||||||
|
AntiVehicular6,
|
||||||
|
AntiVehicular7,
|
||||||
|
Avenger1,
|
||||||
|
Avenger2,
|
||||||
|
Avenger3,
|
||||||
|
Avenger4,
|
||||||
|
Avenger5,
|
||||||
|
Avenger6,
|
||||||
|
//50
|
||||||
|
Avenger7,
|
||||||
|
AwardColors, //what?
|
||||||
|
BendingMovieActor,
|
||||||
|
BFRAdvanced,
|
||||||
|
BFRAdvanced2,
|
||||||
|
BFRAdvanced3,
|
||||||
|
BFRAdvanced4,
|
||||||
|
BFRAdvanced5,
|
||||||
|
BFRBuster1,
|
||||||
|
BFRBuster2,
|
||||||
|
//60
|
||||||
|
BFRBuster3,
|
||||||
|
BFRBuster4,
|
||||||
|
BFRBuster5,
|
||||||
|
BFRBuster6,
|
||||||
|
BFRBuster7,
|
||||||
|
BlackOpsHunter1,
|
||||||
|
BlackOpsHunter2,
|
||||||
|
BlackOpsHunter3,
|
||||||
|
BlackOpsHunter4,
|
||||||
|
BlackOpsHunter5,
|
||||||
|
//70
|
||||||
|
BlackOpsParticipant,
|
||||||
|
BlackOpsVictory,
|
||||||
|
Bombadier1,
|
||||||
|
Bombadier2,
|
||||||
|
Bombadier3,
|
||||||
|
Bombadier4,
|
||||||
|
Bombadier5,
|
||||||
|
Bombadier6,
|
||||||
|
Bombadier7,
|
||||||
|
BomberAce1,
|
||||||
|
//80
|
||||||
|
BomberAce2,
|
||||||
|
BomberAce3,
|
||||||
|
BomberAce4,
|
||||||
|
BomberAce5,
|
||||||
|
BomberAce6,
|
||||||
|
BomberAce7,
|
||||||
|
Boomer1,
|
||||||
|
Boomer2,
|
||||||
|
Boomer3,
|
||||||
|
Boomer4,
|
||||||
|
//90
|
||||||
|
Boomer5,
|
||||||
|
Boomer6,
|
||||||
|
Boomer7,
|
||||||
|
CalvaryDriver1,
|
||||||
|
CalvaryDriver2,
|
||||||
|
CalvaryDriver3,
|
||||||
|
CalvaryDriver4,
|
||||||
|
CalvaryDriver5,
|
||||||
|
CalvaryDriver6,
|
||||||
|
CalvaryDriver7,
|
||||||
|
//100
|
||||||
|
CalvaryPilot,
|
||||||
|
CalvaryPilot2,
|
||||||
|
CalvaryPilot3,
|
||||||
|
CalvaryPilot4,
|
||||||
|
CalvaryPilot5,
|
||||||
|
CalvaryPilot6,
|
||||||
|
CalvaryPilot7,
|
||||||
|
CMTopOutfit,
|
||||||
|
CombatMedic,
|
||||||
|
CombatMedic2,
|
||||||
|
//110
|
||||||
|
CombatMedic3,
|
||||||
|
CombatMedic4,
|
||||||
|
CombatMedic5,
|
||||||
|
CombatMedic6,
|
||||||
|
CombatMedic7,
|
||||||
|
CombatRepair1,
|
||||||
|
CombatRepair2,
|
||||||
|
CombatRepair3,
|
||||||
|
CombatRepair4,
|
||||||
|
CombatRepair5,
|
||||||
|
//120
|
||||||
|
CombatRepair6,
|
||||||
|
CombatRepair7,
|
||||||
|
ContestFirstBR40,
|
||||||
|
ContestMovieMaker,
|
||||||
|
ContestMovieMakerOutfit,
|
||||||
|
ContestPlayerOfTheMonth,
|
||||||
|
ContestPlayerOfTheYear,
|
||||||
|
CSAppreciation,
|
||||||
|
DefenseNC1,
|
||||||
|
DefenseNC2,
|
||||||
|
//130
|
||||||
|
DefenseNC3,
|
||||||
|
DefenseNC4,
|
||||||
|
DefenseNC5,
|
||||||
|
DefenseNC6,
|
||||||
|
DefenseNC7,
|
||||||
|
DefenseTR1,
|
||||||
|
DefenseTR2,
|
||||||
|
DefenseTR3,
|
||||||
|
DefenseTR4,
|
||||||
|
DefenseTR5,
|
||||||
|
//40
|
||||||
|
DefenseTR6,
|
||||||
|
DefenseTR7,
|
||||||
|
DefenseVS1,
|
||||||
|
DefenseVS2,
|
||||||
|
DefenseVS3,
|
||||||
|
DefenseVS4,
|
||||||
|
DefenseVS5,
|
||||||
|
DefenseVS6,
|
||||||
|
DefenseVS7,
|
||||||
|
DevilDogsMovie,
|
||||||
|
//150
|
||||||
|
DogFighter1,
|
||||||
|
DogFighter2,
|
||||||
|
DogFighter3,
|
||||||
|
DogFighter4,
|
||||||
|
DogFighter5,
|
||||||
|
DogFighter6,
|
||||||
|
DogFighter7,
|
||||||
|
DriverGunner1,
|
||||||
|
DriverGunner2,
|
||||||
|
DriverGunner3,
|
||||||
|
//160
|
||||||
|
DriverGunner4,
|
||||||
|
DriverGunner5,
|
||||||
|
DriverGunner6,
|
||||||
|
DriverGunner7,
|
||||||
|
EliteAssault0,
|
||||||
|
EliteAssault1,
|
||||||
|
EliteAssault2,
|
||||||
|
EliteAssault3,
|
||||||
|
EliteAssault4,
|
||||||
|
EliteAssault5,
|
||||||
|
//170
|
||||||
|
EliteAssault6,
|
||||||
|
EliteAssault7,
|
||||||
|
EmeraldVeteran,
|
||||||
|
Engineer1,
|
||||||
|
Engineer2,
|
||||||
|
Engineer3,
|
||||||
|
Engineer4,
|
||||||
|
Engineer5,
|
||||||
|
Engineer6,
|
||||||
|
EquipmentSupport1,
|
||||||
|
//180
|
||||||
|
EquipmentSupport2,
|
||||||
|
EquipmentSupport3,
|
||||||
|
EquipmentSupport4,
|
||||||
|
EquipmentSupport5,
|
||||||
|
EquipmentSupport6,
|
||||||
|
EquipmentSupport7,
|
||||||
|
EventNCCommander,
|
||||||
|
EventNCElite,
|
||||||
|
EventNCSoldier,
|
||||||
|
EventTRCommander,
|
||||||
|
//190
|
||||||
|
EventTRElite,
|
||||||
|
EventTRSoldier,
|
||||||
|
EventVSCommander,
|
||||||
|
EventVSElite,
|
||||||
|
EventVSSoldier,
|
||||||
|
Explorer1,
|
||||||
|
FiveYearNC,
|
||||||
|
FiveYearTR,
|
||||||
|
FiveYearVS,
|
||||||
|
FourYearNC,
|
||||||
|
//200
|
||||||
|
FourYearTR,
|
||||||
|
FourYearVS,
|
||||||
|
GalaxySupport1,
|
||||||
|
GalaxySupport2,
|
||||||
|
GalaxySupport3,
|
||||||
|
GalaxySupport4,
|
||||||
|
GalaxySupport5,
|
||||||
|
GalaxySupport6,
|
||||||
|
GalaxySupport7,
|
||||||
|
Grenade1,
|
||||||
|
//210
|
||||||
|
Grenade2,
|
||||||
|
Grenade3,
|
||||||
|
Grenade4,
|
||||||
|
Grenade5,
|
||||||
|
Grenade6,
|
||||||
|
Grenade7,
|
||||||
|
GroundGunner1,
|
||||||
|
GroundGunner2,
|
||||||
|
GroundGunner3,
|
||||||
|
GroundGunner4,
|
||||||
|
//220
|
||||||
|
GroundGunner5,
|
||||||
|
GroundGunner6,
|
||||||
|
GroundGunner7,
|
||||||
|
HackingSupport1,
|
||||||
|
HackingSupport2,
|
||||||
|
HackingSupport3,
|
||||||
|
HackingSupport4,
|
||||||
|
HackingSupport5,
|
||||||
|
HackingSupport6,
|
||||||
|
HackingSupport7,
|
||||||
|
//230
|
||||||
|
HeavyAssault1,
|
||||||
|
HeavyAssault2,
|
||||||
|
HeavyAssault3,
|
||||||
|
HeavyAssault4,
|
||||||
|
HeavyAssault5,
|
||||||
|
HeavyAssault6,
|
||||||
|
HeavyAssault7,
|
||||||
|
HeavyInfantry,
|
||||||
|
HeavyInfantry2,
|
||||||
|
HeavyInfantry3,
|
||||||
|
//240
|
||||||
|
HeavyInfantry4,
|
||||||
|
InfantryExpert1,
|
||||||
|
InfantryExpert2,
|
||||||
|
InfantryExpert3,
|
||||||
|
Jacking,
|
||||||
|
Jacking2,
|
||||||
|
Jacking3,
|
||||||
|
Jacking4,
|
||||||
|
Jacking5,
|
||||||
|
Jacking6,
|
||||||
|
//250
|
||||||
|
Jacking7,
|
||||||
|
KnifeCombat1,
|
||||||
|
KnifeCombat2,
|
||||||
|
KnifeCombat3,
|
||||||
|
KnifeCombat4,
|
||||||
|
KnifeCombat5,
|
||||||
|
KnifeCombat6,
|
||||||
|
KnifeCombat7,
|
||||||
|
LightInfantry,
|
||||||
|
LockerCracker1,
|
||||||
|
//260
|
||||||
|
LockerCracker2,
|
||||||
|
LockerCracker3,
|
||||||
|
LockerCracker4,
|
||||||
|
LockerCracker5,
|
||||||
|
LockerCracker6,
|
||||||
|
LockerCracker7,
|
||||||
|
LodestarSupport1,
|
||||||
|
LodestarSupport2,
|
||||||
|
LodestarSupport3,
|
||||||
|
LodestarSupport4,
|
||||||
|
//270
|
||||||
|
LodestarSupport5,
|
||||||
|
LodestarSupport6,
|
||||||
|
LodestarSupport7,
|
||||||
|
Loser,
|
||||||
|
Loser2,
|
||||||
|
Loser3,
|
||||||
|
Loser4,
|
||||||
|
MarkovVeteran,
|
||||||
|
Max1,
|
||||||
|
Max2,
|
||||||
|
//280
|
||||||
|
Max3,
|
||||||
|
Max4,
|
||||||
|
Max5,
|
||||||
|
Max6,
|
||||||
|
MaxBuster1,
|
||||||
|
MaxBuster2,
|
||||||
|
MaxBuster3,
|
||||||
|
MaxBuster4,
|
||||||
|
MaxBuster5,
|
||||||
|
MaxBuster6,
|
||||||
|
//290
|
||||||
|
MediumAssault1,
|
||||||
|
MediumAssault2,
|
||||||
|
MediumAssault3,
|
||||||
|
MediumAssault4,
|
||||||
|
MediumAssault5,
|
||||||
|
MediumAssault6,
|
||||||
|
MediumAssault7,
|
||||||
|
OneYearNC,
|
||||||
|
OneYearTR,
|
||||||
|
OneYearVS,
|
||||||
|
//300
|
||||||
|
Orion1,
|
||||||
|
Orion2,
|
||||||
|
Orion3,
|
||||||
|
Orion4,
|
||||||
|
Orion5,
|
||||||
|
Orion6,
|
||||||
|
Orion7,
|
||||||
|
Osprey1,
|
||||||
|
Osprey2,
|
||||||
|
Osprey3,
|
||||||
|
//310
|
||||||
|
Osprey4,
|
||||||
|
Osprey5,
|
||||||
|
Osprey6,
|
||||||
|
Osprey7,
|
||||||
|
Phalanx1,
|
||||||
|
Phalanx2,
|
||||||
|
Phalanx3,
|
||||||
|
Phalanx4,
|
||||||
|
Phalanx5,
|
||||||
|
Phalanx6,
|
||||||
|
//320
|
||||||
|
Phalanx7,
|
||||||
|
PSUMaAttendee,
|
||||||
|
PSUMbAttendee,
|
||||||
|
QAAppreciation,
|
||||||
|
ReinforcementHackSpecialist,
|
||||||
|
ReinforcementInfantrySpecialist,
|
||||||
|
ReinforcementSpecialist,
|
||||||
|
ReinforcementVehicleSpecialist,
|
||||||
|
RouterSupport1,
|
||||||
|
RouterSupport2,
|
||||||
|
//330
|
||||||
|
RouterSupport3,
|
||||||
|
RouterSupport4,
|
||||||
|
RouterSupport5,
|
||||||
|
RouterSupport6,
|
||||||
|
RouterSupport7,
|
||||||
|
RouterTelepadDeploy1,
|
||||||
|
RouterTelepadDeploy2,
|
||||||
|
RouterTelepadDeploy3,
|
||||||
|
RouterTelepadDeploy4,
|
||||||
|
RouterTelepadDeploy5,
|
||||||
|
//340
|
||||||
|
RouterTelepadDeploy6,
|
||||||
|
RouterTelepadDeploy7,
|
||||||
|
ScavengerNC1,
|
||||||
|
ScavengerNC2,
|
||||||
|
ScavengerNC3,
|
||||||
|
ScavengerNC4,
|
||||||
|
ScavengerNC5,
|
||||||
|
ScavengerNC6,
|
||||||
|
ScavengerTR1,
|
||||||
|
ScavengerTR2,
|
||||||
|
//350
|
||||||
|
ScavengerTR3,
|
||||||
|
ScavengerTR4,
|
||||||
|
ScavengerTR5,
|
||||||
|
ScavengerTR6,
|
||||||
|
ScavengerVS1,
|
||||||
|
ScavengerVS2,
|
||||||
|
ScavengerVS3,
|
||||||
|
ScavengerVS4,
|
||||||
|
ScavengerVS5,
|
||||||
|
ScavengerVS6,
|
||||||
|
//360
|
||||||
|
SixYearNC,
|
||||||
|
SixYearTR,
|
||||||
|
SixYearVS,
|
||||||
|
Sniper1,
|
||||||
|
Sniper2,
|
||||||
|
Sniper3,
|
||||||
|
Sniper4,
|
||||||
|
Sniper5,
|
||||||
|
Sniper6,
|
||||||
|
Sniper7,
|
||||||
|
//370
|
||||||
|
SpecialAssault1,
|
||||||
|
SpecialAssault2,
|
||||||
|
SpecialAssault3,
|
||||||
|
SpecialAssault4,
|
||||||
|
SpecialAssault5,
|
||||||
|
SpecialAssault6,
|
||||||
|
SpecialAssault7,
|
||||||
|
StandardAssault1,
|
||||||
|
StandardAssault2,
|
||||||
|
StandardAssault3,
|
||||||
|
//380
|
||||||
|
StandardAssault4,
|
||||||
|
StandardAssault5,
|
||||||
|
StandardAssault6,
|
||||||
|
StandardAssault7,
|
||||||
|
StracticsHistorian,
|
||||||
|
Supply1,
|
||||||
|
Supply2,
|
||||||
|
Supply3,
|
||||||
|
Supply4,
|
||||||
|
Supply5,
|
||||||
|
//390
|
||||||
|
Supply6,
|
||||||
|
Supply7,
|
||||||
|
TankBuster1,
|
||||||
|
TankBuster2,
|
||||||
|
TankBuster3,
|
||||||
|
TankBuster4,
|
||||||
|
TankBuster5,
|
||||||
|
TankBuster6,
|
||||||
|
TankBuster7,
|
||||||
|
ThreeYearNC,
|
||||||
|
//400
|
||||||
|
ThreeYearTR,
|
||||||
|
ThreeYearVS,
|
||||||
|
TinyRoboticSupport1,
|
||||||
|
TinyRoboticSupport2,
|
||||||
|
TinyRoboticSupport3,
|
||||||
|
TinyRoboticSupport4,
|
||||||
|
TinyRoboticSupport5,
|
||||||
|
TinyRoboticSupport6,
|
||||||
|
TinyRoboticSupport7,
|
||||||
|
Transport1,
|
||||||
|
//410
|
||||||
|
Transport2,
|
||||||
|
Transport3,
|
||||||
|
Transport4,
|
||||||
|
Transport5,
|
||||||
|
Transport6,
|
||||||
|
Transport7,
|
||||||
|
TransportationCitation1,
|
||||||
|
TransportationCitation2,
|
||||||
|
TransportationCitation3,
|
||||||
|
TransportationCitation4,
|
||||||
|
//420
|
||||||
|
TransportationCitation5,
|
||||||
|
TwoYearNC,
|
||||||
|
TwoYearTR,
|
||||||
|
TwoYearVS,
|
||||||
|
ValentineFemale,
|
||||||
|
ValentineMale,
|
||||||
|
WernerVeteran,
|
||||||
|
XmasGingerman,
|
||||||
|
XmasSnowman,
|
||||||
|
XmasSpirit
|
||||||
|
= Value
|
||||||
|
|
||||||
|
/*
|
||||||
|
The value None requires special consideration.
|
||||||
|
- A Long number is required for this Enumeration codec.
|
||||||
|
- Enumerations are designed to only handle Int numbers.
|
||||||
|
- A Codec only handles unsigned numbers.
|
||||||
|
- The value of MeritCommendation.None is intended to be 0xFFFFFFFF, which (a) is 4294967295 as a Long, but (b) is -1 as an Integer.
|
||||||
|
- Due to (a), an Enumeration can not be used to represent that number.
|
||||||
|
- Due to (b), a Codec can not be used to convert to that number.
|
||||||
|
*/
|
||||||
|
val None = Value(-1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Carefully and explicitly convert between `Codec[Long] -> Long -> Int -> MeritCommendation.Value`.
|
||||||
|
*/
|
||||||
|
implicit val codec = uint32L.exmap[MeritCommendation.Value] (
|
||||||
|
{
|
||||||
|
case 0xFFFFFFFFL =>
|
||||||
|
Attempt.successful(MeritCommendation.None)
|
||||||
|
case n =>
|
||||||
|
if(n > Int.MaxValue) {
|
||||||
|
Attempt.failure(Err(s"value $n is too high, above maximum integer value ${Int.MaxValue}"))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Attempt.successful(MeritCommendation(n.toInt))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
case MeritCommendation.None =>
|
||||||
|
Attempt.successful(0xFFFFFFFFL)
|
||||||
|
case enum =>
|
||||||
|
Attempt.successful(enum.id.toLong)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
30
common/src/test/scala/game/DelayedPathMountMsgTest.scala
Normal file
30
common/src/test/scala/game/DelayedPathMountMsgTest.scala
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package game
|
||||||
|
|
||||||
|
import org.specs2.mutable._
|
||||||
|
import net.psforever.packet._
|
||||||
|
import net.psforever.packet.game._
|
||||||
|
import scodec.bits._
|
||||||
|
|
||||||
|
class DelayedPathMountMsgTest extends Specification {
|
||||||
|
val string = hex"5a f50583044680"
|
||||||
|
|
||||||
|
"decode" in {
|
||||||
|
PacketCoding.DecodePacket(string).require match {
|
||||||
|
case DelayedPathMountMsg(player_guid, vehicle_guid,u3,u4) =>
|
||||||
|
player_guid mustEqual PlanetSideGUID(1525)
|
||||||
|
vehicle_guid mustEqual PlanetSideGUID(1155)
|
||||||
|
u3 mustEqual 70
|
||||||
|
u4 mustEqual true
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode" in {
|
||||||
|
val msg = DelayedPathMountMsg(PlanetSideGUID(1525), PlanetSideGUID(1155),70,true)
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string
|
||||||
|
}
|
||||||
|
}
|
||||||
30
common/src/test/scala/game/DisplayedAwardMessageTest.scala
Normal file
30
common/src/test/scala/game/DisplayedAwardMessageTest.scala
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package game
|
||||||
|
|
||||||
|
import net.psforever.types.MeritCommendation
|
||||||
|
import org.specs2.mutable._
|
||||||
|
import net.psforever.packet._
|
||||||
|
import net.psforever.packet.game._
|
||||||
|
import scodec.bits._
|
||||||
|
|
||||||
|
class DisplayedAwardMessageTest extends Specification {
|
||||||
|
val string = hex"D1 9F06 A6010000 3 0"
|
||||||
|
|
||||||
|
"decode" in {
|
||||||
|
PacketCoding.DecodePacket(string).require match {
|
||||||
|
case DisplayedAwardMessage(player_guid, ribbon, bar) =>
|
||||||
|
player_guid mustEqual PlanetSideGUID(1695)
|
||||||
|
ribbon mustEqual MeritCommendation.TwoYearTR
|
||||||
|
bar mustEqual RibbonBarsSlot.TermOfService
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode" in {
|
||||||
|
val msg = DisplayedAwardMessage(PlanetSideGUID(1695), MeritCommendation.TwoYearTR, RibbonBarsSlot.TermOfService)
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string
|
||||||
|
}
|
||||||
|
}
|
||||||
55
common/src/test/scala/game/FireHintMessageTest.scala
Normal file
55
common/src/test/scala/game/FireHintMessageTest.scala
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package game
|
||||||
|
|
||||||
|
import org.specs2.mutable._
|
||||||
|
import net.psforever.packet._
|
||||||
|
import net.psforever.packet.game._
|
||||||
|
import net.psforever.types.Vector3
|
||||||
|
import scodec.bits._
|
||||||
|
|
||||||
|
class FireHintMessageTest extends Specification {
|
||||||
|
val string = hex"a1 0117 23cd63f1d7480d 000077ff9d1d00"
|
||||||
|
val string2 = hex"a1 080e 65af5705074411 0000cffee0fc7b08899f5580"
|
||||||
|
|
||||||
|
"decode" in {
|
||||||
|
PacketCoding.DecodePacket(string).require match {
|
||||||
|
case FireHintMessage(weapon_guid, pos, u1, u2, u3, u4, u5) =>
|
||||||
|
weapon_guid mustEqual PlanetSideGUID(5889)
|
||||||
|
pos mustEqual Vector3(3482.2734f,3642.4922f,53.125f)
|
||||||
|
u1 mustEqual 0
|
||||||
|
u2 mustEqual 65399
|
||||||
|
u3 mustEqual 7581
|
||||||
|
u4 mustEqual 0
|
||||||
|
u5 mustEqual None
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"decode string2" in {
|
||||||
|
PacketCoding.DecodePacket(string2).require match {
|
||||||
|
case FireHintMessage(weapon_guid, pos, u1, u2, u3, u4, u5) =>
|
||||||
|
weapon_guid mustEqual PlanetSideGUID(3592)
|
||||||
|
pos mustEqual Vector3(2910.789f,3744.875f,69.0625f)
|
||||||
|
u1 mustEqual 0
|
||||||
|
u2 mustEqual 65231
|
||||||
|
u3 mustEqual 64736
|
||||||
|
u4 mustEqual 3
|
||||||
|
u5 mustEqual Some(Vector3(21.5f,-6.8125f,2.65625f))
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode" in {
|
||||||
|
val msg = FireHintMessage(PlanetSideGUID(5889), Vector3(3482.2734f,3642.4922f,53.125f), 0, 65399, 7581, 0)
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string
|
||||||
|
}
|
||||||
|
"encode string2" in {
|
||||||
|
val msg = FireHintMessage(PlanetSideGUID(3592), Vector3(2910.789f,3744.875f,69.0625f), 0, 65231, 64736, 3, Some(Vector3(21.5f,-6.8125f,2.65625f)))
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -201,10 +201,10 @@ class ObjectCreateDetailedMessageTest extends Specification {
|
||||||
char.appearance.is_cloaking mustEqual false
|
char.appearance.is_cloaking mustEqual false
|
||||||
char.appearance.charging_pose mustEqual false
|
char.appearance.charging_pose mustEqual false
|
||||||
char.appearance.on_zipline mustEqual false
|
char.appearance.on_zipline mustEqual false
|
||||||
char.appearance.ribbons.upper mustEqual 0xFFFFFFFFL //none
|
char.appearance.ribbons.upper mustEqual MeritCommendation.None
|
||||||
char.appearance.ribbons.middle mustEqual 0xFFFFFFFFL //none
|
char.appearance.ribbons.middle mustEqual MeritCommendation.None
|
||||||
char.appearance.ribbons.lower mustEqual 0xFFFFFFFFL //none
|
char.appearance.ribbons.lower mustEqual MeritCommendation.None
|
||||||
char.appearance.ribbons.tos mustEqual 0xFFFFFFFFL //none
|
char.appearance.ribbons.tos mustEqual MeritCommendation.None
|
||||||
char.healthMax mustEqual 100
|
char.healthMax mustEqual 100
|
||||||
char.health mustEqual 100
|
char.health mustEqual 100
|
||||||
char.armor mustEqual 50 //standard exosuit value
|
char.armor mustEqual 50 //standard exosuit value
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ package game
|
||||||
import net.psforever.packet._
|
import net.psforever.packet._
|
||||||
import net.psforever.packet.game._
|
import net.psforever.packet.game._
|
||||||
import net.psforever.packet.game.objectcreate._
|
import net.psforever.packet.game.objectcreate._
|
||||||
import net.psforever.types.{CharacterGender, ExoSuitType, GrenadeState, PlanetSideEmpire, Vector3}
|
import net.psforever.types._
|
||||||
import org.specs2.mutable._
|
import org.specs2.mutable._
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
|
||||||
|
|
@ -684,10 +684,10 @@ class ObjectCreateMessageTest extends Specification {
|
||||||
pc.appearance.is_cloaking mustEqual false
|
pc.appearance.is_cloaking mustEqual false
|
||||||
pc.appearance.charging_pose mustEqual false
|
pc.appearance.charging_pose mustEqual false
|
||||||
pc.appearance.on_zipline mustEqual false
|
pc.appearance.on_zipline mustEqual false
|
||||||
pc.appearance.ribbons.upper mustEqual 276L
|
pc.appearance.ribbons.upper mustEqual MeritCommendation.Loser4
|
||||||
pc.appearance.ribbons.middle mustEqual 239L
|
pc.appearance.ribbons.middle mustEqual MeritCommendation.HeavyInfantry3
|
||||||
pc.appearance.ribbons.lower mustEqual 397L
|
pc.appearance.ribbons.lower mustEqual MeritCommendation.TankBuster6
|
||||||
pc.appearance.ribbons.tos mustEqual 360L
|
pc.appearance.ribbons.tos mustEqual MeritCommendation.SixYearNC
|
||||||
pc.health mustEqual 255
|
pc.health mustEqual 255
|
||||||
pc.armor mustEqual 253
|
pc.armor mustEqual 253
|
||||||
pc.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
|
pc.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
|
||||||
|
|
@ -780,10 +780,10 @@ class ObjectCreateMessageTest extends Specification {
|
||||||
pc.appearance.is_cloaking mustEqual false
|
pc.appearance.is_cloaking mustEqual false
|
||||||
pc.appearance.charging_pose mustEqual false
|
pc.appearance.charging_pose mustEqual false
|
||||||
pc.appearance.on_zipline mustEqual false
|
pc.appearance.on_zipline mustEqual false
|
||||||
pc.appearance.ribbons.upper mustEqual 244L
|
pc.appearance.ribbons.upper mustEqual MeritCommendation.Jacking
|
||||||
pc.appearance.ribbons.middle mustEqual 353L
|
pc.appearance.ribbons.middle mustEqual MeritCommendation.ScavengerTR6
|
||||||
pc.appearance.ribbons.lower mustEqual 33L
|
pc.appearance.ribbons.lower mustEqual MeritCommendation.AMSSupport4
|
||||||
pc.appearance.ribbons.tos mustEqual 361L
|
pc.appearance.ribbons.tos mustEqual MeritCommendation.SixYearTR
|
||||||
pc.health mustEqual 0
|
pc.health mustEqual 0
|
||||||
pc.armor mustEqual 0
|
pc.armor mustEqual 0
|
||||||
pc.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
|
pc.uniform_upgrade mustEqual UniformStyle.ThirdUpgrade
|
||||||
|
|
@ -1107,7 +1107,12 @@ class ObjectCreateMessageTest extends Specification {
|
||||||
false,
|
false,
|
||||||
GrenadeState.None,
|
GrenadeState.None,
|
||||||
false, false, false,
|
false, false, false,
|
||||||
RibbonBars(276L, 239L, 397L, 360L)
|
RibbonBars(
|
||||||
|
MeritCommendation.Loser4,
|
||||||
|
MeritCommendation.HeavyInfantry3,
|
||||||
|
MeritCommendation.TankBuster6,
|
||||||
|
MeritCommendation.SixYearNC
|
||||||
|
)
|
||||||
),
|
),
|
||||||
255, 253,
|
255, 253,
|
||||||
UniformStyle.ThirdUpgrade,
|
UniformStyle.ThirdUpgrade,
|
||||||
|
|
@ -1159,7 +1164,12 @@ class ObjectCreateMessageTest extends Specification {
|
||||||
false,
|
false,
|
||||||
GrenadeState.None,
|
GrenadeState.None,
|
||||||
false, false, false,
|
false, false, false,
|
||||||
RibbonBars(244L, 353L, 33L, 361L)
|
RibbonBars(
|
||||||
|
MeritCommendation.Jacking,
|
||||||
|
MeritCommendation.ScavengerTR6,
|
||||||
|
MeritCommendation.AMSSupport4,
|
||||||
|
MeritCommendation.SixYearTR
|
||||||
|
)
|
||||||
),
|
),
|
||||||
0, 0,
|
0, 0,
|
||||||
UniformStyle.ThirdUpgrade,
|
UniformStyle.ThirdUpgrade,
|
||||||
|
|
|
||||||
53
common/src/test/scala/game/OxygenStateMessageTest.scala
Normal file
53
common/src/test/scala/game/OxygenStateMessageTest.scala
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package game
|
||||||
|
|
||||||
|
import org.specs2.mutable._
|
||||||
|
import net.psforever.packet._
|
||||||
|
import net.psforever.packet.game._
|
||||||
|
import scodec.bits._
|
||||||
|
|
||||||
|
class OxygenStateMessageTest extends Specification {
|
||||||
|
val string_self = hex"78 4b00f430"
|
||||||
|
val string_vehicle = hex"78 4b00f4385037a180"
|
||||||
|
|
||||||
|
"decode (self)" in {
|
||||||
|
PacketCoding.DecodePacket(string_self).require match {
|
||||||
|
case OxygenStateMessage(guid, progress, active, veh_state) =>
|
||||||
|
guid mustEqual PlanetSideGUID(75)
|
||||||
|
progress mustEqual 50.0
|
||||||
|
active mustEqual true
|
||||||
|
veh_state.isDefined mustEqual false
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"decode (vehicle)" in {
|
||||||
|
PacketCoding.DecodePacket(string_vehicle).require match {
|
||||||
|
case OxygenStateMessage(guid, progress, active, veh_state) =>
|
||||||
|
guid mustEqual PlanetSideGUID(75)
|
||||||
|
progress mustEqual 50.0f
|
||||||
|
active mustEqual true
|
||||||
|
veh_state.isDefined mustEqual true
|
||||||
|
veh_state.get.vehicle_guid mustEqual PlanetSideGUID(1546)
|
||||||
|
veh_state.get.progress mustEqual 50.0f
|
||||||
|
veh_state.get.active mustEqual true
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode (self)" in {
|
||||||
|
val msg = OxygenStateMessage(PlanetSideGUID(75), 50.0f, true)
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string_self
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode (vehicle)" in {
|
||||||
|
val msg = OxygenStateMessage(PlanetSideGUID(75), 50.0f, true, WaterloggedVehicleState(PlanetSideGUID(1546), 50.0f, true))
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string_vehicle
|
||||||
|
}
|
||||||
|
}
|
||||||
44
common/src/test/scala/game/ProjectileStateMessageTest.scala
Normal file
44
common/src/test/scala/game/ProjectileStateMessageTest.scala
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package game
|
||||||
|
|
||||||
|
import org.specs2.mutable._
|
||||||
|
import net.psforever.packet._
|
||||||
|
import net.psforever.packet.game._
|
||||||
|
import net.psforever.types.Vector3
|
||||||
|
import scodec.bits._
|
||||||
|
|
||||||
|
class ProjectileStateMessageTest extends Specification {
|
||||||
|
val string = hex"3f 259d c5019 30e4a 9514 c52c9541 d9ba05c2 c5973941 00 f8 ec 020000"
|
||||||
|
|
||||||
|
"decode" in {
|
||||||
|
PacketCoding.DecodePacket(string).require match {
|
||||||
|
case ProjectileStateMessage(projectile, pos, vel, unk1, unk2, unk3, unk4, time_alive) =>
|
||||||
|
projectile mustEqual PlanetSideGUID(40229)
|
||||||
|
pos.x mustEqual 4611.539f
|
||||||
|
pos.y mustEqual 5576.375f
|
||||||
|
pos.z mustEqual 82.328125f
|
||||||
|
vel.x mustEqual 18.64686f
|
||||||
|
vel.y mustEqual -33.43247f
|
||||||
|
vel.z mustEqual 11.599553f
|
||||||
|
unk1 mustEqual 0
|
||||||
|
unk2 mustEqual 248
|
||||||
|
unk3 mustEqual 236
|
||||||
|
unk4 mustEqual false
|
||||||
|
time_alive mustEqual 4
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode" in {
|
||||||
|
val msg = ProjectileStateMessage(
|
||||||
|
PlanetSideGUID(40229),
|
||||||
|
Vector3(4611.539f, 5576.375f, 82.328125f),
|
||||||
|
Vector3(18.64686f, -33.43247f, 11.599553f),
|
||||||
|
0, 248, 236, false, 4
|
||||||
|
)
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string
|
||||||
|
}
|
||||||
|
}
|
||||||
116
common/src/test/scala/game/TargetingImplantRequestTest.scala
Normal file
116
common/src/test/scala/game/TargetingImplantRequestTest.scala
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package game
|
||||||
|
|
||||||
|
import org.specs2.mutable._
|
||||||
|
import net.psforever.packet._
|
||||||
|
import net.psforever.packet.game._
|
||||||
|
import scodec.bits._
|
||||||
|
|
||||||
|
class TargetingImplantRequestTest extends Specification {
|
||||||
|
val string_single = hex"b5 061016"
|
||||||
|
val string_long = hex"b5 41edeb12d4409f0144053f8010541ba91d03df376831b1e26000611041e1107c0209c0"//0510085013d9ffb6720d5b132900003770?
|
||||||
|
|
||||||
|
"decode (single)" in {
|
||||||
|
PacketCoding.DecodePacket(string_single).require match {
|
||||||
|
case TargetingImplantRequest(target_list) =>
|
||||||
|
target_list.length mustEqual 1
|
||||||
|
//0
|
||||||
|
target_list.head.target_guid mustEqual PlanetSideGUID(1412)
|
||||||
|
target_list.head.unk mustEqual true
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"decode (long)" in {
|
||||||
|
PacketCoding.DecodePacket(string_long).require match {
|
||||||
|
case TargetingImplantRequest(target_list) =>
|
||||||
|
target_list.length mustEqual 16
|
||||||
|
//0
|
||||||
|
target_list.head.target_guid mustEqual PlanetSideGUID(31355)
|
||||||
|
target_list.head.unk mustEqual true
|
||||||
|
//1
|
||||||
|
target_list(1).target_guid mustEqual PlanetSideGUID(27273)
|
||||||
|
target_list(1).unk mustEqual false
|
||||||
|
//2
|
||||||
|
target_list(2).target_guid mustEqual PlanetSideGUID(40768)
|
||||||
|
target_list(2).unk mustEqual false
|
||||||
|
//3
|
||||||
|
target_list(3).target_guid mustEqual PlanetSideGUID(34818)
|
||||||
|
target_list(3).unk mustEqual false
|
||||||
|
//4
|
||||||
|
target_list(4).target_guid mustEqual PlanetSideGUID(65044)
|
||||||
|
target_list(4).unk mustEqual false
|
||||||
|
//5
|
||||||
|
target_list(5).target_guid mustEqual PlanetSideGUID(33280)
|
||||||
|
target_list(5).unk mustEqual true
|
||||||
|
//6
|
||||||
|
target_list(6).target_guid mustEqual PlanetSideGUID(47681)
|
||||||
|
target_list(6).unk mustEqual true
|
||||||
|
//7
|
||||||
|
target_list(7).target_guid mustEqual PlanetSideGUID(40995)
|
||||||
|
target_list(7).unk mustEqual false
|
||||||
|
//8
|
||||||
|
target_list(8).target_guid mustEqual PlanetSideGUID(52727)
|
||||||
|
target_list(8).unk mustEqual true
|
||||||
|
//9
|
||||||
|
target_list(9).target_guid mustEqual PlanetSideGUID(6324)
|
||||||
|
target_list(9).unk mustEqual true
|
||||||
|
//10
|
||||||
|
target_list(10).target_guid mustEqual PlanetSideGUID(58033)
|
||||||
|
target_list(10).unk mustEqual false
|
||||||
|
//11
|
||||||
|
target_list(11).target_guid mustEqual PlanetSideGUID(192)
|
||||||
|
target_list(11).unk mustEqual true
|
||||||
|
//12
|
||||||
|
target_list(12).target_guid mustEqual PlanetSideGUID(16772)
|
||||||
|
target_list(12).unk mustEqual false
|
||||||
|
//13
|
||||||
|
target_list(13).target_guid mustEqual PlanetSideGUID(2063)
|
||||||
|
target_list(13).unk mustEqual true
|
||||||
|
//14
|
||||||
|
target_list(14).target_guid mustEqual PlanetSideGUID(49159)
|
||||||
|
target_list(14).unk mustEqual false
|
||||||
|
//15
|
||||||
|
target_list(15).target_guid mustEqual PlanetSideGUID(14401)
|
||||||
|
target_list(15).unk mustEqual false
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode (single)" in {
|
||||||
|
val msg = TargetingImplantRequest(
|
||||||
|
TargetRequest(PlanetSideGUID(1412), true) ::
|
||||||
|
Nil
|
||||||
|
)
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string_single
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode (long)" in {
|
||||||
|
val msg = TargetingImplantRequest(
|
||||||
|
TargetRequest(PlanetSideGUID(31355), true) ::
|
||||||
|
TargetRequest(PlanetSideGUID(27273), false) ::
|
||||||
|
TargetRequest(PlanetSideGUID(40768), false) ::
|
||||||
|
TargetRequest(PlanetSideGUID(34818), false) ::
|
||||||
|
TargetRequest(PlanetSideGUID(65044), false) ::
|
||||||
|
TargetRequest(PlanetSideGUID(33280), true) ::
|
||||||
|
TargetRequest(PlanetSideGUID(47681), true) ::
|
||||||
|
TargetRequest(PlanetSideGUID(40995), false) ::
|
||||||
|
TargetRequest(PlanetSideGUID(52727), true) ::
|
||||||
|
TargetRequest(PlanetSideGUID(6324), true) ::
|
||||||
|
TargetRequest(PlanetSideGUID(58033), false) ::
|
||||||
|
TargetRequest(PlanetSideGUID(192), true) ::
|
||||||
|
TargetRequest(PlanetSideGUID(16772), false) ::
|
||||||
|
TargetRequest(PlanetSideGUID(2063), true) ::
|
||||||
|
TargetRequest(PlanetSideGUID(49159), false) ::
|
||||||
|
TargetRequest(PlanetSideGUID(14401), false) ::
|
||||||
|
Nil
|
||||||
|
)
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string_long
|
||||||
|
}
|
||||||
|
}
|
||||||
73
common/src/test/scala/game/TriggerEffectMessageTest.scala
Normal file
73
common/src/test/scala/game/TriggerEffectMessageTest.scala
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package game
|
||||||
|
|
||||||
|
import org.specs2.mutable._
|
||||||
|
import net.psforever.packet._
|
||||||
|
import net.psforever.packet.game._
|
||||||
|
import net.psforever.types.Vector3
|
||||||
|
import scodec.bits._
|
||||||
|
|
||||||
|
class TriggerEffectMessageTest extends Specification {
|
||||||
|
val string_motionalarmsensor = hex"51 970B 82 6F6E FA00C00000"
|
||||||
|
val string_boomer = hex"51 0000 93 737061776E5F6F626A6563745F656666656374 417BB2CB3B4F8E00000000"
|
||||||
|
|
||||||
|
"decode (motion alarm sensor)" in {
|
||||||
|
PacketCoding.DecodePacket(string_motionalarmsensor).require match {
|
||||||
|
case TriggerEffectMessage(guid, effect, unk, location) =>
|
||||||
|
guid mustEqual PlanetSideGUID(2967)
|
||||||
|
effect mustEqual "on"
|
||||||
|
unk.isDefined mustEqual true
|
||||||
|
unk.get.unk1 mustEqual true
|
||||||
|
unk.get.unk2 mustEqual 1000L
|
||||||
|
location.isDefined mustEqual false
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"decode (boomer)" in {
|
||||||
|
PacketCoding.DecodePacket(string_boomer).require match {
|
||||||
|
case TriggerEffectMessage(guid, effect, unk, location) =>
|
||||||
|
guid mustEqual PlanetSideGUID(0)
|
||||||
|
effect mustEqual "spawn_object_effect"
|
||||||
|
unk.isDefined mustEqual false
|
||||||
|
location.isDefined mustEqual true
|
||||||
|
location.get.pos.x mustEqual 3567.0156f
|
||||||
|
location.get.pos.y mustEqual 3278.6953f
|
||||||
|
location.get.pos.z mustEqual 114.484375f
|
||||||
|
location.get.roll mustEqual 0
|
||||||
|
location.get.pitch mustEqual 0
|
||||||
|
location.get.yaw mustEqual 0
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode (motion alarm sensor)" in {
|
||||||
|
val msg = TriggerEffectMessage(
|
||||||
|
PlanetSideGUID(2967),
|
||||||
|
"on",
|
||||||
|
Some(TriggeredEffect(true, 1000L)),
|
||||||
|
None
|
||||||
|
)
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string_motionalarmsensor
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode (boomer)" in {
|
||||||
|
val msg = TriggerEffectMessage(
|
||||||
|
PlanetSideGUID(0),
|
||||||
|
"spawn_object_effect",
|
||||||
|
None,
|
||||||
|
Some(TriggeredEffectLocation(
|
||||||
|
Vector3(3567.0156f, 3278.6953f, 114.484375f),
|
||||||
|
0, 0, 0
|
||||||
|
))
|
||||||
|
)
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string_boomer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
28
common/src/test/scala/game/UnuseItemMessageTest.scala
Normal file
28
common/src/test/scala/game/UnuseItemMessageTest.scala
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright (c) 2017 PSForever
|
||||||
|
package game
|
||||||
|
|
||||||
|
import org.specs2.mutable._
|
||||||
|
import net.psforever.packet._
|
||||||
|
import net.psforever.packet.game._
|
||||||
|
import scodec.bits._
|
||||||
|
|
||||||
|
class UnuseItemMessageTest extends Specification {
|
||||||
|
val string = hex"26 4B00 340D"
|
||||||
|
|
||||||
|
"decode" in {
|
||||||
|
PacketCoding.DecodePacket(string).require match {
|
||||||
|
case UnuseItemMessage(player, item) =>
|
||||||
|
player mustEqual PlanetSideGUID(75)
|
||||||
|
item mustEqual PlanetSideGUID(3380)
|
||||||
|
case _ =>
|
||||||
|
ko
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"encode" in {
|
||||||
|
val msg = UnuseItemMessage(PlanetSideGUID(75), PlanetSideGUID(3380))
|
||||||
|
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||||
|
|
||||||
|
pkt mustEqual string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -255,6 +255,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case msg @ ChildObjectStateMessage(object_guid : PlanetSideGUID, pitch : Int, yaw : Int) =>
|
case msg @ ChildObjectStateMessage(object_guid : PlanetSideGUID, pitch : Int, yaw : Int) =>
|
||||||
//log.info("ChildObjectState: " + msg)
|
//log.info("ChildObjectState: " + msg)
|
||||||
|
|
||||||
|
case msg @ ProjectileStateMessage(projectile_guid, shot_pos, shot_vector, unk1, unk2, unk3, unk4, time_alive) =>
|
||||||
|
//log.info("ProjectileState: " + msg)
|
||||||
|
|
||||||
case msg @ ChatMsg(messagetype, has_wide_contents, recipient, contents, note_contents) =>
|
case msg @ ChatMsg(messagetype, has_wide_contents, recipient, contents, note_contents) =>
|
||||||
// TODO: Prevents log spam, but should be handled correctly
|
// TODO: Prevents log spam, but should be handled correctly
|
||||||
if (messagetype != ChatMessageType.CMT_TOGGLE_GM) {
|
if (messagetype != ChatMessageType.CMT_TOGGLE_GM) {
|
||||||
|
|
@ -364,6 +367,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(object_guid, 16)))
|
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(object_guid, 16)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case msg @ UnuseItemMessage(player, item) =>
|
||||||
|
log.info("UnuseItem: " + msg)
|
||||||
|
|
||||||
case msg @ DeployObjectMessage(guid, unk1, pos, roll, pitch, yaw, unk2) =>
|
case msg @ DeployObjectMessage(guid, unk1, pos, roll, pitch, yaw, unk2) =>
|
||||||
log.info("DeployObject: " + msg)
|
log.info("DeployObject: " + msg)
|
||||||
|
|
||||||
|
|
@ -440,6 +446,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case msg @ WeaponDryFireMessage(weapon) =>
|
case msg @ WeaponDryFireMessage(weapon) =>
|
||||||
log.info("WeaponDryFireMessage: "+msg)
|
log.info("WeaponDryFireMessage: "+msg)
|
||||||
|
|
||||||
|
case msg @ TargetingImplantRequest(list) =>
|
||||||
|
log.info("TargetingImplantRequest: "+msg)
|
||||||
|
|
||||||
case default => log.error(s"Unhandled GamePacket ${pkt}")
|
case default => log.error(s"Unhandled GamePacket ${pkt}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue