Merge pull request #188 from Fate-JH/ams-deploy

AMS Deployment
This commit is contained in:
Fate-JH 2018-02-21 22:43:58 -05:00 committed by GitHub
commit 599d767598
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 810 additions and 91 deletions

View file

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

View file

@ -2349,6 +2349,9 @@ object GlobalDefinitions {
ams.MountPoints += 2 -> 0
ams.Utilities += 3 -> UtilityType.order_terminala
ams.Utilities += 4 -> UtilityType.order_terminalb
ams.Deployment = true
ams.DeployTime = 2000
ams.UndeployTime = 2000
ams.Packet = utilityConverter
val variantConverter = new VariantVehicleConverter
@ -2356,6 +2359,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 +2371,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 +2382,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
@ -20,8 +20,7 @@ import scala.annotation.tailrec
* All infantry seating, all mounted weapons, and the trunk space are considered part of the same index hierarchy.
* Generally, all seating is declared first - the driver and passengers and and gunners.
* Following that are the mounted weapons and other utilities.
* Trunk space starts being indexed afterwards.<br>
* <br>
* Trunk space starts being indexed afterwards.
* 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.
@ -38,12 +37,12 @@ import scala.annotation.tailrec
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 +124,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 +341,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

@ -22,6 +22,8 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
private val weapons : mutable.HashMap[Int, ToolDefinition] = mutable.HashMap[Int, ToolDefinition]()
private var deployment : Boolean = false
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 var trunkSize : InventoryTile = InventoryTile.None
private var trunkOffset : Int = 0
private var canCloak : Boolean = false
@ -70,8 +72,23 @@ class VehicleDefinition(objectId : Int) extends ObjectDefinition(objectId) {
Deployment
}
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 TrunkSize : InventoryTile = trunkSize
def TrunkSize_=(tile : InventoryTile) : InventoryTile = {

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

@ -5,6 +5,7 @@ import akka.actor.Actor
import net.psforever.objects.Vehicle
import net.psforever.objects.serverobject.mount.MountableBehavior
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.deploy.DeploymentBehavior
/**
* An `Actor` that handles messages being dispatched to a specific `Vehicle`.<br>
@ -15,18 +16,22 @@ 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 {
//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
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
}
}

View file

@ -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,50 @@ 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))
DeploymentActivities(obj)
//...
}
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 +2146,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) =>
@ -2607,12 +2653,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
/**
* Temporary function that iterates over vehicle permissions and turns them into `PlanetsideAttributeMessage` packets.<br>
* <br>
* 2 November 2017
* 2 November 2017:<br>
* Unexpected behavior causes seat mount points to become blocked when a new driver claims the vehicle.
* For the purposes of ensuring that other players are always aware of the proper permission state of the trunk and seats,
* packets are intentionally dispatched to the current client to update the states.
* Perform this action just after any instance where the client would initially gain awareness of the vehicle.
* The most important examples include either the player or the vehicle itself spawning in for the first time.
* The most important examples include either the player or the vehicle itself spawning in for the first time.<br>
* <br>
* 20 February 2018:<br>
* Occasionally, during deployment, local(?) vehicle seat access permissions may change.
* This results in players being locked into their own vehicle.
* Reloading vehicle permissions supposedly ensures the seats will be properly available.
* This is considered a client issue; but, somehow, it also impacts server operation somehow.
* @param vehicle the `Vehicle`
*/
def ReloadVehicleAccessPermissions(vehicle : Vehicle) : Unit = {
@ -2964,6 +3016,38 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
/**
* Perform specific operations depending on the target of deployment.
* @param obj the object that has deployed
*/
def DeploymentActivities(obj : Deployment.DeploymentObject) : Unit = {
obj match {
case vehicle : Vehicle =>
//TODO we should not have to do this imho
ReloadVehicleAccessPermissions(vehicle)
case _ => ;
}
}
/**
* 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()))

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,239 @@
// 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, ServiceManager}
import services.vehicle._
class VehicleService1Test extends ActorTest {
ServiceManager.boot(system)
"VehicleService" should {
"construct" in {
system.actorOf(Props[VehicleService], "v-service")
assert(true)
}
}
}
class VehicleService2Test extends ActorTest {
ServiceManager.boot(system)
"VehicleService" should {
"subscribe" in {
val service = system.actorOf(Props[VehicleService], "v-service")
service ! Service.Join("test")
assert(true)
}
}
}
class VehicleService3Test extends ActorTest {
ServiceManager.boot(system)
"VehicleService" should {
"subscribe to a specific channel" in {
val service = system.actorOf(Props[VehicleService], "v-service")
service ! Service.Join("test")
service ! Service.Leave()
assert(true)
}
}
}
class VehicleService4Test extends ActorTest {
ServiceManager.boot(system)
"VehicleService" should {
"subscribe" in {
val service = system.actorOf(Props[VehicleService], "v-service")
service ! Service.Join("test")
service ! Service.LeaveAll()
assert(true)
}
}
}
class VehicleService5Test extends ActorTest {
ServiceManager.boot(system)
"VehicleService" should {
"pass an unhandled message" in {
val service = system.actorOf(Props[VehicleService], "v-service")
service ! Service.Join("test")
service ! "hello"
expectNoMsg()
}
}
}
class AwarenessTest extends ActorTest {
ServiceManager.boot(system)
"VehicleService" should {
"pass Awareness" in {
val service = system.actorOf(Props[VehicleService], "v-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 {
ServiceManager.boot(system)
"VehicleService" should {
"pass ChildObjectState" in {
val service = system.actorOf(Props[VehicleService], "v-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 {
ServiceManager.boot(system)
"VehicleService" should {
"pass DeployRequest" in {
val service = system.actorOf(Props[VehicleService], "v-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 {
ServiceManager.boot(system)
"VehicleService" should {
"pass DismountVehicle" in {
val service = system.actorOf(Props[VehicleService], "v-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 {
ServiceManager.boot(system)
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], "v-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 {
ServiceManager.boot(system)
"VehicleService" should {
"pass KickPassenger" in {
val service = system.actorOf(Props[VehicleService], "v-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 {
ServiceManager.boot(system)
val vehicle = Vehicle(GlobalDefinitions.quadstealth)
val cdata = vehicle.Definition.Packet.ConstructorData(vehicle).get
"VehicleService" should {
"pass LoadVehicle" in {
val service = system.actorOf(Props[VehicleService], "v-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 {
ServiceManager.boot(system)
"VehicleService" should {
"pass MountVehicle" in {
val service = system.actorOf(Props[VehicleService], "v-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 {
ServiceManager.boot(system)
"VehicleService" should {
"pass SeatPermissions" in {
val service = system.actorOf(Props[VehicleService], "v-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 {
ServiceManager.boot(system)
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], "v-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 {
ServiceManager.boot(system)
"VehicleService" should {
"pass UnstowEquipment" in {
val service = system.actorOf(Props[VehicleService], "v-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 {
ServiceManager.boot(system)
"VehicleService" should {
"pass VehicleState" in {
val service = system.actorOf(Props[VehicleService], "v-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
}