diff --git a/.codecov.yml b/.codecov.yml
index 75bed4f2..959d9a66 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -25,6 +25,7 @@ ignore:
- "common/src/main/scala/net/psforever/types/Angular.scala"
- "common/src/main/scala/net/psforever/types/CertificationType.scala"
- "common/src/main/scala/net/psforever/types/ChatMessageType.scala"
+ - "common/src/main/scala/net/psforever/types/DriveState.scala"
- "common/src/main/scala/net/psforever/types/EmoteType.scala"
- "common/src/main/scala/net/psforever/types/ExoSuitType.scala"
- "common/src/main/scala/net/psforever/types/GrenadeState.scala"
diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index edc10560..e16159b5 100644
--- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -2347,8 +2347,14 @@ object GlobalDefinitions {
ams.Seats(0).ArmorRestriction = SeatArmorRestriction.NoReinforcedOrMax
ams.MountPoints += 1 -> 0
ams.MountPoints += 2 -> 0
+<<<<<<< 27d86af015d5a835f7d594aed9ccdd1de4048c53
ams.Utilities += 3 -> UtilityType.order_terminala
ams.Utilities += 4 -> UtilityType.order_terminalb
+=======
+ ams.Deployment = true
+ ams.DeployTime = 2000
+ ams.UndeployTime = 2000
+>>>>>>> Deployment:
ams.Packet = utilityConverter
val variantConverter = new VariantVehicleConverter
@@ -2356,6 +2362,9 @@ object GlobalDefinitions {
router.MountPoints += 1 -> 0
router.TrunkSize = InventoryTile.Tile1511
router.TrunkOffset = 30
+ router.Deployment = true
+ router.DeployTime = 2000
+ router.UndeployTime = 2000
router.Packet = variantConverter
switchblade.Seats += 0 -> new SeatDefinition()
@@ -2365,6 +2374,9 @@ object GlobalDefinitions {
switchblade.MountPoints += 2 -> 0
switchblade.TrunkSize = InventoryTile.Tile1511
switchblade.TrunkOffset = 30
+ switchblade.Deployment = true
+ switchblade.DeployTime = 2000
+ switchblade.UndeployTime = 2000
switchblade.Packet = variantConverter
flail.Seats += 0 -> new SeatDefinition()
@@ -2373,6 +2385,9 @@ object GlobalDefinitions {
flail.MountPoints += 1 -> 0
flail.TrunkSize = InventoryTile.Tile1511
flail.TrunkOffset = 30
+ flail.Deployment = true
+ flail.DeployTime = 2000
+ flail.UndeployTime = 2000
flail.Packet = variantConverter
mosquito.Seats += 0 -> new SeatDefinition()
diff --git a/common/src/main/scala/net/psforever/objects/Vehicle.scala b/common/src/main/scala/net/psforever/objects/Vehicle.scala
index a5ce3bbd..b15590e2 100644
--- a/common/src/main/scala/net/psforever/objects/Vehicle.scala
+++ b/common/src/main/scala/net/psforever/objects/Vehicle.scala
@@ -7,9 +7,9 @@ import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem,
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.affinity.FactionAffinity
+import net.psforever.objects.serverobject.deploy.Deployment
import net.psforever.objects.vehicles.{AccessPermissionGroup, Seat, Utility, VehicleLockState}
import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.packet.game.objectcreate.DriveState
import net.psforever.types.PlanetSideEmpire
import scala.annotation.tailrec
@@ -22,6 +22,7 @@ import scala.annotation.tailrec
* Following that are the mounted weapons and other utilities.
* Trunk space starts being indexed afterwards.
*
+<<<<<<< 27d86af015d5a835f7d594aed9ccdd1de4048c53
* To keep it simple, infantry seating, mounted weapons, and utilities are stored separately.
*
* Vehicles maintain a `Map` of `Utility` objects in given index positions.
@@ -32,18 +33,22 @@ import scala.annotation.tailrec
* The value of the negative index does not have a specific meaning.
* @see `Vehicle.EquipmentUtilities`
* @param vehicleDef the vehicle's definition entry';
+=======
+ * To keep it simple, infantry seating, mounted weapons, and utilities are stored separately.
+ * @param vehicleDef the vehicle's definition entry;
+>>>>>>> Deployment:
* stores and unloads pertinent information about the `Vehicle`'s configuration;
* used in the initialization process (`loadVehicleDefinition`)
*/
class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServerObject
with FactionAffinity
with Mountable
+ with Deployment
with Container {
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.TR
private var owner : Option[PlanetSideGUID] = None
private var health : Int = 1
private var shields : Int = 0
- private var deployed : DriveState.Value = DriveState.Mobile
private var decal : Int = 0
private var trunkAccess : Option[PlanetSideGUID] = None
private var jammered : Boolean = false
@@ -125,17 +130,6 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
Definition.MaxShields
}
- def Drive : DriveState.Value = {
- this.deployed
- }
-
- def Drive_=(deploy : DriveState.Value) : DriveState.Value = {
- if(Definition.Deployment) {
- this.deployed = deploy
- }
- Drive
- }
-
def Decal : Int = {
this.decal
}
@@ -353,6 +347,10 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends PlanetSideServ
}
}
+ override def DeployTime = Definition.DeployTime
+
+ override def UndeployTime = Definition.UndeployTime
+
def Inventory : GridInventory = trunk
def Find(obj : Equipment) : Option[Int] = Find(obj.GUID)
diff --git a/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
index 693a1164..09d8136d 100644
--- a/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
@@ -21,7 +21,13 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
/* key - seat index (where this weapon attaches during object construction), value - the weapon on an EquipmentSlot */
private val weapons : mutable.HashMap[Int, ToolDefinition] = mutable.HashMap[Int, ToolDefinition]()
private var deployment : Boolean = false
+<<<<<<< 27d86af015d5a835f7d594aed9ccdd1de4048c53
private val utilities : mutable.HashMap[Int, UtilityType.Value] = mutable.HashMap()
+=======
+ private var deploymentTime_Deploy : Int = 0 //ms
+ private var deploymentTime_Undeploy : Int = 0 //ms
+ private val utilities : mutable.ArrayBuffer[Int] = mutable.ArrayBuffer[Int]()
+>>>>>>> Deployment:
private var trunkSize : InventoryTile = InventoryTile.None
private var trunkOffset : Int = 0
private var canCloak : Boolean = false
@@ -70,7 +76,25 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
Deployment
}
+<<<<<<< 27d86af015d5a835f7d594aed9ccdd1de4048c53
def Utilities : mutable.HashMap[Int, UtilityType.Value] = utilities
+=======
+ def DeployTime : Int = deploymentTime_Deploy
+
+ def DeployTime_=(dtime : Int) : Int = {
+ deploymentTime_Deploy = dtime
+ DeployTime
+ }
+
+ def UndeployTime : Int = deploymentTime_Undeploy
+
+ def UndeployTime_=(dtime : Int) : Int = {
+ deploymentTime_Undeploy = dtime
+ UndeployTime
+ }
+
+ def Utilities : mutable.ArrayBuffer[Int] = utilities
+>>>>>>> Deployment:
def TrunkSize : InventoryTile = trunkSize
diff --git a/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala b/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala
index 137ad6c0..5a0ba630 100644
--- a/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala
+++ b/common/src/main/scala/net/psforever/objects/definition/converter/VehicleConverter.scala
@@ -24,7 +24,7 @@ class VehicleConverter extends ObjectCreateConverter[Vehicle]() {
0,
obj.Health / obj.MaxHealth * 255, //TODO not precise
false, false,
- obj.Drive,
+ obj.DeploymentState,
false,
false,
obj.Cloaked,
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/deploy/Deployment.scala b/common/src/main/scala/net/psforever/objects/serverobject/deploy/Deployment.scala
new file mode 100644
index 00000000..fe2bb7c8
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/serverobject/deploy/Deployment.scala
@@ -0,0 +1,106 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.serverobject.deploy
+
+import net.psforever.objects.serverobject.PlanetSideServerObject
+import net.psforever.types.DriveState
+
+/**
+ * A `trait` for the purposes of deploying a server object that supports the operation.
+ * As a mixin, it provides the local variable used to keep track of the deployment state
+ * and the logic to change the value of the deployment.
+ * Initially, the deployment state is `Mobile`.
+ */
+trait Deployment {
+ this : PlanetSideServerObject =>
+
+ private var deployState : DriveState.Value = DriveState.Mobile
+
+ def DeployTime : Int = 0 //ms
+
+ def UndeployTime : Int = 0 //ms
+
+ def DeploymentState : DriveState.Value = deployState
+
+ def DeploymentState_=(to_deploy_state : DriveState.Value) : DriveState.Value = {
+ deployState = to_deploy_state
+ DeploymentState
+ }
+}
+
+object Deployment {
+ /**
+ * A shorthand `type` for a valid object of `Deployment`.
+ */
+ type DeploymentObject = PlanetSideServerObject with Deployment
+
+ /**
+ * A message for instigating a change in deployment state.
+ * @param state the new deployment state
+ */
+ final case class TryDeploymentChange(state : DriveState.Value)
+ /**
+ * A message for instigating a change to a deploy state.
+ * @param state the new deploy state
+ */
+ final case class TryDeploy(state : DriveState.Value)
+ /**
+ * A message for instigating a change to an undeploy state.
+ * @param state the new undeploy state
+ */
+ final case class TryUndeploy(state : DriveState.Value)
+ /**
+ * A response message to report successful deploy change.
+ * @param obj the object being deployed
+ * @param state the new deploy state
+ */
+ final case class CanDeploy(obj : DeploymentObject, state : DriveState.Value)
+ /**
+ * A response message to report successful undeploy change.
+ * @param obj the object being undeployed
+ * @param state the new undeploy state
+ */
+ final case class CanUndeploy(obj : DeploymentObject, state : DriveState.Value)
+ /**
+ * A response message to report an unsuccessful deployment change.
+ * @param obj the object being changed
+ * @param to_state the attempted deployment state
+ * @param reason a string explaining why the state can not or will not change
+ */
+ final case class CanNotChangeDeployment(obj : DeploymentObject, to_state : DriveState.Value, reason : String)
+
+ /**
+ * Given a starting deployment state, provide the next deployment state in a sequence.
+ *
+ * Two sequences are defined.
+ * The more elaborate sequence proceeds from `Mobile --> Deploying --> Deployed --> Undeploying --> Mobile`.
+ * This is the standard in-game deploy cycle.
+ * The sequence void of complexity is `State7 --> State7`.
+ * `State7` is an odd condition possessed mainly by vehicles that do not deploy.
+ * @param from_state the original deployment state
+ * @return the deployment state that is being transitioned
+ */
+ def NextState(from_state : DriveState.Value) : DriveState.Value = {
+ from_state match {
+ case DriveState.Mobile => DriveState.Deploying
+ case DriveState.Deploying => DriveState.Deployed
+ case DriveState.Deployed => DriveState.Undeploying
+ case DriveState.Undeploying => DriveState.Mobile
+ case DriveState.State7 => DriveState.State7
+ }
+ }
+
+ /**
+ * Is this `state` considered one of "deploy?"
+ * @param state the state to check
+ * @return yes, if it is a valid state; otherwise, false
+ */
+ def CheckForDeployState(state : DriveState.Value) : Boolean =
+ state == DriveState.Deploying || state == DriveState.Deployed
+ /**
+ * Is this `state` considered one of "undeploy?"
+ * @param state the state to check
+ * @return yes, if it is a valid state; otherwise, false
+ */
+ def CheckForUndeployState(state : DriveState.Value) : Boolean =
+ state == DriveState.Undeploying || state == DriveState.Mobile || state == DriveState.State7
+}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/deploy/DeploymentBehavior.scala b/common/src/main/scala/net/psforever/objects/serverobject/deploy/DeploymentBehavior.scala
new file mode 100644
index 00000000..94c1702e
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/serverobject/deploy/DeploymentBehavior.scala
@@ -0,0 +1,58 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.serverobject.deploy
+
+import akka.actor.Actor
+
+/**
+ * The logic governing `Deployment` objects that use the following messages:
+ * `TryDeploymentChange`,
+ * `TryDeploy`,
+ * and `TryUndeploy`.
+ * This is a mix-in trait for combining with existing `Receive` logic.
+ * @see `Deployment`
+ * @see `DriveState`
+ */
+trait DeploymentBehavior {
+ this : Actor =>
+
+ def DeploymentObject : Deployment.DeploymentObject
+
+ val deployBehavior : Receive = {
+ case Deployment.TryDeploymentChange(state) =>
+ val obj = DeploymentObject
+ if(Deployment.NextState(obj.DeploymentState) == state
+ && (obj.DeploymentState = state) == state) {
+ if(Deployment.CheckForDeployState(state)) {
+ sender ! Deployment.CanDeploy(obj, state)
+ }
+ else { //may need to check in future
+ sender ! Deployment.CanUndeploy(obj, state)
+ }
+ }
+ else {
+ sender ! Deployment.CanNotChangeDeployment(obj, state, "incorrect transition state")
+ }
+
+ case Deployment.TryDeploy(state) =>
+ val obj = DeploymentObject
+ if(Deployment.CheckForDeployState(state)
+ && Deployment.NextState(obj.DeploymentState) == state
+ && (obj.DeploymentState = state) == state) {
+ sender ! Deployment.CanDeploy(obj, state)
+ }
+ else {
+ sender ! Deployment.CanNotChangeDeployment(obj, state, "incorrect deploy transition state")
+ }
+
+ case Deployment.TryUndeploy(state) =>
+ val obj = DeploymentObject
+ if(Deployment.CheckForUndeployState(state)
+ && Deployment.NextState(obj.DeploymentState) == state
+ && (obj.DeploymentState = state) == state) {
+ sender ! Deployment.CanUndeploy(obj, state)
+ }
+ else {
+ sender ! Deployment.CanNotChangeDeployment(obj, state, "incorrect undeploy transition state")
+ }
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
index ced4442c..cffb9030 100644
--- a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
+++ b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
@@ -4,7 +4,12 @@ package net.psforever.objects.vehicles
import akka.actor.Actor
import net.psforever.objects.Vehicle
import net.psforever.objects.serverobject.mount.MountableBehavior
+<<<<<<< 27d86af015d5a835f7d594aed9ccdd1de4048c53
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
+=======
+import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
+import net.psforever.objects.serverobject.deploy.DeploymentBehavior
+>>>>>>> Deployment:
/**
* An `Actor` that handles messages being dispatched to a specific `Vehicle`.
@@ -15,18 +20,26 @@ import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffi
*/
class VehicleControl(vehicle : Vehicle) extends Actor
with FactionAffinityBehavior.Check
+ with DeploymentBehavior
with MountableBehavior.Mount
with MountableBehavior.Dismount {
+<<<<<<< 27d86af015d5a835f7d594aed9ccdd1de4048c53
//make control actors belonging to utilities when making control actor belonging to vehicle
vehicle.Utilities.foreach({case (_, util) => util.Setup })
def MountableObject = vehicle //do not add type!
+=======
+ def MountableObject = vehicle
+>>>>>>> Deployment:
- def FactionObject : FactionAffinity = vehicle
+ def FactionObject = vehicle
+
+ def DeploymentObject = vehicle
def receive : Receive = Enabled
def Enabled : Receive = checkBehavior
+ .orElse(deployBehavior)
.orElse(mountBehavior)
.orElse(dismountBehavior)
.orElse {
diff --git a/common/src/main/scala/net/psforever/packet/game/DeployRequestMessage.scala b/common/src/main/scala/net/psforever/packet/game/DeployRequestMessage.scala
index 008f96f7..0a38816a 100644
--- a/common/src/main/scala/net/psforever/packet/game/DeployRequestMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/DeployRequestMessage.scala
@@ -2,7 +2,7 @@
package net.psforever.packet.game
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
-import net.psforever.types.Vector3
+import net.psforever.types.{DriveState, Vector3}
import scodec.Codec
import scodec.codecs._
@@ -22,8 +22,7 @@ import scodec.codecs._
* This packet has nothing to do with ACE deployables.
* @param player_guid the player requesting the deployment
* @param vehicle_guid the vehicle to be deployed
- * @param unk1 na;
- * usually 2
+ * @param deploy_state either requests for a specific deployment state or assignment of the requested state
* @param unk2 na;
* usually 0
* @param unk3 na
@@ -31,7 +30,7 @@ import scodec.codecs._
*/
final case class DeployRequestMessage(player_guid : PlanetSideGUID,
vehicle_guid : PlanetSideGUID,
- unk1 : Int,
+ deploy_state : DriveState.Value,
unk2 : Int,
unk3 : Boolean,
pos : Vector3)
@@ -44,8 +43,8 @@ final case class DeployRequestMessage(player_guid : PlanetSideGUID,
object DeployRequestMessage extends Marshallable[DeployRequestMessage] {
implicit val codec : Codec[DeployRequestMessage] = (
("player_guid" | PlanetSideGUID.codec) ::
- ("deploy_guid" | PlanetSideGUID.codec) ::
- ("unk1" | uint(3)) ::
+ ("vehicle_guid" | PlanetSideGUID.codec) ::
+ ("deploy_state" | DriveState.codec) ::
("unk2" | uint(5)) ::
("unk3" | bool) ::
("pos" | Vector3.codec_pos)
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala
index 54a6fff4..a424d02b 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/Prefab.scala
@@ -2,7 +2,7 @@
package net.psforever.packet.game.objectcreate
import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.types.PlanetSideEmpire
+import net.psforever.types.{DriveState, PlanetSideEmpire}
/**
* A compilation of the common `*Data` objects that would be used for stock game objects.
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala b/common/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala
index 8292a5b2..34eac81d 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala
+++ b/common/src/main/scala/net/psforever/packet/game/objectcreate/VehicleData.scala
@@ -1,11 +1,13 @@
// Copyright (c) 2017 PSForever
package net.psforever.packet.game.objectcreate
-import net.psforever.packet.Marshallable
+import net.psforever.packet.{Marshallable, PacketHelpers}
import scodec.Attempt.{Failure, Successful}
import scodec.{Attempt, Codec, Err}
import scodec.codecs._
import shapeless.{::, HNil}
+import net.psforever.types.DriveState
+
/**
* An `Enumeration` of the various formats that known structures that the stream of bits for `VehicleData` can assume.
*/
@@ -141,6 +143,8 @@ object VehicleData extends Marshallable[VehicleData] {
new VehicleData(basic, unk1, health, unk2>0, false, driveState, unk3, unk5>0, false, Some(unk4), inventory)(VehicleFormat.Variant)
}
+ private val driveState8u = PacketHelpers.createEnumerationCodec(DriveState, uint8L)
+
/**
* `Codec` for the "utility" format.
*/
@@ -192,7 +196,7 @@ object VehicleData extends Marshallable[VehicleData] {
("health" | uint8L) ::
("unk2" | bool) :: //usually 0
("no_mount_points" | bool) ::
- ("driveState" | DriveState.codec) :: //used for deploy state
+ ("driveState" | driveState8u) :: //used for deploy state
("unk3" | bool) :: //unknown but generally false; can cause stream misalignment if set when unexpectedly
("unk4" | bool) ::
("cloak" | bool) :: //cloak as wraith, phantasm
diff --git a/common/src/main/scala/net/psforever/packet/game/objectcreate/DriveState.scala b/common/src/main/scala/net/psforever/types/DriveState.scala
similarity index 62%
rename from common/src/main/scala/net/psforever/packet/game/objectcreate/DriveState.scala
rename to common/src/main/scala/net/psforever/types/DriveState.scala
index fd685b73..e32c774a 100644
--- a/common/src/main/scala/net/psforever/packet/game/objectcreate/DriveState.scala
+++ b/common/src/main/scala/net/psforever/types/DriveState.scala
@@ -1,26 +1,24 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.packet.game.objectcreate
+package net.psforever.types
import net.psforever.packet.PacketHelpers
-import scodec.codecs._
+import scodec.codecs.uint
/**
* An `Enumeration` of the mobility states of vehicles.
*
- * In general, two important mobility states exist - `Mobile` and "deployed."
- * There are three stages of a formal deployment.
+ * In general, two important mobility states exist - `Mobile` and `Deployed`.
+ * There are stages of a formal deployment.
* For any deployment state other than the defined ones, the vehicle assumes it is in one of the transitional states.
* If the target vehicle has no deployment behavior, a non-`Mobile` value will not affect it.
*/
object DriveState extends Enumeration {
type Type = Value
- val Mobile = Value(0) //drivable
- val Undeployed = Value(1) //stationary
- val Unavailable = Value(2) //stationary, partial activation
- val Deployed = Value(3) //stationary, full activation
-
+ val Mobile = Value(0)
+ val Undeploying = Value(1)
+ val Deploying = Value(2)
+ val Deployed = Value(3)
val State7 = Value(7) //unknown; not encountered on a vehicle that can deploy; functions like Mobile
- implicit val codec = PacketHelpers.createEnumerationCodec(this, uint8L)
-}
\ No newline at end of file
+ implicit val codec = PacketHelpers.createEnumerationCodec(this, uint(3))
+}
diff --git a/common/src/test/scala/game/DeployRequestMessageTest.scala b/common/src/test/scala/game/DeployRequestMessageTest.scala
index 6d10e911..69139812 100644
--- a/common/src/test/scala/game/DeployRequestMessageTest.scala
+++ b/common/src/test/scala/game/DeployRequestMessageTest.scala
@@ -4,7 +4,7 @@ package game
import org.specs2.mutable._
import net.psforever.packet._
import net.psforever.packet.game._
-import net.psforever.types.Vector3
+import net.psforever.types.{DriveState, Vector3}
import scodec.bits._
class DeployRequestMessageTest extends Specification {
@@ -12,10 +12,10 @@ class DeployRequestMessageTest extends Specification {
"decode" in {
PacketCoding.DecodePacket(string).require match {
- case DeployRequestMessage(player_guid, vehicle_guid, unk1, unk2, unk3, pos) =>
+ case DeployRequestMessage(player_guid, vehicle_guid, deploy_state, unk2, unk3, pos) =>
player_guid mustEqual PlanetSideGUID(75)
vehicle_guid mustEqual PlanetSideGUID(380)
- unk1 mustEqual 2
+ deploy_state mustEqual DriveState.Deploying
unk2 mustEqual 0
unk3 mustEqual false
pos.x mustEqual 4060.1953f
@@ -30,7 +30,8 @@ class DeployRequestMessageTest extends Specification {
val msg = DeployRequestMessage(
PlanetSideGUID(75),
PlanetSideGUID(380),
- 2, 0, false,
+ DriveState.Deploying,
+ 0, false,
Vector3(4060.1953f, 2218.8281f, 155.32812f)
)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
diff --git a/common/src/test/scala/objects/DeploymentTest.scala b/common/src/test/scala/objects/DeploymentTest.scala
new file mode 100644
index 00000000..8321149e
--- /dev/null
+++ b/common/src/test/scala/objects/DeploymentTest.scala
@@ -0,0 +1,197 @@
+// Copyright (c) 2017 PSForever
+package objects
+
+import akka.actor.{Actor, ActorSystem, Props}
+import net.psforever.objects.serverobject.PlanetSideServerObject
+import net.psforever.objects.{GlobalDefinitions, Vehicle}
+import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior}
+import net.psforever.types.{DriveState, PlanetSideEmpire}
+import org.specs2.mutable.Specification
+
+import scala.concurrent.duration.Duration
+
+class DeploymentTest extends Specification {
+ "Deployment" should {
+ "construct" in {
+ val obj = new DeploymentTest.DeploymentObject()
+ obj.DeploymentState mustEqual DriveState.Mobile
+ obj.DeployTime mustEqual 0
+ obj.UndeployTime mustEqual 0
+ }
+
+ "change deployment state" in {
+ val obj = new DeploymentTest.DeploymentObject()
+ obj.DeploymentState mustEqual DriveState.Mobile
+
+ obj.DeploymentState = DriveState.Deployed
+ obj.DeploymentState mustEqual DriveState.Deployed
+ obj.DeploymentState = DriveState.Deploying
+ obj.DeploymentState mustEqual DriveState.Deploying
+ obj.DeploymentState = DriveState.Undeploying
+ obj.DeploymentState mustEqual DriveState.Undeploying
+ obj.DeploymentState = DriveState.State7
+ obj.DeploymentState mustEqual DriveState.State7
+ }
+
+ "have custom deployment time by object" in {
+ val ams = Vehicle(GlobalDefinitions.ams)
+ (ams.DeployTime == 0) mustEqual false //not default
+ (ams.UndeployTime == 0) mustEqual false //not default
+ }
+ }
+}
+
+class DeploymentBehavior1Test extends ActorTest {
+ "Deployment" should {
+ "construct" in {
+ val obj = DeploymentTest.SetUpAgent
+ assert(obj.Actor != Actor.noSender)
+ assert(obj.DeploymentState == DriveState.Mobile)
+ }
+ }
+}
+
+class DeploymentBehavior2Test extends ActorTest {
+ "Deployment" should {
+ "change following a deployment cycle using TryDeployChange" in {
+ val obj = DeploymentTest.SetUpAgent
+ assert(obj.DeploymentState == DriveState.Mobile)
+ //to Deploying
+ obj.Actor ! Deployment.TryDeploymentChange(DriveState.Deploying)
+ val reply1 = receiveOne(Duration.create(100, "ms"))
+ assert(reply1.isInstanceOf[Deployment.CanDeploy])
+ assert(reply1.asInstanceOf[Deployment.CanDeploy].obj == obj)
+ assert(reply1.asInstanceOf[Deployment.CanDeploy].state == DriveState.Deploying)
+ //to Deployed
+ obj.Actor ! Deployment.TryDeploymentChange(DriveState.Deployed)
+ val reply2 = receiveOne(Duration.create(100, "ms"))
+ assert(reply2.isInstanceOf[Deployment.CanDeploy])
+ assert(reply2.asInstanceOf[Deployment.CanDeploy].obj == obj)
+ assert(reply2.asInstanceOf[Deployment.CanDeploy].state == DriveState.Deployed)
+ //to Deployed
+ obj.Actor ! Deployment.TryDeploymentChange(DriveState.Undeploying)
+ val reply3 = receiveOne(Duration.create(100, "ms"))
+ assert(reply3.isInstanceOf[Deployment.CanUndeploy])
+ assert(reply3.asInstanceOf[Deployment.CanUndeploy].obj == obj)
+ assert(reply3.asInstanceOf[Deployment.CanUndeploy].state == DriveState.Undeploying)
+ //to Deployed
+ obj.Actor ! Deployment.TryDeploymentChange(DriveState.Mobile)
+ val reply4 = receiveOne(Duration.create(100, "ms"))
+ assert(reply4.isInstanceOf[Deployment.CanUndeploy])
+ assert(reply4.asInstanceOf[Deployment.CanUndeploy].obj == obj)
+ assert(reply4.asInstanceOf[Deployment.CanUndeploy].state == DriveState.Mobile)
+ }
+ }
+}
+
+class DeploymentBehavior3Test extends ActorTest {
+ "Deployment" should {
+ "change following a deployment cycle using TryDeploy and TryUndeploy" in {
+ val obj = DeploymentTest.SetUpAgent
+ assert(obj.DeploymentState == DriveState.Mobile)
+ //to Deploying
+ obj.Actor ! Deployment.TryDeploy(DriveState.Deploying)
+ val reply1 = receiveOne(Duration.create(100, "ms"))
+ assert(reply1.isInstanceOf[Deployment.CanDeploy])
+ assert(reply1.asInstanceOf[Deployment.CanDeploy].obj == obj)
+ assert(reply1.asInstanceOf[Deployment.CanDeploy].state == DriveState.Deploying)
+ //to Deployed
+ obj.Actor ! Deployment.TryDeploy(DriveState.Deployed)
+ val reply2 = receiveOne(Duration.create(100, "ms"))
+ assert(reply2.isInstanceOf[Deployment.CanDeploy])
+ assert(reply2.asInstanceOf[Deployment.CanDeploy].obj == obj)
+ assert(reply2.asInstanceOf[Deployment.CanDeploy].state == DriveState.Deployed)
+ //to Deployed
+ obj.Actor ! Deployment.TryUndeploy(DriveState.Undeploying)
+ val reply3 = receiveOne(Duration.create(100, "ms"))
+ assert(reply3.isInstanceOf[Deployment.CanUndeploy])
+ assert(reply3.asInstanceOf[Deployment.CanUndeploy].obj == obj)
+ assert(reply3.asInstanceOf[Deployment.CanUndeploy].state == DriveState.Undeploying)
+ //to Deployed
+ obj.Actor ! Deployment.TryUndeploy(DriveState.Mobile)
+ val reply4 = receiveOne(Duration.create(100, "ms"))
+ assert(reply4.isInstanceOf[Deployment.CanUndeploy])
+ assert(reply4.asInstanceOf[Deployment.CanUndeploy].obj == obj)
+ assert(reply4.asInstanceOf[Deployment.CanUndeploy].state == DriveState.Mobile)
+ }
+ }
+}
+
+class DeploymentBehavior4Test extends ActorTest {
+ "Deployment" should {
+ "not deploy to an out of order state" in {
+ val obj = DeploymentTest.SetUpAgent
+ assert(obj.DeploymentState == DriveState.Mobile)
+
+ obj.Actor ! Deployment.TryDeploymentChange(DriveState.Deployed)
+ val reply1 = receiveOne(Duration.create(100, "ms"))
+ assert(reply1.isInstanceOf[Deployment.CanNotChangeDeployment])
+ assert(reply1.asInstanceOf[Deployment.CanNotChangeDeployment].obj == obj)
+ assert(reply1.asInstanceOf[Deployment.CanNotChangeDeployment].to_state == DriveState.Deployed)
+ assert(obj.DeploymentState == DriveState.Mobile)
+
+ obj.Actor ! Deployment.TryDeploy(DriveState.Deployed)
+ val reply2 = receiveOne(Duration.create(100, "ms"))
+ assert(reply2.isInstanceOf[Deployment.CanNotChangeDeployment])
+ assert(reply2.asInstanceOf[Deployment.CanNotChangeDeployment].obj == obj)
+ assert(reply2.asInstanceOf[Deployment.CanNotChangeDeployment].to_state == DriveState.Deployed)
+ assert(obj.DeploymentState == DriveState.Mobile)
+ }
+ }
+}
+
+class DeploymentBehavior5Test extends ActorTest {
+ "Deployment" should {
+ "not deploy to an undeploy state" in {
+ val obj = DeploymentTest.SetUpAgent
+ assert(obj.DeploymentState == DriveState.Mobile)
+ obj.Actor ! Deployment.TryDeploymentChange(DriveState.Deploying)
+ receiveOne(Duration.create(100, "ms")) //consume
+ obj.Actor ! Deployment.TryDeploymentChange(DriveState.Deployed)
+ receiveOne(Duration.create(100, "ms")) //consume
+ assert(obj.DeploymentState == DriveState.Deployed)
+
+ obj.Actor ! Deployment.TryDeploy(DriveState.Undeploying)
+ val reply = receiveOne(Duration.create(100, "ms"))
+ assert(reply.isInstanceOf[Deployment.CanNotChangeDeployment])
+ assert(reply.asInstanceOf[Deployment.CanNotChangeDeployment].obj == obj)
+ assert(reply.asInstanceOf[Deployment.CanNotChangeDeployment].to_state == DriveState.Undeploying)
+ assert(obj.DeploymentState == DriveState.Deployed)
+ }
+ }
+}
+
+class DeploymentBehavior6Test extends ActorTest {
+ "Deployment" should {
+ "not undeploy to a deploy state" in {
+ val obj = DeploymentTest.SetUpAgent
+ assert(obj.DeploymentState == DriveState.Mobile)
+
+ obj.Actor ! Deployment.TryUndeploy(DriveState.Deploying)
+ val reply = receiveOne(Duration.create(100, "ms"))
+ assert(reply.isInstanceOf[Deployment.CanNotChangeDeployment])
+ assert(reply.asInstanceOf[Deployment.CanNotChangeDeployment].obj == obj)
+ assert(reply.asInstanceOf[Deployment.CanNotChangeDeployment].to_state == DriveState.Deploying)
+ assert(obj.DeploymentState == DriveState.Mobile)
+ }
+ }
+}
+
+object DeploymentTest {
+ class DeploymentObject extends PlanetSideServerObject with Deployment {
+ def Faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
+ def Definition = null
+ }
+
+ private class DeploymentControl(obj : Deployment.DeploymentObject) extends Actor
+ with DeploymentBehavior {
+ override def DeploymentObject = obj
+ def receive = deployBehavior.orElse { case _ => }
+ }
+
+ def SetUpAgent(implicit system : ActorSystem) = {
+ val obj = new DeploymentObject()
+ obj.Actor = system.actorOf(Props(classOf[DeploymentControl], obj), "test")
+ obj
+ }
+}
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index d0e4826e..9cb0057c 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -16,6 +16,7 @@ import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.affinity.FactionAffinity
+import net.psforever.objects.serverobject.deploy.Deployment
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
@@ -330,6 +331,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, DismountVehicleMsg(guid, unk1, unk2)))
}
+ case VehicleResponse.DeployRequest(object_guid, state, unk1, unk2, pos) =>
+ if(player.GUID != guid) {
+ sendResponse(PacketCoding.CreateGamePacket(0, DeployRequestMessage(guid, object_guid, state, unk1, unk2, pos)))
+ }
+
case VehicleResponse.InventoryState(obj, parent_guid, start, con_data) =>
if(player.GUID != guid) {
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
@@ -399,6 +405,49 @@ class WorldSessionActor extends Actor with MDCContextAware {
case _ => ;
}
+ case Deployment.CanDeploy(obj, state) =>
+ val vehicle_guid = obj.GUID
+ if(state == DriveState.Deploying) {
+ log.info(s"DeployRequest: $obj transitioning to deploy state")
+ sendResponse(PacketCoding.CreateGamePacket(0, DeployRequestMessage(player.GUID, vehicle_guid, state, 0, false, obj.Position)))
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DeployRequest(player.GUID, vehicle_guid, state, 0, false, obj.Position))
+ import scala.concurrent.duration._
+ import scala.concurrent.ExecutionContext.Implicits.global
+ context.system.scheduler.scheduleOnce(obj.DeployTime milliseconds, obj.Actor, Deployment.TryDeploy(DriveState.Deployed))
+ }
+ else if(state == DriveState.Deployed) {
+ log.info(s"DeployRequest: $obj has been Deployed")
+ sendResponse(PacketCoding.CreateGamePacket(0, DeployRequestMessage(player.GUID, vehicle_guid, state, 0, false, obj.Position)))
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DeployRequest(player.GUID, vehicle_guid, state, 0, false, obj.Position))
+ //...
+ }
+ else {
+ CanNotChangeDeployment(obj, state, "incorrect deploy state")
+ }
+
+ case Deployment.CanUndeploy(obj, state) =>
+ val vehicle_guid = obj.GUID
+ if(state == DriveState.Undeploying) {
+ log.info(s"DeployRequest: $obj transitioning to undeploy state")
+ sendResponse(PacketCoding.CreateGamePacket(0, DeployRequestMessage(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero)))
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DeployRequest(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero))
+ import scala.concurrent.duration._
+ import scala.concurrent.ExecutionContext.Implicits.global
+ context.system.scheduler.scheduleOnce(obj.UndeployTime milliseconds, obj.Actor, Deployment.TryUndeploy(DriveState.Mobile))
+ }
+ else if(state == DriveState.Mobile) {
+ log.info(s"DeployRequest: $obj is Mobile")
+ sendResponse(PacketCoding.CreateGamePacket(0, DeployRequestMessage(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero)))
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DeployRequest(player.GUID, vehicle_guid, state, 0, false, Vector3.Zero))
+ //...
+ }
+ else {
+ CanNotChangeDeployment(obj, state, "incorrect undeploy state")
+ }
+
+ case Deployment.CanNotChangeDeployment(obj, state, reason) =>
+ CanNotChangeDeployment(obj, state, reason)
+
case Door.DoorMessage(tplayer, msg, order) =>
val door_guid = msg.object_guid
order match {
@@ -2096,24 +2145,20 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
- case msg @ DeployRequestMessage(player_guid, entity, unk1, unk2, unk3, pos) =>
- log.info("DeployRequest: " + msg)
- //LOCAL FUNCTIONALITY ONLY FOR THIS BRANCH
- val player_guid = player.GUID
- if(unk1 == 2) { // deploy AMS
- //sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(entity,49,1))) // TODO : ANT ? With increment when loading NTU ?
- sendResponse(PacketCoding.CreateGamePacket(0, DeployRequestMessage(player_guid, entity, unk1, unk2, unk3, Vector3(0f, 0f, 0f))))
- Thread.sleep(1000) // 2 seconds
- sendResponse(PacketCoding.CreateGamePacket(0, DeployRequestMessage(player_guid, entity, 3, unk2, unk3, Vector3(0f, 0f, 0f))))
- sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(entity, 10, 1)))
- sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(entity, 11, 1)))
- sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(entity, 12, 1)))
- sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(entity, 13, 1)))
+ case msg @ DeployRequestMessage(player_guid, vehicle_guid, deploy_state, unk2, unk3, pos) =>
+ log.info(s"DeployRequest: $msg")
+ if(player.VehicleOwned == Some(vehicle_guid) && player.VehicleOwned == player.VehicleSeated) {
+ continent.GUID(vehicle_guid) match {
+ case Some(obj : Vehicle) =>
+ obj.Actor ! Deployment.TryDeploymentChange(deploy_state)
+
+ case _ =>
+ log.error(s"DeployRequest: can not find $vehicle_guid in scope; removing ownership to mitigate confusion")
+ player.VehicleOwned = None
+ }
}
- else if(unk1 == 1) { // undeploy AMS
- sendResponse(PacketCoding.CreateGamePacket(0, DeployRequestMessage(player_guid, entity, unk1, unk2, unk3, Vector3(0f, 0f, 0f))))
- Thread.sleep(1000) // 2 seconds
- sendResponse(PacketCoding.CreateGamePacket(0, DeployRequestMessage(player_guid, entity, 0, unk2, unk3, Vector3(0f, 0f, 0f))))
+ else {
+ log.warn(s"DeployRequest: $player does not own the deploying $vehicle_guid object")
}
case msg @ AvatarGrenadeStateMessage(player_guid, state) =>
@@ -2964,6 +3009,25 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
+ /**
+ * Common reporting behavior when a `Deployment` object fails to properly transition between states.
+ * @param obj the game object that could not
+ * @param state the `DriveState` that could not be promoted
+ * @param reason a string explaining why the state can not or will not change
+ */
+ def CanNotChangeDeployment(obj : PlanetSideServerObject with Deployment, state : DriveState.Value, reason : String) : Unit = {
+ val mobileShift : String = if(obj.DeploymentState != DriveState.Mobile) {
+ obj.DeploymentState = DriveState.Mobile
+ sendResponse(PacketCoding.CreateGamePacket(0, DeployRequestMessage(player.GUID, obj.GUID, DriveState.Mobile, 0, false, Vector3.Zero)))
+ vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.DeployRequest(player.GUID, obj.GUID, DriveState.Mobile, 0, false, Vector3.Zero))
+ "; enforcing Mobile deployment state"
+ }
+ else {
+ ""
+ }
+ log.error(s"DeployRequest: $obj can not transition to $state - $reason$mobileShift")
+ }
+
def failWithError(error : String) = {
log.error(error)
sendResponse(PacketCoding.CreateControlPacket(ConnectionClose()))
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
index 5bf6a5d4..66afa834 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleAction.scala
@@ -6,13 +6,14 @@ import net.psforever.objects.equipment.Equipment
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.packet.game.objectcreate.ConstructorData
-import net.psforever.types.Vector3
+import net.psforever.types.{DriveState, Vector3}
object VehicleAction {
trait Action
final case class Awareness(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID) extends Action
final case class ChildObjectState(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Action
+ final case class DeployRequest(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Action
final case class DismountVehicle(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean) extends Action
final case class InventoryState(player_guid : PlanetSideGUID, obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Action
final case class KickPassenger(player_guid : PlanetSideGUID, unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Action
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
index 32c08412..a7bed3bc 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleResponse.scala
@@ -4,13 +4,14 @@ package services.vehicle
import net.psforever.objects.{PlanetSideGameObject, Vehicle}
import net.psforever.packet.game.PlanetSideGUID
import net.psforever.packet.game.objectcreate.ConstructorData
-import net.psforever.types.Vector3
+import net.psforever.types.{DriveState, Vector3}
object VehicleResponse {
trait Response
final case class Awareness(vehicle_guid : PlanetSideGUID) extends Response
final case class ChildObjectState(object_guid : PlanetSideGUID, pitch : Float, yaw : Float) extends Response
+ final case class DeployRequest(object_guid : PlanetSideGUID, state : DriveState.Value, unk1 : Int, unk2 : Boolean, pos : Vector3) extends Response
final case class DismountVehicle(unk1 : Int, unk2 : Boolean) extends Response
final case class InventoryState(obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Response
final case class KickPassenger(unk1 : Int, unk2 : Boolean, vehicle_guid : PlanetSideGUID) extends Response
diff --git a/pslogin/src/main/scala/services/vehicle/VehicleService.scala b/pslogin/src/main/scala/services/vehicle/VehicleService.scala
index 9fa88ee3..26a2412e 100644
--- a/pslogin/src/main/scala/services/vehicle/VehicleService.scala
+++ b/pslogin/src/main/scala/services/vehicle/VehicleService.scala
@@ -2,8 +2,6 @@
package services.vehicle
import akka.actor.{Actor, ActorRef, Props}
-import net.psforever.packet.game.PlanetSideGUID
-import net.psforever.packet.game.objectcreate.ConstructorData
import services.vehicle.support.{DeconstructionActor, DelayedDeconstructionActor, VehicleContextActor}
import services.{GenericEventBus, Service}
@@ -49,14 +47,18 @@ class VehicleService extends Actor {
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.ChildObjectState(object_guid, pitch, yaw))
)
- case VehicleAction.InventoryState(player_guid, obj, parent_guid, start, con_data) =>
+ case VehicleAction.DeployRequest(player_guid, object_guid, state, unk1, unk2, pos) =>
VehicleEvents.publish(
- VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.InventoryState(obj, parent_guid, start, con_data))
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.DeployRequest(object_guid, state, unk1, unk2, pos))
)
case VehicleAction.DismountVehicle(player_guid, unk1, unk2) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.DismountVehicle(unk1, unk2))
)
+ case VehicleAction.InventoryState(player_guid, obj, parent_guid, start, con_data) =>
+ VehicleEvents.publish(
+ VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.InventoryState(obj, parent_guid, start, con_data))
+ )
case VehicleAction.KickPassenger(player_guid, unk1, unk2, vehicle_guid) =>
VehicleEvents.publish(
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickPassenger(unk1, unk2, vehicle_guid))
diff --git a/pslogin/src/main/test/scala/ActorTest.scala b/pslogin/src/test/scala/ActorTest.scala
similarity index 100%
rename from pslogin/src/main/test/scala/ActorTest.scala
rename to pslogin/src/test/scala/ActorTest.scala
diff --git a/pslogin/src/main/test/scala/AvatarServiceTest.scala b/pslogin/src/test/scala/AvatarServiceTest.scala
similarity index 90%
rename from pslogin/src/main/test/scala/AvatarServiceTest.scala
rename to pslogin/src/test/scala/AvatarServiceTest.scala
index d4b01f89..56e02385 100644
--- a/pslogin/src/main/test/scala/AvatarServiceTest.scala
+++ b/pslogin/src/test/scala/AvatarServiceTest.scala
@@ -6,7 +6,7 @@ import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire, Vect
import services.Service
import services.avatar._
-class AvatarService0Test extends ActorTest {
+class AvatarService1Test extends ActorTest {
"AvatarService" should {
"construct" in {
system.actorOf(Props[AvatarService], "service")
@@ -15,7 +15,7 @@ class AvatarService0Test extends ActorTest {
}
}
-class AvatarService1_1Test extends ActorTest {
+class AvatarService2Test extends ActorTest {
"AvatarService" should {
"subscribe" in {
val service = system.actorOf(Props[AvatarService], "service")
@@ -25,9 +25,9 @@ class AvatarService1_1Test extends ActorTest {
}
}
-class AvatarService1_2Test extends ActorTest {
+class AvatarService3Test extends ActorTest {
"AvatarService" should {
- "subscribe" in {
+ "subscribe to a specific channel" in {
val service = system.actorOf(Props[AvatarService], "service")
service ! Service.Join("test")
service ! Service.Leave()
@@ -36,7 +36,7 @@ class AvatarService1_2Test extends ActorTest {
}
}
-class AvatarService1_3Test extends ActorTest {
+class AvatarService4Test extends ActorTest {
"AvatarService" should {
"subscribe" in {
val service = system.actorOf(Props[AvatarService], "service")
@@ -47,7 +47,7 @@ class AvatarService1_3Test extends ActorTest {
}
}
-class AvatarService2Test extends ActorTest {
+class AvatarService5Test extends ActorTest {
"AvatarService" should {
"pass an unhandled message" in {
val service = system.actorOf(Props[AvatarService], "service")
@@ -58,7 +58,7 @@ class AvatarService2Test extends ActorTest {
}
}
-class AvatarService3Test extends ActorTest {
+class ArmorChangedTest extends ActorTest {
"AvatarService" should {
"pass ArmorChanged" in {
val service = system.actorOf(Props[AvatarService], "service")
@@ -69,7 +69,7 @@ class AvatarService3Test extends ActorTest {
}
}
-class AvatarService4Test extends ActorTest {
+class ConcealPlayerTest extends ActorTest {
"AvatarService" should {
"pass ConcealPlayer" in {
val service = system.actorOf(Props[AvatarService], "service")
@@ -80,7 +80,7 @@ class AvatarService4Test extends ActorTest {
}
}
-class AvatarService5Test extends ActorTest {
+class EquipmentInHandTest extends ActorTest {
val tool = Tool(GlobalDefinitions.beamer)
"AvatarService" should {
@@ -93,21 +93,23 @@ class AvatarService5Test extends ActorTest {
}
}
-class AvatarService6Test extends ActorTest {
+class EquipmentOnGroundTest extends ActorTest {
val toolDef = GlobalDefinitions.beamer
val tool = Tool(toolDef)
+ tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(1)
+ val cdata = toolDef.Packet.ConstructorData(tool).get
"AvatarService" should {
"pass EquipmentOnGround" in {
val service = system.actorOf(Props[AvatarService], "service")
service ! Service.Join("test")
- service ! AvatarServiceMessage("test", AvatarAction.EquipmentOnGround(PlanetSideGUID(10), Vector3(300f, 200f, 100f), Vector3(450f, 300f, 150f), toolDef.ObjectId, PlanetSideGUID(11), toolDef.Packet.ConstructorData(tool).get))
- expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentOnGround(Vector3(300f, 200f, 100f), Vector3(450f, 300f, 150f), toolDef.ObjectId, PlanetSideGUID(11), toolDef.Packet.ConstructorData(tool).get)))
+ service ! AvatarServiceMessage("test", AvatarAction.EquipmentOnGround(PlanetSideGUID(10), Vector3(300f, 200f, 100f), Vector3(450f, 300f, 150f), toolDef.ObjectId, PlanetSideGUID(11), cdata))
+ expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentOnGround(Vector3(300f, 200f, 100f), Vector3(450f, 300f, 150f), toolDef.ObjectId, PlanetSideGUID(11), cdata)))
}
}
}
-class AvatarService7Test extends ActorTest {
+class LoadPlayerTest extends ActorTest {
val obj = Player("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1)
obj.GUID = PlanetSideGUID(10)
obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(11)
@@ -123,7 +125,7 @@ class AvatarService7Test extends ActorTest {
}
}
-class AvatarService8Test extends ActorTest {
+class ObjectDeleteTest extends ActorTest {
"AvatarService" should {
"pass ObjectDelete" in {
val service = system.actorOf(Props[AvatarService], "service")
@@ -137,7 +139,7 @@ class AvatarService8Test extends ActorTest {
}
}
-class AvatarService9Test extends ActorTest {
+class ObjectHeldTest extends ActorTest {
"AvatarService" should {
"pass ObjectHeld" in {
val service = system.actorOf(Props[AvatarService], "service")
@@ -148,7 +150,7 @@ class AvatarService9Test extends ActorTest {
}
}
-class AvatarServiceATest extends ActorTest {
+class PlanetsideAttributeTest extends ActorTest {
"AvatarService" should {
"pass PlanetsideAttribute" in {
val service = system.actorOf(Props[AvatarService], "service")
@@ -159,7 +161,7 @@ class AvatarServiceATest extends ActorTest {
}
}
-class AvatarServiceBTest extends ActorTest {
+class PlayerStateTest extends ActorTest {
val msg = PlayerStateMessageUpstream(PlanetSideGUID(75), Vector3(3694.1094f, 2735.4531f, 90.84375f), Some(Vector3(4.375f, 2.59375f, 0.0f)), 61.875f, 351.5625f, 0.0f, 136, 0, false, false, false, false, 112, 0)
"AvatarService" should {
@@ -172,7 +174,7 @@ class AvatarServiceBTest extends ActorTest {
}
}
-class AvatarServiceCTest extends ActorTest {
+class ReloadTest extends ActorTest {
"AvatarService" should {
"pass Reload" in {
val service = system.actorOf(Props[AvatarService], "service")
@@ -183,7 +185,7 @@ class AvatarServiceCTest extends ActorTest {
}
}
-class AvatarServiceDTest extends ActorTest {
+class ChangeAmmoTest extends ActorTest {
val ammoDef = GlobalDefinitions.energy_cell
val ammoBox = AmmoBox(ammoDef)
@@ -197,7 +199,7 @@ class AvatarServiceDTest extends ActorTest {
}
}
-class AvatarServiceETest extends ActorTest {
+class ChangeFireModeTest extends ActorTest {
val ammoDef = GlobalDefinitions.energy_cell
val ammoBox = AmmoBox(ammoDef)
@@ -211,7 +213,7 @@ class AvatarServiceETest extends ActorTest {
}
}
-class AvatarServiceF_1Test extends ActorTest {
+class ChangeFireStateStartTest extends ActorTest {
"AvatarService" should {
"pass ChangeFireState_Start" in {
val service = system.actorOf(Props[AvatarService], "service")
@@ -222,7 +224,7 @@ class AvatarServiceF_1Test extends ActorTest {
}
}
-class AvatarServiceF_2Test extends ActorTest {
+class ChangeFireStateStopTest extends ActorTest {
"AvatarService" should {
"pass ChangeFireState_Stop" in {
val service = system.actorOf(Props[AvatarService], "service")
@@ -233,7 +235,7 @@ class AvatarServiceF_2Test extends ActorTest {
}
}
-class AvatarService01Test extends ActorTest {
+class WeaponDryFireTest extends ActorTest {
"AvatarService" should {
"pass WeaponDryFire" in {
val service = system.actorOf(Props[AvatarService], "service")
diff --git a/pslogin/src/main/test/scala/PacketCodingActorTest.scala b/pslogin/src/test/scala/PacketCodingActorTest.scala
similarity index 100%
rename from pslogin/src/main/test/scala/PacketCodingActorTest.scala
rename to pslogin/src/test/scala/PacketCodingActorTest.scala
diff --git a/pslogin/src/test/scala/VehicleServiceTest.scala b/pslogin/src/test/scala/VehicleServiceTest.scala
new file mode 100644
index 00000000..8803db51
--- /dev/null
+++ b/pslogin/src/test/scala/VehicleServiceTest.scala
@@ -0,0 +1,208 @@
+// Copyright (c) 2017 PSForever
+import akka.actor.Props
+import net.psforever.objects._
+import net.psforever.packet.game.PlanetSideGUID
+import net.psforever.types._
+import services.Service
+import services.vehicle._
+
+class VehicleService1Test extends ActorTest {
+ "VehicleService" should {
+ "construct" in {
+ system.actorOf(Props[VehicleService], "service")
+ assert(true)
+ }
+ }
+}
+
+class VehicleService2Test extends ActorTest {
+ "VehicleService" should {
+ "subscribe" in {
+ val service = system.actorOf(Props[VehicleService], "service")
+ service ! Service.Join("test")
+ assert(true)
+ }
+ }
+}
+
+class VehicleService3Test extends ActorTest {
+ "VehicleService" should {
+ "subscribe to a specific channel" in {
+ val service = system.actorOf(Props[VehicleService], "service")
+ service ! Service.Join("test")
+ service ! Service.Leave()
+ assert(true)
+ }
+ }
+}
+
+class VehicleService4Test extends ActorTest {
+ "VehicleService" should {
+ "subscribe" in {
+ val service = system.actorOf(Props[VehicleService], "service")
+ service ! Service.Join("test")
+ service ! Service.LeaveAll()
+ assert(true)
+ }
+ }
+}
+
+class VehicleService5Test extends ActorTest {
+ "VehicleService" should {
+ "pass an unhandled message" in {
+ val service = system.actorOf(Props[VehicleService], "service")
+ service ! Service.Join("test")
+ service ! "hello"
+ expectNoMsg()
+ }
+ }
+}
+
+class AwarenessTest extends ActorTest {
+ "VehicleService" should {
+ "pass Awareness" in {
+ val service = system.actorOf(Props[VehicleService], "service")
+ service ! Service.Join("test")
+ service ! VehicleServiceMessage("test", VehicleAction.Awareness(PlanetSideGUID(10), PlanetSideGUID(11)))
+ expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.Awareness(PlanetSideGUID(11))))
+ }
+ }
+}
+
+class ChildObjectStateTest extends ActorTest {
+ "VehicleService" should {
+ "pass ChildObjectState" in {
+ val service = system.actorOf(Props[VehicleService], "service")
+ service ! Service.Join("test")
+ service ! VehicleServiceMessage("test", VehicleAction.ChildObjectState(PlanetSideGUID(10), PlanetSideGUID(11), 1.2f, 3.4f))
+ expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.ChildObjectState(PlanetSideGUID(11), 1.2f, 3.4f)))
+ }
+ }
+}
+
+class DeployRequestTest extends ActorTest {
+ "VehicleService" should {
+ "pass DeployRequest" in {
+ val service = system.actorOf(Props[VehicleService], "service")
+ service ! Service.Join("test")
+ service ! VehicleServiceMessage("test", VehicleAction.DeployRequest(PlanetSideGUID(10), PlanetSideGUID(11), DriveState.Mobile, 0, false, Vector3(1.2f, 3.4f, 5.6f)))
+ expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.DeployRequest(PlanetSideGUID(11), DriveState.Mobile, 0, false, Vector3(1.2f, 3.4f, 5.6f))))
+ }
+ }
+}
+
+class DismountVehicleTest extends ActorTest {
+ "VehicleService" should {
+ "pass DismountVehicle" in {
+ val service = system.actorOf(Props[VehicleService], "service")
+ service ! Service.Join("test")
+ service ! VehicleServiceMessage("test", VehicleAction.DismountVehicle(PlanetSideGUID(10), 0, false))
+ expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.DismountVehicle(0, false)))
+ }
+ }
+}
+
+class InventoryStateTest extends ActorTest {
+ val tool = Tool(GlobalDefinitions.beamer)
+ tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(13)
+ val cdata = tool.Definition.Packet.ConstructorData(tool).get
+
+ "VehicleService" should {
+ "pass InventoryState" in {
+ val service = system.actorOf(Props[VehicleService], "service")
+ service ! Service.Join("test")
+ service ! VehicleServiceMessage("test", VehicleAction.InventoryState(PlanetSideGUID(10), tool, PlanetSideGUID(11), 0, cdata))
+ expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.InventoryState(tool, PlanetSideGUID(11), 0, cdata)))
+ }
+ }
+}
+
+class KickPassengerTest extends ActorTest {
+ "VehicleService" should {
+ "pass KickPassenger" in {
+ val service = system.actorOf(Props[VehicleService], "service")
+ service ! Service.Join("test")
+ service ! VehicleServiceMessage("test", VehicleAction.KickPassenger(PlanetSideGUID(10), 0, false, PlanetSideGUID(11)))
+ expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.KickPassenger(0, false, PlanetSideGUID(11))))
+ }
+ }
+}
+
+class LoadVehicleTest extends ActorTest {
+ val vehicle = Vehicle(GlobalDefinitions.quadstealth)
+ val cdata = vehicle.Definition.Packet.ConstructorData(vehicle).get
+
+ "VehicleService" should {
+ "pass LoadVehicle" in {
+ val service = system.actorOf(Props[VehicleService], "service")
+ service ! Service.Join("test")
+ service ! VehicleServiceMessage("test", VehicleAction.LoadVehicle(PlanetSideGUID(10), vehicle, 12, PlanetSideGUID(11), cdata))
+ expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.LoadVehicle(vehicle, 12, PlanetSideGUID(11), cdata)))
+ }
+ }
+}
+
+class MountVehicleTest extends ActorTest {
+ "VehicleService" should {
+ "pass MountVehicle" in {
+ val service = system.actorOf(Props[VehicleService], "service")
+ service ! Service.Join("test")
+ service ! VehicleServiceMessage("test", VehicleAction.MountVehicle(PlanetSideGUID(10), PlanetSideGUID(11), 0))
+ expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.MountVehicle(PlanetSideGUID(11), 0)))
+ }
+ }
+}
+
+class SeatPermissionsTest extends ActorTest {
+ "VehicleService" should {
+ "pass SeatPermissions" in {
+ val service = system.actorOf(Props[VehicleService], "service")
+ service ! Service.Join("test")
+ service ! VehicleServiceMessage("test", VehicleAction.SeatPermissions(PlanetSideGUID(10), PlanetSideGUID(11), 0, 12L))
+ expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.SeatPermissions(PlanetSideGUID(11), 0, 12L)))
+ }
+ }
+}
+
+class StowEquipmentTest extends ActorTest {
+ val tool = Tool(GlobalDefinitions.beamer)
+ tool.GUID = PlanetSideGUID(12)
+ tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(13)
+ val toolDef = tool.Definition
+ val cdata = tool.Definition.Packet.DetailedConstructorData(tool).get
+
+ "StowEquipment" should {
+ "pass StowEquipment" in {
+ val service = system.actorOf(Props[VehicleService], "service")
+ service ! Service.Join("test")
+ service ! VehicleServiceMessage("test", VehicleAction.StowEquipment(PlanetSideGUID(10), PlanetSideGUID(11), 0, tool))
+ expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.StowEquipment(PlanetSideGUID(11), 0, toolDef.ObjectId, tool.GUID, cdata)))
+ }
+ }
+}
+
+class UnstowEquipmentTest extends ActorTest {
+ "VehicleService" should {
+ "pass UnstowEquipment" in {
+ val service = system.actorOf(Props[VehicleService], "service")
+ service ! Service.Join("test")
+ service ! VehicleServiceMessage("test", VehicleAction.UnstowEquipment(PlanetSideGUID(10), PlanetSideGUID(11)))
+ expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.UnstowEquipment(PlanetSideGUID(11))))
+ }
+ }
+}
+
+class VehicleStateTest extends ActorTest {
+ "VehicleService" should {
+ "pass VehicleState" in {
+ val service = system.actorOf(Props[VehicleService], "service")
+ service ! Service.Join("test")
+ service ! VehicleServiceMessage("test", VehicleAction.VehicleState(PlanetSideGUID(10), PlanetSideGUID(11), 0, Vector3(1.2f, 3.4f, 5.6f), Vector3(7.8f, 9.1f, 2.3f), Some(Vector3(4.5f, 6.7f, 8.9f)), Option(1), 2, 3, 4, false, true))
+ expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.VehicleState(PlanetSideGUID(11), 0, Vector3(1.2f, 3.4f, 5.6f), Vector3(7.8f, 9.1f, 2.3f), Some(Vector3(4.5f, 6.7f, 8.9f)), Option(1), 2, 3, 4, false, true)))
+ }
+ }
+}
+
+object VehicleServiceTest {
+ //decoy
+}