Deployment:

Class and Actor mixins for Deployment state.  The logic is surprisingly self-contained, mostly.

DriveState:

This is not the former DriveState Enumeration of /packet/game/objectcreate/.  This is now a /types/ Enumeration shared across /common/ objects and serves the functionality of both, at least to the extent that it is understood.  Functions are includes that define the logic order or state changes and divides states into (two) groups.

VehicleService:

The directory /pslogin/src/test has been created and tests have been migrated there.  Originally, the tests were located in the wrong place and were being skipped when not executed manually.  They should now appear in coverage reports and be run as a part of continuous integration.
This commit is contained in:
FateJH 2018-02-09 19:05:16 -05:00
parent 27d86af015
commit 0fe46311ad
22 changed files with 778 additions and 86 deletions

View file

@ -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()

View file

@ -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.<br>
* <br>
<<<<<<< 27d86af015d5a835f7d594aed9ccdd1de4048c53
* To keep it simple, infantry seating, mounted weapons, and utilities are stored separately.<br>
* <br>
* 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)

View file

@ -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

View file

@ -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,

View file

@ -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.<br>
* <br>
* 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
}

View file

@ -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")
}
}
}

View file

@ -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`.<br>
@ -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 {

View file

@ -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)

View file

@ -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.

View file

@ -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

View file

@ -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.<br>
* <br>
* 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)
}
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint(3))
}

View file

@ -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

View file

@ -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
}
}