diff --git a/.codecov.yml b/.codecov.yml index 75bed4f24..959d9a665 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 edc105606..e16159b50 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 a5ce3bbdd..b15590e27 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 693a11648..09d8136d7 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 137ad6c0e..5a0ba6300 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 000000000..fe2bb7c85 --- /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 000000000..94c1702ea --- /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 ced4442ca..cffb90305 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 008f96f7a..0a38816a2 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 54a6fff41..a424d02bb 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 8292a5b20..34eac81db 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 fd685b730..e32c774a5 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 6d10e9110..691398120 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 000000000..8321149ec --- /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 d0e4826e9..9cb0057c5 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 5bf6a5d4c..66afa834b 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 32c084125..a7bed3bc9 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 9fa88ee36..26a2412e4 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 d4b01f894..56e02385f 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 000000000..8803db513 --- /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 +}