mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +00:00
Vehicles Must Be Destroyed (#390)
* internalized the actions of the VehicleRemover into the vehicle control agency; this required modifications to vehicle deconstruction messaging, but also had implications for the vehicle spawn pad * fixed tests; created a with-context ActorTest environment; hacked away the unnecessary aspects of VehicleRemover * changes to tests; simplifications to terminated cargoing abilities * removing unnecessary indirection from cargo handling at the end of a vehicle's life
This commit is contained in:
parent
58134e02fa
commit
9f12cfa625
|
|
@ -16,6 +16,7 @@ import net.psforever.objects.vital.{DamageResistanceModel, StandardResistancePro
|
|||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
/**
|
||||
* The server-side support object that represents a vehicle.<br>
|
||||
|
|
@ -577,11 +578,12 @@ object Vehicle {
|
|||
final case class VehicleMessages(player : Player, response : Exchange)
|
||||
|
||||
/**
|
||||
* The `Vehicle` will become unresponsive to player activity.
|
||||
* Usually, it does this to await deconstruction and clean-up.
|
||||
* Initiate vehicle deconstruction.
|
||||
* @see `VehicleControl`
|
||||
* @param time the delay before deconstruction should initiate;
|
||||
* should initiate instantly when `None`
|
||||
*/
|
||||
final case class PrepareForDeletion()
|
||||
final case class Deconstruct(time : Option[FiniteDuration] = None)
|
||||
|
||||
/**
|
||||
* The `Vehicle` will resume previous unresponsiveness to player activity.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import net.psforever.objects.vehicles.{CargoBehavior, VehicleLockState}
|
|||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.TriggeredSound
|
||||
import net.psforever.types.{DriveState, PlanetSideGUID}
|
||||
import services.{RemoverActor, Service}
|
||||
import services.Service
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import services.local.{LocalAction, LocalServiceMessage}
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
|
@ -191,10 +191,10 @@ object Vehicles {
|
|||
case Some(cargo : Vehicle) => {
|
||||
cargo.Seats(0).Occupant match {
|
||||
case Some(cargoDriver: Player) =>
|
||||
CargoBehavior.HandleVehicleCargoDismount(target.Zone, cargoDriver.GUID, cargo.GUID, bailed = target.Flying, requestedByPassenger = false, kicked = true )
|
||||
CargoBehavior.HandleVehicleCargoDismount(target.Zone, cargo.GUID, bailed = target.Flying, requestedByPassenger = false, kicked = true )
|
||||
case None =>
|
||||
log.error("FinishHackingVehicle: vehicle in cargo hold missing driver")
|
||||
CargoBehavior.HandleVehicleCargoDismount(hacker.GUID, cargo.GUID, cargo, target.GUID, target, false, false, true)
|
||||
CargoBehavior.HandleVehicleCargoDismount(cargo.GUID, cargo, target.GUID, target, false, false, true)
|
||||
}
|
||||
}
|
||||
case None => ;
|
||||
|
|
@ -215,8 +215,7 @@ object Vehicles {
|
|||
// If the vehicle can fly and is flying deconstruct it, and well played to whomever managed to hack a plane in mid air. I'm impressed.
|
||||
if(target.Definition.CanFly && target.Flying) {
|
||||
// todo: Should this force the vehicle to land in the same way as when a pilot bails with passengers on board?
|
||||
zone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(target), zone))
|
||||
zone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.AddTask(target, zone, Some(0 seconds)))
|
||||
target.Actor ! Vehicle.Deconstruct()
|
||||
} else { // Otherwise handle ownership transfer as normal
|
||||
// Remove ownership of our current vehicle, if we have one
|
||||
hacker.VehicleOwned match {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import net.psforever.objects.serverobject.damage.Damageable.Target
|
|||
import net.psforever.objects.serverobject.deploy.Deployment
|
||||
import net.psforever.objects.vital.resolution.ResolutionCalculations
|
||||
import net.psforever.types.{DriveState, PlanetSideGUID}
|
||||
import services.{RemoverActor, Service}
|
||||
import services.Service
|
||||
import services.local.{LocalAction, LocalServiceMessage}
|
||||
import services.vehicle.{VehicleAction, VehicleService, VehicleServiceMessage}
|
||||
|
||||
|
|
@ -180,8 +180,7 @@ object DamageableVehicle {
|
|||
target.Shields = 0
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, 0))
|
||||
}
|
||||
zone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(target), zone))
|
||||
zone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.AddTask(target, zone, Some(1 minute)))
|
||||
target.Actor ! Vehicle.Deconstruct(Some(1 minute))
|
||||
target.ClearHistory()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffi
|
|||
import net.psforever.objects.serverobject.pad.process.{VehicleSpawnControlBase, VehicleSpawnControlConcealPlayer}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.objects.{DefaultCancellable, Player, Vehicle}
|
||||
import services.RemoverActor
|
||||
import services.vehicle.VehicleServiceMessage
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
|
@ -64,9 +66,7 @@ class VehicleSpawnControl(pad : VehicleSpawnPad) extends VehicleSpawnControlBase
|
|||
handleOrderFunc(VehicleSpawnControl.Order(player, vehicle))
|
||||
}
|
||||
catch {
|
||||
case _ : AssertionError if vehicle.HasGUID => //same as order being dropped
|
||||
VehicleSpawnControl.DisposeSpawnedVehicle(vehicle, pad.Zone)
|
||||
case _ : AssertionError => ; //shrug
|
||||
case _ : AssertionError => ; //ehhh
|
||||
case e : Exception => //something unexpected
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
|
@ -143,7 +143,7 @@ class VehicleSpawnControl(pad : VehicleSpawnPad) extends VehicleSpawnControlBase
|
|||
pad.Zone.VehicleEvents ! VehicleSpawnPad.PeriodicReminder(name, VehicleSpawnPad.Reminders.Queue, Some(orders.length + 1))
|
||||
}
|
||||
else {
|
||||
VehicleSpawnControl.DisposeSpawnedVehicle(order, pad.Zone)
|
||||
VehicleSpawnControl.DisposeVehicle(order.vehicle, pad.Zone)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -282,19 +282,24 @@ object VehicleSpawnControl {
|
|||
* Properly clean up a vehicle that has been registered and spawned into the game world.
|
||||
* Call this downstream of "`ConcealPlayer`".
|
||||
* @param entry the order being cancelled
|
||||
* @param zone the continent on which the vehicle was registered
|
||||
* @param zone the zone in which the vehicle is registered (should be located)
|
||||
*/
|
||||
def DisposeSpawnedVehicle(entry : VehicleSpawnControl.Order, zone: Zone) : Unit = {
|
||||
DisposeSpawnedVehicle(entry.vehicle, zone)
|
||||
DisposeVehicle(entry.vehicle, zone)
|
||||
zone.VehicleEvents ! VehicleSpawnPad.RevealPlayer(entry.DriverGUID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly clean up a vehicle that has been registered and spawned into the game world.
|
||||
* @param vehicle the vehicle being cancelled
|
||||
* @param zone the continent on which the vehicle was registered
|
||||
* @param vehicle the vehicle being disposed
|
||||
* @param zone the zone in which the vehicle is registered (should be located)
|
||||
*/
|
||||
def DisposeSpawnedVehicle(vehicle : Vehicle, zone: Zone) : Unit = {
|
||||
zone.VehicleEvents ! VehicleSpawnPad.DisposeVehicle(vehicle)
|
||||
def DisposeVehicle(vehicle : Vehicle, zone : Zone) : Unit = {
|
||||
if(zone.Vehicles.exists(_.GUID == vehicle.GUID)) { //already added to zone
|
||||
vehicle.Actor ! Vehicle.Deconstruct()
|
||||
}
|
||||
else { //just registered to zone
|
||||
zone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.AddTask(vehicle, zone, Some(0 seconds)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class VehicleSpawnControlConcealPlayer(pad : VehicleSpawnPad) extends VehicleSpa
|
|||
}
|
||||
else {
|
||||
trace(s"integral component lost; abort order fulfillment")
|
||||
VehicleSpawnControl.DisposeSpawnedVehicle(order.vehicle, pad.Zone)
|
||||
VehicleSpawnControl.DisposeVehicle(order.vehicle, pad.Zone)
|
||||
context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,15 +21,12 @@ class VehicleSpawnControlDriverControl(pad : VehicleSpawnPad) extends VehicleSpa
|
|||
|
||||
def receive : Receive = {
|
||||
case order @ VehicleSpawnControl.Order(driver, vehicle) =>
|
||||
if(vehicle.Health == 0) {
|
||||
trace(s"vehicle was already destroyed; but, everything is fine")
|
||||
}
|
||||
if(vehicle.PassengerInSeat(driver).contains(0)) {
|
||||
if(vehicle.Health > 0 && vehicle.PassengerInSeat(driver).contains(0)) {
|
||||
trace(s"returning control of ${vehicle.Definition.Name} to ${driver.Name}")
|
||||
pad.Zone.VehicleEvents ! VehicleSpawnPad.ServerVehicleOverrideEnd(driver.Name, vehicle, pad)
|
||||
}
|
||||
else {
|
||||
trace(s"${driver.Name} is not seated in ${vehicle.Definition.Name}; vehicle controls have been locked")
|
||||
trace(s"${driver.Name} is not seated in ${vehicle.Definition.Name}; vehicle controls might have been locked")
|
||||
}
|
||||
finalClear ! order
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ package net.psforever.objects.serverobject.pad.process
|
|||
import akka.actor.Props
|
||||
import net.psforever.objects.GlobalDefinitions
|
||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
|
@ -36,7 +37,7 @@ class VehicleSpawnControlLoadVehicle(pad : VehicleSpawnPad) extends VehicleSpawn
|
|||
}
|
||||
else {
|
||||
trace("owner lost or vehicle in poor condition; abort order fulfillment")
|
||||
VehicleSpawnControl.DisposeSpawnedVehicle(order, pad.Zone)
|
||||
VehicleSpawnControl.DisposeVehicle(order.vehicle, pad.Zone)
|
||||
context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
package net.psforever.objects.serverobject.pad.process
|
||||
|
||||
import akka.actor.{ActorRef, Props}
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
|
@ -38,9 +39,12 @@ class VehicleSpawnControlSeatDriver(pad : VehicleSpawnPad) extends VehicleSpawnC
|
|||
|
||||
case VehicleSpawnControlSeatDriver.BeginDriverInSeat(entry) =>
|
||||
val driver = entry.driver
|
||||
if(entry.vehicle.Health > 0 && driver.isAlive && driver.Continent == pad.Continent && driver.VehicleSeated.isEmpty) {
|
||||
val vehicle = entry.vehicle
|
||||
//avoid unattended vehicle blocking the pad; user should mount (and does so normally) to reset decon timer
|
||||
vehicle.Actor ! Vehicle.Deconstruct(Some(30 seconds))
|
||||
if(vehicle.Health > 0 && driver.isAlive && driver.Continent == pad.Continent && driver.VehicleSeated.isEmpty) {
|
||||
trace("driver to be made seated in vehicle")
|
||||
pad.Zone.VehicleEvents ! VehicleSpawnPad.StartPlayerSeatedInVehicle(entry.driver.Name, entry.vehicle, pad)
|
||||
pad.Zone.VehicleEvents ! VehicleSpawnPad.StartPlayerSeatedInVehicle(driver.Name, vehicle, pad)
|
||||
}
|
||||
else{
|
||||
trace("driver lost; vehicle stranded on pad")
|
||||
|
|
@ -48,7 +52,7 @@ class VehicleSpawnControlSeatDriver(pad : VehicleSpawnPad) extends VehicleSpawnC
|
|||
context.system.scheduler.scheduleOnce(2500 milliseconds, self, VehicleSpawnControlSeatDriver.DriverInSeat(entry))
|
||||
|
||||
case VehicleSpawnControlSeatDriver.DriverInSeat(entry) =>
|
||||
if(entry.driver.isAlive && entry.vehicle.PassengerInSeat(entry.driver).contains(0)) {
|
||||
if(entry.vehicle.Health > 0 && entry.driver.isAlive && entry.vehicle.PassengerInSeat(entry.driver).contains(0)) {
|
||||
trace(s"driver ${entry.driver.Name} has taken the wheel")
|
||||
pad.Zone.VehicleEvents ! VehicleSpawnPad.PlayerSeatedInVehicle(entry.driver.Name, entry.vehicle, pad)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import net.psforever.objects.vehicles.CargoBehavior.{CheckCargoDismount, CheckCa
|
|||
import net.psforever.packet.game.{CargoMountPointStatusMessage, ObjectAttachMessage, ObjectDetachMessage, PlanetsideAttributeMessage}
|
||||
import net.psforever.types.{CargoStatus, PlanetSideGUID, Vector3}
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import services.{RemoverActor, Service}
|
||||
import services.Service
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -231,18 +231,18 @@ object CargoBehavior {
|
|||
|
||||
/**
|
||||
* na
|
||||
* @param player_guid na
|
||||
* @param zone na
|
||||
* @param cargo_guid na
|
||||
* @param bailed na
|
||||
* @param requestedByPassenger na
|
||||
* @param kicked na
|
||||
*/
|
||||
def HandleVehicleCargoDismount(zone : Zone, player_guid : PlanetSideGUID, cargo_guid : PlanetSideGUID, bailed : Boolean, requestedByPassenger : Boolean, kicked : Boolean) : Unit = {
|
||||
def HandleVehicleCargoDismount(zone : Zone, cargo_guid : PlanetSideGUID, bailed : Boolean, requestedByPassenger : Boolean, kicked : Boolean) : Unit = {
|
||||
zone.GUID(cargo_guid) match {
|
||||
case Some(cargo : Vehicle) =>
|
||||
zone.GUID(cargo.MountedIn) match {
|
||||
case Some(ferry : Vehicle) =>
|
||||
HandleVehicleCargoDismount(player_guid, cargo_guid, cargo, ferry.GUID, ferry, bailed, requestedByPassenger, kicked)
|
||||
HandleVehicleCargoDismount(cargo_guid, cargo, ferry.GUID, ferry, bailed, requestedByPassenger, kicked)
|
||||
case _ =>
|
||||
log.warn(s"DismountVehicleCargo: target ${cargo.Definition.Name}@$cargo_guid does not know what treats it as cargo")
|
||||
}
|
||||
|
|
@ -253,7 +253,6 @@ object CargoBehavior {
|
|||
|
||||
/**
|
||||
* na
|
||||
* @param player_guid the target player
|
||||
* @param cargoGUID the globally unique number for the vehicle being ferried
|
||||
* @param cargo the vehicle being ferried
|
||||
* @param carrierGUID the globally unique number for the vehicle doing the ferrying
|
||||
|
|
@ -262,7 +261,7 @@ object CargoBehavior {
|
|||
* @param requestedByPassenger the ferried vehicle is being politely disembarked from the cargo hold
|
||||
* @param kicked the ferried vehicle is being kicked out of the cargo hold
|
||||
*/
|
||||
def HandleVehicleCargoDismount(player_guid : PlanetSideGUID, cargoGUID : PlanetSideGUID, cargo : Vehicle, carrierGUID : PlanetSideGUID, carrier : Vehicle, bailed : Boolean, requestedByPassenger : Boolean, kicked : Boolean) : Unit = {
|
||||
def HandleVehicleCargoDismount(cargoGUID : PlanetSideGUID, cargo : Vehicle, carrierGUID : PlanetSideGUID, carrier : Vehicle, bailed : Boolean, requestedByPassenger : Boolean, kicked : Boolean) : Unit = {
|
||||
val zone = carrier.Zone
|
||||
carrier.CargoHolds.find({case(_, hold) => hold.Occupant.contains(cargo)}) match {
|
||||
case Some((mountPoint, hold)) =>
|
||||
|
|
@ -284,41 +283,43 @@ object CargoBehavior {
|
|||
//the lodestar's cargo hold is almost the center of the vehicle
|
||||
carrier.Position
|
||||
}
|
||||
zone.VehicleEvents ! VehicleServiceMessage(s"${cargo.Actor}", VehicleAction.SendResponse(PlanetSideGUID(0), PlanetsideAttributeMessage(cargoGUID, 0, cargo.Health)))
|
||||
zone.VehicleEvents ! VehicleServiceMessage(s"${cargo.Actor}", VehicleAction.SendResponse(PlanetSideGUID(0), PlanetsideAttributeMessage(cargoGUID, 68, cargo.Shields)))
|
||||
val GUID0 = Service.defaultPlayerGUID
|
||||
val zoneId = zone.Id
|
||||
val events = zone.VehicleEvents
|
||||
val cargoActor = cargo.Actor
|
||||
events ! VehicleServiceMessage(s"$cargoActor", VehicleAction.SendResponse(GUID0, PlanetsideAttributeMessage(cargoGUID, 0, cargo.Health)))
|
||||
events ! VehicleServiceMessage(s"$cargoActor", VehicleAction.SendResponse(GUID0, PlanetsideAttributeMessage(cargoGUID, 68, cargo.Shields)))
|
||||
if(carrier.Flying) {
|
||||
//the carrier vehicle is flying; eject the cargo vehicle
|
||||
val ejectCargoMsg = CargoMountPointStatusMessage(carrierGUID, PlanetSideGUID(0), PlanetSideGUID(0), cargoGUID, mountPoint, CargoStatus.InProgress, 0)
|
||||
val ejectCargoMsg = CargoMountPointStatusMessage(carrierGUID, GUID0, GUID0, cargoGUID, mountPoint, CargoStatus.InProgress, 0)
|
||||
val detachCargoMsg = ObjectDetachMessage(carrierGUID, cargoGUID, cargoHoldPosition - Vector3.z(1), rotation)
|
||||
val resetCargoMsg = CargoMountPointStatusMessage(carrierGUID, PlanetSideGUID(0), PlanetSideGUID(0), cargoGUID, mountPoint, CargoStatus.Empty, 0)
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.SendResponse(Service.defaultPlayerGUID, ejectCargoMsg))
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.SendResponse(Service.defaultPlayerGUID, detachCargoMsg))
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.SendResponse(Service.defaultPlayerGUID, resetCargoMsg))
|
||||
val resetCargoMsg = CargoMountPointStatusMessage(carrierGUID, GUID0, GUID0, cargoGUID, mountPoint, CargoStatus.Empty, 0)
|
||||
events ! VehicleServiceMessage(zoneId, VehicleAction.SendResponse(GUID0, ejectCargoMsg))
|
||||
events ! VehicleServiceMessage(zoneId, VehicleAction.SendResponse(GUID0, detachCargoMsg))
|
||||
events ! VehicleServiceMessage(zoneId, VehicleAction.SendResponse(GUID0, resetCargoMsg))
|
||||
log.debug(ejectCargoMsg.toString)
|
||||
log.debug(detachCargoMsg.toString)
|
||||
if(driverOpt.isEmpty) {
|
||||
//TODO cargo should drop like a rock like normal; until then, deconstruct it
|
||||
zone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(cargo), zone))
|
||||
zone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.AddTask(cargo, zone, Some(0 seconds)))
|
||||
cargo.Actor ! Vehicle.Deconstruct()
|
||||
}
|
||||
}
|
||||
else {
|
||||
//the carrier vehicle is not flying; just open the door and let the cargo vehicle back out; force it out if necessary
|
||||
val cargoStatusMessage = CargoMountPointStatusMessage(carrierGUID, PlanetSideGUID(0), cargoGUID, PlanetSideGUID(0), mountPoint, CargoStatus.InProgress, 0)
|
||||
val cargoStatusMessage = CargoMountPointStatusMessage(carrierGUID, GUID0, cargoGUID, GUID0, mountPoint, CargoStatus.InProgress, 0)
|
||||
val cargoDetachMessage = ObjectDetachMessage(carrierGUID, cargoGUID, cargoHoldPosition + Vector3.z(1f), rotation)
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.SendResponse(Service.defaultPlayerGUID, cargoStatusMessage))
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.SendResponse(Service.defaultPlayerGUID, cargoDetachMessage))
|
||||
events ! VehicleServiceMessage(zoneId, VehicleAction.SendResponse(GUID0, cargoStatusMessage))
|
||||
events ! VehicleServiceMessage(zoneId, VehicleAction.SendResponse(GUID0, cargoDetachMessage))
|
||||
driverOpt match {
|
||||
case Some(driver) =>
|
||||
zone.VehicleEvents ! VehicleServiceMessage(s"${driver.Name}", VehicleAction.KickCargo(player_guid, cargo, cargo.Definition.AutoPilotSpeed2, 2500))
|
||||
events ! VehicleServiceMessage(s"${driver.Name}", VehicleAction.KickCargo(GUID0, cargo, cargo.Definition.AutoPilotSpeed2, 2500))
|
||||
//check every quarter second if the vehicle has moved far enough away to be considered dismounted
|
||||
cargo.Actor ! CheckCargoDismount(carrierGUID, mountPoint, 0)
|
||||
cargoActor ! CheckCargoDismount(carrierGUID, mountPoint, 0)
|
||||
case None =>
|
||||
val resetCargoMsg = CargoMountPointStatusMessage(carrierGUID, PlanetSideGUID(0), PlanetSideGUID(0), cargoGUID, mountPoint, CargoStatus.Empty, 0)
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.SendResponse(PlanetSideGUID(0), resetCargoMsg)) //lazy
|
||||
val resetCargoMsg = CargoMountPointStatusMessage(carrierGUID, GUID0, GUID0, cargoGUID, mountPoint, CargoStatus.Empty, 0)
|
||||
events ! VehicleServiceMessage(zoneId, VehicleAction.SendResponse(GUID0, resetCargoMsg)) //lazy
|
||||
//TODO cargo should back out like normal; until then, deconstruct it
|
||||
zone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(cargo), zone))
|
||||
zone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.AddTask(cargo, zone, Some(0 seconds)))
|
||||
cargoActor ! Vehicle.Deconstruct()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.vehicles
|
||||
|
||||
import akka.actor.{Actor, ActorRef}
|
||||
import net.psforever.objects.{GlobalDefinitions, SimpleItem, Vehicle, Vehicles}
|
||||
import akka.actor.{Actor, ActorRef, Cancellable}
|
||||
import net.psforever.objects.{DefaultCancellable, GlobalDefinitions, SimpleItem, Vehicle, Vehicles}
|
||||
import net.psforever.objects.ballistics.{ResolvedProjectile, VehicleSource}
|
||||
import net.psforever.objects.equipment.JammableMountedWeapons
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
|
|
@ -13,9 +13,14 @@ import net.psforever.objects.serverobject.deploy.DeploymentBehavior
|
|||
import net.psforever.objects.serverobject.hackable.GenericHackables
|
||||
import net.psforever.objects.serverobject.repair.RepairableVehicle
|
||||
import net.psforever.objects.vital.VehicleShieldCharge
|
||||
import net.psforever.types.{ExoSuitType, PlanetSideGUID}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.types.{DriveState, ExoSuitType, PlanetSideGUID, Vector3}
|
||||
import services.{RemoverActor, Service}
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* An `Actor` that handles messages being dispatched to a specific `Vehicle`.<br>
|
||||
* <br>
|
||||
|
|
@ -44,10 +49,17 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
|||
def DamageableObject = vehicle
|
||||
def RepairableObject = vehicle
|
||||
|
||||
/** cheap flag for whether the vehicle is decaying */
|
||||
var decaying : Boolean = false
|
||||
/** primary vehicle decay timer */
|
||||
var decayTimer : Cancellable = DefaultCancellable.obj
|
||||
|
||||
def receive : Receive = Enabled
|
||||
|
||||
override def postStop() : Unit = {
|
||||
super.postStop()
|
||||
decaying = false
|
||||
decayTimer.cancel
|
||||
vehicle.Utilities.values.foreach { util =>
|
||||
context.stop(util().Actor)
|
||||
util().Actor = ActorRef.noSender
|
||||
|
|
@ -63,9 +75,18 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
|||
.orElse {
|
||||
case msg : Mountable.TryMount =>
|
||||
tryMountBehavior.apply(msg)
|
||||
if(MountableObject.Seats.values.exists(_.isOccupied)) {
|
||||
decaying = false
|
||||
decayTimer.cancel
|
||||
}
|
||||
|
||||
case msg : Mountable.TryDismount =>
|
||||
dismountBehavior.apply(msg)
|
||||
val obj = MountableObject
|
||||
if(!decaying && obj.Owner.isEmpty && obj.Seats.values.forall(!_.isOccupied)) {
|
||||
decaying = true
|
||||
decayTimer = context.system.scheduler.scheduleOnce(MountableObject.Definition.DeconstructionTime.getOrElse(5 minutes), self, VehicleControl.PrepareForDeletion())
|
||||
}
|
||||
|
||||
case Vehicle.ChargeShields(amount) =>
|
||||
val now : Long = System.nanoTime
|
||||
|
|
@ -94,10 +115,18 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
|||
)
|
||||
}
|
||||
|
||||
case Vehicle.PrepareForDeletion() =>
|
||||
CancelJammeredSound(vehicle)
|
||||
CancelJammeredStatus(vehicle)
|
||||
context.become(Disabled)
|
||||
case Vehicle.Deconstruct(time) =>
|
||||
time match {
|
||||
case Some(delay) =>
|
||||
decaying = true
|
||||
decayTimer.cancel
|
||||
decayTimer = context.system.scheduler.scheduleOnce(delay, self, VehicleControl.PrepareForDeletion())
|
||||
case _ =>
|
||||
PrepareForDeletion()
|
||||
}
|
||||
|
||||
case VehicleControl.PrepareForDeletion() =>
|
||||
PrepareForDeletion()
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
|
@ -128,14 +157,56 @@ class VehicleControl(vehicle : Vehicle) extends Actor
|
|||
}
|
||||
}
|
||||
|
||||
def PrepareForDeletion() : Unit = {
|
||||
decaying = false
|
||||
val guid = vehicle.GUID
|
||||
val zone = vehicle.Zone
|
||||
val zoneId = zone.Id
|
||||
val events = zone.VehicleEvents
|
||||
//become disabled
|
||||
context.become(Disabled)
|
||||
//cancel jammed behavior
|
||||
CancelJammeredSound(vehicle)
|
||||
CancelJammeredStatus(vehicle)
|
||||
//escape being someone else's cargo
|
||||
vehicle.MountedIn match {
|
||||
case Some(_) =>
|
||||
CargoBehavior.HandleVehicleCargoDismount(zone, guid, bailed = false, requestedByPassenger = false, kicked = false)
|
||||
case _ => ;
|
||||
}
|
||||
//kick all passengers
|
||||
vehicle.Seats.values.foreach(seat => {
|
||||
seat.Occupant match {
|
||||
case Some(player) =>
|
||||
seat.Occupant = None
|
||||
player.VehicleSeated = None
|
||||
if(player.HasGUID) {
|
||||
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, false, guid))
|
||||
}
|
||||
case None => ;
|
||||
}
|
||||
//abandon all cargo
|
||||
vehicle.CargoHolds.values
|
||||
.collect { case hold if hold.isOccupied =>
|
||||
val cargo = hold.Occupant.get
|
||||
CargoBehavior.HandleVehicleCargoDismount(cargo.GUID, cargo, guid, vehicle, bailed = false, requestedByPassenger = false, kicked = false)
|
||||
}
|
||||
})
|
||||
//unregister
|
||||
events ! VehicleServiceMessage.Decon(RemoverActor.AddTask(vehicle, zone, Some(0 seconds)))
|
||||
//banished to the shadow realm
|
||||
vehicle.Position = Vector3.Zero
|
||||
vehicle.DeploymentState = DriveState.Mobile
|
||||
//queue final deletion
|
||||
decayTimer = context.system.scheduler.scheduleOnce(5 seconds, self, VehicleControl.Deletion())
|
||||
}
|
||||
|
||||
def Disabled : Receive = checkBehavior
|
||||
.orElse {
|
||||
case msg : Mountable.TryDismount =>
|
||||
dismountBehavior.apply(msg)
|
||||
|
||||
case Vehicle.Reactivate() =>
|
||||
context.become(Enabled)
|
||||
|
||||
case VehicleControl.Deletion() =>
|
||||
val zone = vehicle.Zone
|
||||
zone.VehicleEvents ! VehicleServiceMessage(zone.Id, VehicleAction.UnloadVehicle(Service.defaultPlayerGUID, zone, vehicle, vehicle.GUID))
|
||||
zone.Transport ! Zone.Vehicle.Despawn(vehicle)
|
||||
case _ =>
|
||||
}
|
||||
|
||||
|
|
@ -150,6 +221,10 @@ object VehicleControl {
|
|||
import net.psforever.objects.vital.{DamageFromProjectile, VehicleShieldCharge, VitalsActivity}
|
||||
import scala.concurrent.duration._
|
||||
|
||||
private case class PrepareForDeletion()
|
||||
|
||||
private case class Deletion()
|
||||
|
||||
/**
|
||||
* Determine if a given activity entry would invalidate the act of charging vehicle shields this tick.
|
||||
* @param now the current time (in nanoseconds)
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ abstract class RemoverActor extends SupportActor[RemoverActor.Entry] {
|
|||
*/
|
||||
override def preStart() : Unit = {
|
||||
super.preStart()
|
||||
self ! Service.Startup()
|
||||
ServiceManager.serviceManager ! ServiceManager.Lookup("taskResolver") //ask for a resolver to deal with the GUID system
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -84,9 +84,6 @@ abstract class RemoverActor extends SupportActor[RemoverActor.Entry] {
|
|||
}
|
||||
|
||||
def receive : Receive = {
|
||||
case Service.Startup() =>
|
||||
ServiceManager.serviceManager ! ServiceManager.Lookup("taskResolver") //ask for a resolver to deal with the GUID system
|
||||
|
||||
case ServiceManager.LookupResult("taskResolver", endpoint) =>
|
||||
taskResolver = endpoint
|
||||
context.become(Processing)
|
||||
|
|
|
|||
|
|
@ -348,16 +348,15 @@ class PersistenceMonitor(name : String, squadService : ActorRef, taskResolver :
|
|||
def DisownVehicle(player : Player) : Unit = {
|
||||
Vehicles.Disown(player, inZone) match {
|
||||
case Some(vehicle) if vehicle.Health == 0 || (vehicle.Seats.values.forall(seat => !seat.isOccupied) && vehicle.Owner.isEmpty) =>
|
||||
inZone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(vehicle), inZone))
|
||||
inZone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.AddTask(vehicle, inZone,
|
||||
vehicle.Actor ! Vehicle.Deconstruct(
|
||||
if(vehicle.Flying) {
|
||||
//TODO gravity
|
||||
Some(0 seconds) //immediate deconstruction
|
||||
None //immediate deconstruction
|
||||
}
|
||||
else {
|
||||
vehicle.Definition.DeconstructionTime //normal deconstruction
|
||||
}
|
||||
))
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,11 +137,6 @@ class VehicleService(zone : Zone) extends Actor {
|
|||
VehicleEvents.publish(
|
||||
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.TransferPassengerChannel(old_channel, temp_channel, vehicle, vehicle_to_delete))
|
||||
)
|
||||
|
||||
case VehicleAction.ForceDismountVehicleCargo(player_guid, vehicle_guid, bailed, requestedByPassenger, kicked) =>
|
||||
VehicleEvents.publish(
|
||||
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.ForceDismountVehicleCargo(vehicle_guid, bailed, requestedByPassenger, kicked))
|
||||
)
|
||||
case VehicleAction.KickCargo(player_guid, cargo, speed, delay) =>
|
||||
VehicleEvents.publish(
|
||||
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickCargo(cargo, speed, delay))
|
||||
|
|
@ -216,12 +211,6 @@ class VehicleService(zone : Zone) extends Actor {
|
|||
VehicleEvents.publish(
|
||||
VehicleServiceResponse(s"/${zone.Id}/Vehicle", Service.defaultPlayerGUID, VehicleResponse.LoadVehicle(vehicle, vtype, vguid, vdata))
|
||||
)
|
||||
//avoid unattended vehicle spawning blocking the pad; user should mount (and does so normally) to reset decon timer
|
||||
vehicleDecon forward RemoverActor.AddTask(vehicle, zone, Some(30 seconds))
|
||||
|
||||
case VehicleSpawnPad.DisposeVehicle(vehicle) =>
|
||||
vehicleDecon forward RemoverActor.AddTask(vehicle, zone, Some(0 seconds))
|
||||
vehicleDecon forward RemoverActor.HurrySpecific(List(vehicle), zone)
|
||||
|
||||
//correspondence from WorldSessionActor
|
||||
case VehicleServiceMessage.AMSDeploymentChange(_) =>
|
||||
|
|
|
|||
|
|
@ -46,6 +46,5 @@ object VehicleAction {
|
|||
|
||||
final case class TransferPassengerChannel(player_guid : PlanetSideGUID, temp_channel : String, new_channel : String, vehicle : Vehicle, vehicle_to_delete : PlanetSideGUID) extends Action
|
||||
|
||||
final case class ForceDismountVehicleCargo(player_guid : PlanetSideGUID, vehicle_guid : PlanetSideGUID, bailed : Boolean, requestedByPassenger : Boolean, kicked : Boolean) extends Action
|
||||
final case class KickCargo(player_guid : PlanetSideGUID, cargo : Vehicle, speed : Int, delay : Long) extends Action
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,5 @@ object VehicleResponse {
|
|||
|
||||
final case class TransferPassengerChannel(old_channel : String, temp_channel : String, vehicle : Vehicle, vehicle_to_delete : PlanetSideGUID) extends Response
|
||||
|
||||
final case class ForceDismountVehicleCargo(vehicle_guid : PlanetSideGUID, bailed : Boolean, requestedByPassenger : Boolean, kicked : Boolean) extends Response
|
||||
final case class KickCargo(cargo : Vehicle, speed : Int, delay : Long) extends Response
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,88 +1,31 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
// Copyright (c) 2017-2020 PSForever
|
||||
package services.vehicle.support
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.{Actor, ActorRef}
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.types.{DriveState, PlanetSideGUID}
|
||||
import services.{RemoverActor, Service}
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.objects.guid.GUIDTask.UnregisterVehicle
|
||||
import services.{RemoverActor, ServiceManager}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
class VehicleRemover extends Actor {
|
||||
var taskResolver : ActorRef = Actor.noSender
|
||||
|
||||
class VehicleRemover extends RemoverActor {
|
||||
final val FirstStandardDuration : FiniteDuration = 5 minutes
|
||||
|
||||
final val SecondStandardDuration : FiniteDuration = 5 seconds
|
||||
|
||||
def InclusionTest(entry : RemoverActor.Entry) : Boolean = {
|
||||
entry.obj.isInstanceOf[Vehicle]
|
||||
override def preStart() : Unit = {
|
||||
super.preStart()
|
||||
ServiceManager.serviceManager ! ServiceManager.Lookup("taskResolver") //ask for a resolver to deal with the GUID system
|
||||
}
|
||||
|
||||
def InitialJob(entry : RemoverActor.Entry) : Unit = { }
|
||||
def receive : Receive = {
|
||||
case ServiceManager.LookupResult("taskResolver", endpoint) =>
|
||||
taskResolver = endpoint
|
||||
context.become(Processing)
|
||||
|
||||
def FirstJob(entry : RemoverActor.Entry) : Unit = {
|
||||
val vehicleGUID = entry.obj.GUID
|
||||
entry.zone.GUID(vehicleGUID) match {
|
||||
case Some(vehicle : Vehicle) if vehicle.HasGUID =>
|
||||
val zoneId = entry.zone.Id
|
||||
if(vehicle.Actor != ActorRef.noSender) {
|
||||
vehicle.Actor ! Vehicle.PrepareForDeletion()
|
||||
}
|
||||
//escape being someone else's cargo
|
||||
(vehicle.MountedIn match {
|
||||
case Some(carrierGUID) =>
|
||||
entry.zone.Vehicles.find(v => v.GUID == carrierGUID)
|
||||
case None =>
|
||||
None
|
||||
}) match {
|
||||
case Some(carrier : Vehicle) =>
|
||||
val driverName = carrier.Seats(0).Occupant match {
|
||||
case Some(driver) => driver.Name
|
||||
case _ => zoneId
|
||||
}
|
||||
context.parent ! VehicleServiceMessage(s"$driverName", VehicleAction.ForceDismountVehicleCargo(PlanetSideGUID(0), vehicleGUID, true, false, false))
|
||||
case _ => ;
|
||||
}
|
||||
//kick out all passengers
|
||||
vehicle.Seats.values.foreach(seat => {
|
||||
seat.Occupant match {
|
||||
case Some(tplayer) =>
|
||||
seat.Occupant = None
|
||||
tplayer.VehicleSeated = None
|
||||
if(tplayer.HasGUID) {
|
||||
context.parent ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(tplayer.GUID, 4, false, vehicleGUID))
|
||||
}
|
||||
case None => ;
|
||||
}
|
||||
//abandon all cargo
|
||||
vehicle.CargoHolds.values
|
||||
.collect { case hold if hold.isOccupied =>
|
||||
val cargo = hold.Occupant.get
|
||||
context.parent ! VehicleServiceMessage(zoneId, VehicleAction.ForceDismountVehicleCargo(PlanetSideGUID(0), cargo.GUID, true, false, false))
|
||||
}
|
||||
})
|
||||
case _ => ;
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
override def SecondJob(entry : RemoverActor.Entry) : Unit = {
|
||||
val vehicleGUID = entry.obj.GUID
|
||||
entry.zone.GUID(vehicleGUID) match {
|
||||
case Some(vehicle : Vehicle) if vehicle.HasGUID =>
|
||||
val zone = entry.zone
|
||||
vehicle.DeploymentState = DriveState.Mobile
|
||||
zone.Transport ! Zone.Vehicle.Despawn(vehicle)
|
||||
context.parent ! VehicleServiceMessage(zone.Id, VehicleAction.UnloadVehicle(Service.defaultPlayerGUID, zone, vehicle, vehicleGUID))
|
||||
super.SecondJob(entry)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
def Processing : Receive = {
|
||||
case RemoverActor.AddTask(obj : Vehicle, zone, _) =>
|
||||
taskResolver ! UnregisterVehicle(obj)(zone.GUID)
|
||||
|
||||
def ClearanceTest(entry : RemoverActor.Entry) : Boolean = entry.obj.asInstanceOf[Vehicle].Seats.values.count(_.isOccupied) == 0
|
||||
|
||||
def DeletionTask(entry : RemoverActor.Entry) : TaskResolver.GiveTask = {
|
||||
GUIDTask.UnregisterVehicle(entry.obj.asInstanceOf[Vehicle])(entry.zone.GUID)
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
54
common/src/test/scala/base/FreedContextActorTest.scala
Normal file
54
common/src/test/scala/base/FreedContextActorTest.scala
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package base
|
||||
|
||||
import akka.actor.{Actor, ActorContext, ActorRef, Props}
|
||||
import akka.pattern.ask
|
||||
import akka.util.Timeout
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.Await
|
||||
|
||||
/**
|
||||
* Create an `ActorTest` environment that has an `ActorContext` object.
|
||||
*/
|
||||
abstract class FreedContextActorTest extends ActorTest {
|
||||
/*
|
||||
Never do this in actual production code!
|
||||
ActorSystem and ActorContext offer similar mechanisms for instantiating actors.
|
||||
This is a consequence of their shared inheritance of the ActorRefFactory trait.
|
||||
They are not equivalent enough to be able to pass one as the other as a parameter.
|
||||
Because the ActorSystem has no context of its own,
|
||||
various bizarre mechanisms have to be developed to use any methods that would pass in a context object.
|
||||
We create a middleman Actor whose main purpose is to surrender its context object to the test environment directly
|
||||
and then direct all messages sent to that object to the test environment.
|
||||
*/
|
||||
private val _testContextHandler = system.actorOf(Props(classOf[ContextSensitive]), "actor-test-cs")
|
||||
private implicit val timeout = Timeout(5 seconds)
|
||||
private val _testContextHandlerResult = ask(_testContextHandler, message = "", self)
|
||||
implicit val context = Await.result(_testContextHandlerResult, timeout.duration).asInstanceOf[ActorContext]
|
||||
}
|
||||
|
||||
/**
|
||||
* Surrender your `context` object for a greater good!
|
||||
*/
|
||||
private class ContextSensitive extends Actor {
|
||||
var output : ActorRef = ActorRef.noSender
|
||||
|
||||
def receive : Receive = {
|
||||
case _ =>
|
||||
context.become(PassThroughBehavior)
|
||||
output = sender
|
||||
sender ! context
|
||||
}
|
||||
|
||||
/**
|
||||
* Once the `context` object has been leased,
|
||||
* this `Actor` becomes transparent.
|
||||
* Calling `context.parent` from whatever `Actor` was spurned by the previously provided `context`,
|
||||
* will now refer to whatever was the contact to gain access to it - the test environment.
|
||||
* @return something to `become`
|
||||
*/
|
||||
def PassThroughBehavior : Receive = {
|
||||
case msg => output forward msg
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ import net.psforever.objects.vital.Vitality
|
|||
import net.psforever.objects.zones.{Zone, ZoneMap}
|
||||
import net.psforever.packet.game.DamageWithPositionMessage
|
||||
import net.psforever.types._
|
||||
import services.{RemoverActor, Service}
|
||||
import services.Service
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import services.support.SupportActor
|
||||
import services.vehicle.support.TurretUpgrader
|
||||
|
|
@ -1307,7 +1307,6 @@ class DamageableVehicleDestroyTest extends ActorTest {
|
|||
atv.Actor ! Vitality.Damage(applyDamageTo)
|
||||
val msg124 = avatarProbe.receiveN(3, 500 milliseconds)
|
||||
val msg3 = player2Probe.receiveOne(200 milliseconds)
|
||||
val msg567 = vehicleProbe.receiveN(2, 200 milliseconds)
|
||||
assert(
|
||||
msg124.head match {
|
||||
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true
|
||||
|
|
@ -1332,18 +1331,6 @@ class DamageableVehicleDestroyTest extends ActorTest {
|
|||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg567.head match {
|
||||
case VehicleServiceMessage.Decon(SupportActor.ClearSpecific(List(target), _zone)) if (target eq atv) && (_zone eq zone) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg567(1) match {
|
||||
case VehicleServiceMessage.Decon(RemoverActor.AddTask(target, _zone, _)) if (target eq atv) && (_zone eq zone) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(atv.Health <= atv.Definition.DamageDestroysAt)
|
||||
assert(atv.Destroyed)
|
||||
//
|
||||
|
|
@ -1467,7 +1454,7 @@ class DamageableVehicleDestroyMountedTest extends ActorTest {
|
|||
player2Probe.expectNoMsg(10 milliseconds)
|
||||
val msg_player3 = player3Probe.receiveOne(200 milliseconds)
|
||||
player3Probe.expectNoMsg(10 milliseconds)
|
||||
val msg_vehicle = vehicleProbe.receiveN(6, 200 milliseconds)
|
||||
val msg_vehicle = vehicleProbe.receiveN(2, 200 milliseconds)
|
||||
vehicleProbe.expectNoMsg(10 milliseconds)
|
||||
assert(
|
||||
msg_avatar.exists( {
|
||||
|
|
@ -1511,30 +1498,6 @@ class DamageableVehicleDestroyMountedTest extends ActorTest {
|
|||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
msg_vehicle.exists( {
|
||||
case VehicleServiceMessage.Decon(SupportActor.ClearSpecific(List(target), _zone)) if (target eq lodestar) && (_zone eq zone) => true
|
||||
case _ => false
|
||||
})
|
||||
)
|
||||
assert(
|
||||
msg_vehicle.exists( {
|
||||
case VehicleServiceMessage.Decon(RemoverActor.AddTask(target, _zone, _)) if (target eq lodestar) && (_zone eq zone) => true
|
||||
case _ => false
|
||||
})
|
||||
)
|
||||
assert(
|
||||
msg_vehicle.exists( {
|
||||
case VehicleServiceMessage.Decon(SupportActor.ClearSpecific(List(target), _zone)) if (target eq atv) && (_zone eq zone) => true
|
||||
case _ => false
|
||||
})
|
||||
)
|
||||
assert(
|
||||
msg_vehicle.exists( {
|
||||
case VehicleServiceMessage.Decon(RemoverActor.AddTask(target, _zone, _)) if (target eq atv) && (_zone eq zone) => true
|
||||
case _ => false
|
||||
})
|
||||
)
|
||||
assert(
|
||||
msg_vehicle.exists( {
|
||||
case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, PlanetSideGUID(4), 27, 0))=> true
|
||||
|
|
|
|||
|
|
@ -3,16 +3,19 @@ package objects
|
|||
|
||||
import akka.actor.Props
|
||||
import akka.testkit.TestProbe
|
||||
import base.ActorTest
|
||||
import base.{ActorTest, FreedContextActorTest}
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.ballistics.{PlayerSource, Projectile, ProjectileResolution, ResolvedProjectile}
|
||||
import net.psforever.objects.definition.{SeatDefinition, VehicleDefinition}
|
||||
import net.psforever.objects.guid.NumberPoolHub
|
||||
import net.psforever.objects.guid.source.LimitedNumberSource
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.vehicles._
|
||||
import net.psforever.objects.vital.{VehicleShieldCharge, Vitality}
|
||||
import net.psforever.objects.zones.{Zone, ZoneMap}
|
||||
import net.psforever.objects.vital.VehicleShieldCharge
|
||||
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
|
||||
import net.psforever.packet.game.{CargoMountPointStatusMessage, ObjectDetachMessage, PlanetsideAttributeMessage}
|
||||
import net.psforever.types.{PlanetSideGUID, _}
|
||||
import org.specs2.mutable._
|
||||
import services.{RemoverActor, ServiceManager}
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -30,13 +33,13 @@ class VehicleTest extends Specification {
|
|||
val t_seat = new SeatDefinition
|
||||
t_seat.ArmorRestriction mustEqual SeatArmorRestriction.NoMax
|
||||
t_seat.Bailable mustEqual false
|
||||
t_seat.ControlledWeapon mustEqual None
|
||||
t_seat.ControlledWeapon.isEmpty mustEqual true
|
||||
}
|
||||
|
||||
"define (custom)" in {
|
||||
seat.ArmorRestriction mustEqual SeatArmorRestriction.MaxOnly
|
||||
seat.Bailable mustEqual true
|
||||
seat.ControlledWeapon mustEqual Some(5)
|
||||
seat.ControlledWeapon.contains(5)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -47,13 +50,13 @@ class VehicleTest extends Specification {
|
|||
fury.CanCloak mustEqual false
|
||||
fury.Seats.size mustEqual 1
|
||||
fury.Seats(0).Bailable mustEqual true
|
||||
fury.Seats(0).ControlledWeapon mustEqual Some(1)
|
||||
fury.Seats(0).ControlledWeapon.contains(1)
|
||||
fury.MountPoints.size mustEqual 2
|
||||
fury.MountPoints.get(1) mustEqual Some(0)
|
||||
fury.MountPoints.get(2) mustEqual Some(0)
|
||||
fury.MountPoints.get(1).contains(0)
|
||||
fury.MountPoints.get(2).contains(0)
|
||||
fury.Weapons.size mustEqual 1
|
||||
fury.Weapons.get(0) mustEqual None
|
||||
fury.Weapons.get(1) mustEqual Some(GlobalDefinitions.fury_weapon_systema)
|
||||
fury.Weapons.get(0).isEmpty mustEqual true
|
||||
fury.Weapons.get(1).contains(GlobalDefinitions.fury_weapon_systema)
|
||||
fury.TrunkSize.Width mustEqual 11
|
||||
fury.TrunkSize.Height mustEqual 11
|
||||
fury.TrunkOffset mustEqual 30
|
||||
|
|
@ -70,9 +73,9 @@ class VehicleTest extends Specification {
|
|||
val seat = new Seat(seat_def)
|
||||
seat.ArmorRestriction mustEqual SeatArmorRestriction.MaxOnly
|
||||
seat.Bailable mustEqual true
|
||||
seat.ControlledWeapon mustEqual Some(5)
|
||||
seat.ControlledWeapon.contains(5)
|
||||
seat.isOccupied mustEqual false
|
||||
seat.Occupant mustEqual None
|
||||
seat.Occupant.isEmpty mustEqual true
|
||||
}
|
||||
|
||||
"player can sit" in {
|
||||
|
|
@ -136,19 +139,19 @@ class VehicleTest extends Specification {
|
|||
|
||||
"construct (detailed)" in {
|
||||
val fury_vehicle = Vehicle(GlobalDefinitions.fury)
|
||||
fury_vehicle.Owner mustEqual None
|
||||
fury_vehicle.Owner.isEmpty mustEqual true
|
||||
fury_vehicle.Seats.size mustEqual 1
|
||||
fury_vehicle.Seats(0).ArmorRestriction mustEqual SeatArmorRestriction.NoMax
|
||||
fury_vehicle.Seats(0).isOccupied mustEqual false
|
||||
fury_vehicle.Seats(0).Occupant mustEqual None
|
||||
fury_vehicle.Seats(0).Occupant.isEmpty mustEqual true
|
||||
fury_vehicle.Seats(0).Bailable mustEqual true
|
||||
fury_vehicle.Seats(0).ControlledWeapon mustEqual Some(1)
|
||||
fury_vehicle.PermissionGroup(0) mustEqual Some(VehicleLockState.Locked) //driver
|
||||
fury_vehicle.PermissionGroup(1) mustEqual Some(VehicleLockState.Empire) //gunner
|
||||
fury_vehicle.PermissionGroup(2) mustEqual Some(VehicleLockState.Empire) //passenger
|
||||
fury_vehicle.PermissionGroup(3) mustEqual Some(VehicleLockState.Locked) //trunk
|
||||
fury_vehicle.Seats(0).ControlledWeapon.contains(1)
|
||||
fury_vehicle.PermissionGroup(0).contains(VehicleLockState.Locked) //driver
|
||||
fury_vehicle.PermissionGroup(1).contains(VehicleLockState.Empire) //gunner
|
||||
fury_vehicle.PermissionGroup(2).contains(VehicleLockState.Empire) //passenger
|
||||
fury_vehicle.PermissionGroup(3).contains(VehicleLockState.Locked) //trunk
|
||||
fury_vehicle.Weapons.size mustEqual 1
|
||||
fury_vehicle.Weapons.get(0) mustEqual None
|
||||
fury_vehicle.Weapons.get(0).isEmpty mustEqual true
|
||||
fury_vehicle.Weapons.get(1).isDefined mustEqual true
|
||||
fury_vehicle.Weapons(1).Equipment.isDefined mustEqual true
|
||||
fury_vehicle.Weapons(1).Equipment.get.Definition mustEqual GlobalDefinitions.fury.Weapons(1)
|
||||
|
|
@ -156,8 +159,8 @@ class VehicleTest extends Specification {
|
|||
fury_vehicle.Trunk.Width mustEqual 11
|
||||
fury_vehicle.Trunk.Height mustEqual 11
|
||||
fury_vehicle.Trunk.Offset mustEqual 30
|
||||
fury_vehicle.GetSeatFromMountPoint(1) mustEqual Some(0)
|
||||
fury_vehicle.GetSeatFromMountPoint(2) mustEqual Some(0)
|
||||
fury_vehicle.GetSeatFromMountPoint(1).contains(0)
|
||||
fury_vehicle.GetSeatFromMountPoint(2).contains(0)
|
||||
fury_vehicle.Decal mustEqual 0
|
||||
fury_vehicle.Health mustEqual fury_vehicle.Definition.MaxHealth
|
||||
}
|
||||
|
|
@ -192,30 +195,30 @@ class VehicleTest extends Specification {
|
|||
|
||||
"can use mount point to get seat number" in {
|
||||
val fury_vehicle = Vehicle(GlobalDefinitions.fury)
|
||||
fury_vehicle.GetSeatFromMountPoint(0) mustEqual None
|
||||
fury_vehicle.GetSeatFromMountPoint(1) mustEqual Some(0)
|
||||
fury_vehicle.GetSeatFromMountPoint(2) mustEqual Some(0)
|
||||
fury_vehicle.GetSeatFromMountPoint(3) mustEqual None
|
||||
fury_vehicle.GetSeatFromMountPoint(0).isEmpty mustEqual true
|
||||
fury_vehicle.GetSeatFromMountPoint(1).contains(0)
|
||||
fury_vehicle.GetSeatFromMountPoint(2).contains(0)
|
||||
fury_vehicle.GetSeatFromMountPoint(3).isEmpty mustEqual true
|
||||
}
|
||||
|
||||
"has four permission groups" in {
|
||||
val fury_vehicle = Vehicle(GlobalDefinitions.fury)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id) mustEqual Some(VehicleLockState.Locked)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Gunner.id) mustEqual Some(VehicleLockState.Empire)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Passenger.id) mustEqual Some(VehicleLockState.Empire)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Trunk.id) mustEqual Some(VehicleLockState.Locked)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id).contains(VehicleLockState.Locked)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Gunner.id).contains(VehicleLockState.Empire)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Passenger.id).contains(VehicleLockState.Empire)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Trunk.id).contains(VehicleLockState.Locked)
|
||||
}
|
||||
|
||||
"set new permission level" in {
|
||||
val fury_vehicle = Vehicle(GlobalDefinitions.fury)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id) mustEqual Some(VehicleLockState.Locked)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id, VehicleLockState.Group.id) mustEqual Some(VehicleLockState.Group)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id).contains(VehicleLockState.Locked)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id, VehicleLockState.Group.id).contains(VehicleLockState.Group)
|
||||
}
|
||||
|
||||
"set the same permission level" in {
|
||||
val fury_vehicle = Vehicle(GlobalDefinitions.fury)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id) mustEqual Some(VehicleLockState.Locked)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id, VehicleLockState.Locked.id) mustEqual None
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id).contains(VehicleLockState.Locked)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id, VehicleLockState.Locked.id).isEmpty mustEqual true
|
||||
}
|
||||
|
||||
"alternate permission level indices" in {
|
||||
|
|
@ -226,15 +229,15 @@ class VehicleTest extends Specification {
|
|||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Trunk.id) mustEqual fury_vehicle.PermissionGroup(13)
|
||||
|
||||
(AccessPermissionGroup.Driver.id + 10) mustEqual 10
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id, VehicleLockState.Group.id) mustEqual Some(VehicleLockState.Group)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id, VehicleLockState.Group.id).contains(VehicleLockState.Group)
|
||||
fury_vehicle.PermissionGroup(AccessPermissionGroup.Driver.id) mustEqual fury_vehicle.PermissionGroup(10)
|
||||
}
|
||||
|
||||
"can determine permission group from seat" in {
|
||||
val harasser_vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
harasser_vehicle.SeatPermissionGroup(0) mustEqual Some(AccessPermissionGroup.Driver)
|
||||
harasser_vehicle.SeatPermissionGroup(1) mustEqual Some(AccessPermissionGroup.Gunner)
|
||||
harasser_vehicle.SeatPermissionGroup(2) mustEqual None
|
||||
harasser_vehicle.SeatPermissionGroup(0).contains(AccessPermissionGroup.Driver)
|
||||
harasser_vehicle.SeatPermissionGroup(1).contains(AccessPermissionGroup.Gunner)
|
||||
harasser_vehicle.SeatPermissionGroup(2).isEmpty mustEqual true
|
||||
//TODO test for AccessPermissionGroup.Passenger later
|
||||
}
|
||||
|
||||
|
|
@ -247,11 +250,11 @@ class VehicleTest extends Specification {
|
|||
harasser_vehicle.Seat(0).get.Occupant = player1 //don't worry about ownership for now
|
||||
harasser_vehicle.Seat(1).get.Occupant = player2
|
||||
|
||||
harasser_vehicle.PassengerInSeat(player1) mustEqual Some(0)
|
||||
harasser_vehicle.PassengerInSeat(player2) mustEqual Some(1)
|
||||
harasser_vehicle.PassengerInSeat(player1).contains(0)
|
||||
harasser_vehicle.PassengerInSeat(player2).contains(1)
|
||||
harasser_vehicle.Seat(0).get.Occupant = None
|
||||
harasser_vehicle.PassengerInSeat(player1) mustEqual None
|
||||
harasser_vehicle.PassengerInSeat(player2) mustEqual Some(1)
|
||||
harasser_vehicle.PassengerInSeat(player1).isEmpty mustEqual true
|
||||
harasser_vehicle.PassengerInSeat(player2).contains(1)
|
||||
}
|
||||
|
||||
"can find a weapon controlled from seat" in {
|
||||
|
|
@ -259,7 +262,7 @@ class VehicleTest extends Specification {
|
|||
val chaingun_p = harasser_vehicle.Weapons(2).Equipment
|
||||
chaingun_p.isDefined mustEqual true
|
||||
|
||||
harasser_vehicle.WeaponControlledFromSeat(0) mustEqual None
|
||||
harasser_vehicle.WeaponControlledFromSeat(0).isEmpty mustEqual true
|
||||
harasser_vehicle.WeaponControlledFromSeat(1) mustEqual chaingun_p
|
||||
}
|
||||
|
||||
|
|
@ -273,12 +276,12 @@ class VehicleTest extends Specification {
|
|||
obj.Utilities.size mustEqual 3
|
||||
obj.Utilities(-1).UtilType mustEqual UtilityType.order_terminala
|
||||
obj.Utilities(0).UtilType mustEqual UtilityType.order_terminalb
|
||||
obj.Utilities.get(1) mustEqual None
|
||||
obj.Utilities.get(1).isEmpty mustEqual true
|
||||
obj.Utilities(2).UtilType mustEqual UtilityType.order_terminalb
|
||||
|
||||
val filteredMap = Vehicle.EquipmentUtilities(obj.Utilities)
|
||||
filteredMap.size mustEqual 2
|
||||
filteredMap.get(-1) mustEqual None
|
||||
filteredMap.get(-1).isEmpty mustEqual true
|
||||
filteredMap(0).UtilType mustEqual UtilityType.order_terminalb
|
||||
filteredMap(2).UtilType mustEqual UtilityType.order_terminalb
|
||||
}
|
||||
|
|
@ -303,7 +306,7 @@ class VehicleTest extends Specification {
|
|||
val harasser_vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
harasser_vehicle.Weapons(2).Equipment.get.GUID = PlanetSideGUID(10)
|
||||
|
||||
harasser_vehicle.Find(PlanetSideGUID(10)) mustEqual Some(2)
|
||||
harasser_vehicle.Find(PlanetSideGUID(10)).contains(2)
|
||||
}
|
||||
|
||||
"find items in its trunk by GUID" in {
|
||||
|
|
@ -312,101 +315,284 @@ class VehicleTest extends Specification {
|
|||
ammobox.GUID = PlanetSideGUID(10)
|
||||
harasser_vehicle.Inventory += 30 -> ammobox
|
||||
|
||||
harasser_vehicle.Find(PlanetSideGUID(10)) mustEqual Some(30)
|
||||
harasser_vehicle.Find(PlanetSideGUID(10)).contains(30)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleControlStopMountingTest extends ActorTest {
|
||||
"Vehicle Control" should {
|
||||
"deactivate and stop handling mount messages" in {
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(VehicleTest.avatar2)
|
||||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.Faction = PlanetSideEmpire.TR
|
||||
vehicle.GUID = PlanetSideGUID(3)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
VehicleEvents = new TestProbe(system).ref //necessary
|
||||
}
|
||||
vehicle.Weapons(2).Equipment.get.GUID = PlanetSideGUID(4)
|
||||
val probe = new TestProbe(system)
|
||||
|
||||
vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref)
|
||||
val reply = probe.receiveOne(Duration.create(200, "ms"))
|
||||
assert(reply.isInstanceOf[Mountable.MountMessages])
|
||||
|
||||
vehicle.Actor.tell(Vehicle.PrepareForDeletion(), probe.ref)
|
||||
vehicle.Actor.tell(Mountable.TryMount(player2, 1), probe.ref)
|
||||
probe.expectNoMsg(Duration.create(200, "ms")) //assertion failed: received unexpected message MountMessages(CanMount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleControlRestartMountingTest extends ActorTest {
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(VehicleTest.avatar2)
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
class VehicleControlPrepareForDeletionTest extends ActorTest {
|
||||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.Faction = PlanetSideEmpire.TR
|
||||
vehicle.GUID = PlanetSideGUID(3)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
val vehicleProbe = new TestProbe(system)
|
||||
vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
VehicleEvents = new TestProbe(system).ref
|
||||
VehicleEvents = vehicleProbe.ref
|
||||
}
|
||||
vehicle.Weapons(2).Equipment.get.GUID = PlanetSideGUID(4)
|
||||
val probe = new TestProbe(system)
|
||||
|
||||
"Vehicle Control" should {
|
||||
"reactivate and resume handling mount messages" in {
|
||||
vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref)
|
||||
probe.receiveOne(Duration.create(200, "ms")) //discard
|
||||
vehicle.Actor.tell(Vehicle.PrepareForDeletion(), probe.ref)
|
||||
vehicle.Actor.tell(Mountable.TryMount(player2, 1), probe.ref)
|
||||
probe.expectNoMsg(Duration.create(200, "ms"))
|
||||
vehicle.GUID = PlanetSideGUID(1)
|
||||
expectNoMsg(200 milliseconds)
|
||||
|
||||
vehicle.Actor.tell(Vehicle.Reactivate(), probe.ref)
|
||||
vehicle.Actor.tell(Mountable.TryMount(player2, 1), probe.ref)
|
||||
val reply = probe.receiveOne(Duration.create(200, "ms"))
|
||||
assert(reply.isInstanceOf[Mountable.MountMessages])
|
||||
"VehicleControl" should {
|
||||
"submit for unregistering when marked for deconstruction" in {
|
||||
vehicle.Actor ! Vehicle.Deconstruct()
|
||||
|
||||
val vehicle_msg = vehicleProbe.receiveN(1, 500 milliseconds)
|
||||
assert(
|
||||
vehicle_msg.head match {
|
||||
case VehicleServiceMessage.Decon(RemoverActor.AddTask(v, z, _)) => (v eq vehicle) && (z == vehicle.Zone)
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
|
||||
val vehicle_msg_final = vehicleProbe.receiveN(1, 6 seconds)
|
||||
assert(
|
||||
vehicle_msg_final.head match {
|
||||
case VehicleServiceMessage("test", VehicleAction.UnloadVehicle(_, z, v, PlanetSideGUID(1))) => (v eq vehicle) && (z == vehicle.Zone)
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleControlAlwaysDismountTest extends ActorTest {
|
||||
val probe = new TestProbe(system)
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
player1.GUID = PlanetSideGUID(1)
|
||||
val player2 = Player(VehicleTest.avatar2)
|
||||
player2.GUID = PlanetSideGUID(2)
|
||||
class VehicleControlPrepareForDeletionPassengerTest extends ActorTest {
|
||||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.Faction = PlanetSideEmpire.TR
|
||||
vehicle.GUID = PlanetSideGUID(3)
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test")
|
||||
val vehicleProbe = new TestProbe(system)
|
||||
vehicle.Zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
VehicleEvents = new TestProbe(system).ref
|
||||
VehicleEvents = vehicleProbe.ref
|
||||
}
|
||||
vehicle.Weapons(2).Equipment.get.GUID = PlanetSideGUID(4)
|
||||
val player1 = Player(VehicleTest.avatar1)
|
||||
|
||||
"Vehicle Control" should {
|
||||
"always allow dismount messages" in {
|
||||
vehicle.Actor.tell(Mountable.TryMount(player1, 0), probe.ref)
|
||||
probe.receiveOne(Duration.create(100, "ms")) //discard
|
||||
vehicle.Actor.tell(Mountable.TryMount(player2, 1), probe.ref)
|
||||
probe.receiveOne(Duration.create(100, "ms")) //discard
|
||||
vehicle.GUID = PlanetSideGUID(1)
|
||||
player1.GUID = PlanetSideGUID(2)
|
||||
vehicle.Seats(1).Occupant = player1 //passenger seat
|
||||
player1.VehicleSeated = vehicle.GUID
|
||||
expectNoMsg(200 milliseconds)
|
||||
|
||||
vehicle.Actor.tell(Mountable.TryDismount(player2, 1), probe.ref) //player2 requests dismount
|
||||
val reply1 = probe.receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply1.isInstanceOf[Mountable.MountMessages])
|
||||
assert(reply1.asInstanceOf[Mountable.MountMessages].response.isInstanceOf[Mountable.CanDismount]) //player2 dismounts
|
||||
"VehicleControl" should {
|
||||
"kick all players when marked for deconstruction" in {
|
||||
vehicle.Actor ! Vehicle.Deconstruct()
|
||||
|
||||
vehicle.Actor.tell(Vehicle.PrepareForDeletion(), probe.ref)
|
||||
vehicle.Actor.tell(Mountable.TryDismount(player1, 0), probe.ref) //player1 requests dismount
|
||||
val reply2 = probe.receiveOne(Duration.create(100, "ms"))
|
||||
assert(reply2.isInstanceOf[Mountable.MountMessages])
|
||||
assert(reply2.asInstanceOf[Mountable.MountMessages].response.isInstanceOf[Mountable.CanDismount]) //player1 dismounts
|
||||
val vehicle_msg = vehicleProbe.receiveN(2, 500 milliseconds)
|
||||
assert(
|
||||
vehicle_msg.head match {
|
||||
case VehicleServiceMessage("test", VehicleAction.KickPassenger(PlanetSideGUID(2), 4, false, PlanetSideGUID(1))) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(player1.VehicleSeated.isEmpty)
|
||||
assert(vehicle.Seats(1).Occupant.isEmpty)
|
||||
assert(
|
||||
vehicle_msg(1) match {
|
||||
case VehicleServiceMessage.Decon(RemoverActor.AddTask(v, z, _)) => (v eq vehicle) && (z == vehicle.Zone)
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleControlPrepareForDeletionMountedInTest extends FreedContextActorTest {
|
||||
ServiceManager.boot
|
||||
val guid = new NumberPoolHub(new LimitedNumberSource(10))
|
||||
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
GUID(guid)
|
||||
override def SetupNumberPools() : Unit = { }
|
||||
}
|
||||
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-zone-actor")
|
||||
zone.Init(context)
|
||||
|
||||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.Faction = PlanetSideEmpire.TR
|
||||
vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "vehicle-test-cargo")
|
||||
vehicle.Zone = zone
|
||||
val lodestar = Vehicle(GlobalDefinitions.lodestar)
|
||||
lodestar.Faction = PlanetSideEmpire.TR
|
||||
val player1 = Player(VehicleTest.avatar1) //name="test1"
|
||||
val player2 = Player(VehicleTest.avatar2) //name="test2"
|
||||
|
||||
guid.register(vehicle, 1)
|
||||
guid.register(lodestar, 2)
|
||||
player1.GUID = PlanetSideGUID(3)
|
||||
var utilityId = 10
|
||||
lodestar.Utilities.values.foreach { util =>
|
||||
util().GUID = PlanetSideGUID(utilityId)
|
||||
utilityId += 1
|
||||
}
|
||||
vehicle.Seats(1).Occupant = player1 //passenger seat
|
||||
player1.VehicleSeated = vehicle.GUID
|
||||
lodestar.Seats(0).Occupant = player2
|
||||
player2.VehicleSeated = lodestar.GUID
|
||||
lodestar.CargoHolds(1).Occupant = vehicle
|
||||
vehicle.MountedIn = lodestar.GUID
|
||||
|
||||
val vehicleProbe = new TestProbe(system)
|
||||
zone.VehicleEvents = vehicleProbe.ref
|
||||
zone.Transport ! Zone.Vehicle.Spawn(lodestar) //can not fake this
|
||||
expectNoMsg(200 milliseconds)
|
||||
|
||||
"VehicleControl" should {
|
||||
"if mounted as cargo, self-eject when marked for deconstruction" in {
|
||||
vehicle.Actor ! Vehicle.Deconstruct()
|
||||
|
||||
val vehicle_msg = vehicleProbe.receiveN(7, 500 milliseconds)
|
||||
//dismounting as cargo messages
|
||||
assert(
|
||||
vehicle_msg.head match {
|
||||
case VehicleServiceMessage(_, VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 0, _))) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
vehicle_msg(1) match {
|
||||
case VehicleServiceMessage(_, VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 68, _))) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
vehicle_msg(2) match {
|
||||
case VehicleServiceMessage("test", VehicleAction.SendResponse(_,
|
||||
CargoMountPointStatusMessage(PlanetSideGUID(2), _, PlanetSideGUID(1), _, 1, CargoStatus.InProgress, 0)
|
||||
)) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
vehicle_msg(3) match {
|
||||
case VehicleServiceMessage("test", VehicleAction.SendResponse(_,
|
||||
ObjectDetachMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _, _, _)
|
||||
)) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
vehicle_msg(4) match {
|
||||
case VehicleServiceMessage("test", VehicleAction.SendResponse(_,
|
||||
CargoMountPointStatusMessage(PlanetSideGUID(2), _, _, PlanetSideGUID(1), 1, CargoStatus.Empty, 0)
|
||||
)) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
//dismounting as cargo messages
|
||||
//TODO: does not actually kick out the cargo, but instigates the process
|
||||
assert(
|
||||
vehicle_msg(5) match {
|
||||
case VehicleServiceMessage("test", VehicleAction.KickPassenger(PlanetSideGUID(3), 4, false, PlanetSideGUID(1))) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(player1.VehicleSeated.isEmpty)
|
||||
assert(vehicle.Seats(1).Occupant.isEmpty)
|
||||
assert(
|
||||
vehicle_msg(6) match {
|
||||
case VehicleServiceMessage.Decon(RemoverActor.AddTask(v, z, _)) => (v eq vehicle) && (z == vehicle.Zone)
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VehicleControlPrepareForDeletionMountedCargoTest extends FreedContextActorTest {
|
||||
val guid = new NumberPoolHub(new LimitedNumberSource(10))
|
||||
ServiceManager.boot
|
||||
val zone = new Zone("test", new ZoneMap("test"), 0) {
|
||||
GUID(guid)
|
||||
override def SetupNumberPools() : Unit = { }
|
||||
}
|
||||
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "test-zone-actor")
|
||||
zone.Init(context)
|
||||
|
||||
val vehicle = Vehicle(GlobalDefinitions.two_man_assault_buggy)
|
||||
vehicle.Faction = PlanetSideEmpire.TR
|
||||
vehicle.Zone = zone
|
||||
val cargoProbe = new TestProbe(system)
|
||||
vehicle.Actor = cargoProbe.ref
|
||||
val lodestar = Vehicle(GlobalDefinitions.lodestar)
|
||||
lodestar.Faction = PlanetSideEmpire.TR
|
||||
val player1 = Player(VehicleTest.avatar1) //name="test1"
|
||||
val player2 = Player(VehicleTest.avatar2) //name="test2"
|
||||
|
||||
guid.register(vehicle, 1)
|
||||
guid.register(lodestar, 2)
|
||||
player1.GUID = PlanetSideGUID(3)
|
||||
player2.GUID = PlanetSideGUID(4)
|
||||
var utilityId = 10
|
||||
lodestar.Utilities.values.foreach { util =>
|
||||
util().GUID = PlanetSideGUID(utilityId)
|
||||
utilityId += 1
|
||||
}
|
||||
vehicle.Seats(1).Occupant = player1 //passenger seat
|
||||
player1.VehicleSeated = vehicle.GUID
|
||||
lodestar.Seats(0).Occupant = player2
|
||||
player2.VehicleSeated = lodestar.GUID
|
||||
lodestar.CargoHolds(1).Occupant = vehicle
|
||||
vehicle.MountedIn = lodestar.GUID
|
||||
|
||||
val vehicleProbe = new TestProbe(system)
|
||||
zone.VehicleEvents = vehicleProbe.ref
|
||||
zone.Transport ! Zone.Vehicle.Spawn(lodestar) //can not fake this
|
||||
expectNoMsg(200 milliseconds)
|
||||
|
||||
"VehicleControl" should {
|
||||
"if with mounted cargo, eject it when marked for deconstruction" in {
|
||||
lodestar.Actor ! Vehicle.Deconstruct()
|
||||
|
||||
val vehicle_msg = vehicleProbe.receiveN(7, 500 milliseconds)
|
||||
assert(
|
||||
vehicle_msg.head match {
|
||||
case VehicleServiceMessage("test", VehicleAction.KickPassenger(PlanetSideGUID(4), 4, false, PlanetSideGUID(2))) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(player2.VehicleSeated.isEmpty)
|
||||
assert(lodestar.Seats(0).Occupant.isEmpty)
|
||||
//cargo dismounting messages
|
||||
assert(
|
||||
vehicle_msg(1) match {
|
||||
case VehicleServiceMessage(_, VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 0, _))) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
vehicle_msg(2) match {
|
||||
case VehicleServiceMessage(_, VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 68, _))) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
vehicle_msg(3) match {
|
||||
case VehicleServiceMessage("test", VehicleAction.SendResponse(_,
|
||||
CargoMountPointStatusMessage(PlanetSideGUID(2), _, PlanetSideGUID(1), _, 1, CargoStatus.InProgress, 0)
|
||||
)) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
vehicle_msg(4) match {
|
||||
case VehicleServiceMessage("test", VehicleAction.SendResponse(_,
|
||||
ObjectDetachMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _, _, _)
|
||||
)) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
assert(
|
||||
vehicle_msg(5) match {
|
||||
case VehicleServiceMessage("test", VehicleAction.SendResponse(_,
|
||||
CargoMountPointStatusMessage(PlanetSideGUID(2), _, _, PlanetSideGUID(1), 1, CargoStatus.Empty, 0)
|
||||
)) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
//cargo dismounting messages
|
||||
assert(
|
||||
vehicle_msg(6) match {
|
||||
case VehicleServiceMessage.Decon(RemoverActor.AddTask(v, z, _)) => (v eq lodestar) && (z == vehicle.Zone)
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1400,14 +1400,14 @@ class WorldSessionActor extends Actor
|
|||
else {
|
||||
inZone.GUID(p.VehicleSeated) match {
|
||||
case Some(v : Vehicle) if v.Destroyed =>
|
||||
inZone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(v), inZone))
|
||||
inZone.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.AddTask(v, inZone, if(v.Flying) {
|
||||
v.Actor ! Vehicle.Deconstruct(
|
||||
if(v.Flying) {
|
||||
//TODO gravity
|
||||
Some(0 seconds) //immediate deconstruction
|
||||
None //immediate deconstruction
|
||||
}
|
||||
else {
|
||||
v.Definition.DeconstructionTime //normal deconstruction
|
||||
}))
|
||||
})
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
|
|
@ -2283,7 +2283,6 @@ class WorldSessionActor extends Actor
|
|||
sendResponse(PlanetsideAttributeMessage(obj_guid, 113, capacitor))
|
||||
}
|
||||
if(seat_num == 0) {
|
||||
continent.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(obj), continent)) //clear timer
|
||||
//simplistic vehicle ownership management
|
||||
obj.Owner match {
|
||||
case Some(owner_guid) =>
|
||||
|
|
@ -2336,8 +2335,6 @@ class WorldSessionActor extends Actor
|
|||
case Mountable.CanDismount(obj : Vehicle, seat_num) if obj.Definition == GlobalDefinitions.droppod =>
|
||||
UnAccessContents(obj)
|
||||
DismountAction(tplayer, obj, seat_num)
|
||||
continent.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(obj), continent))
|
||||
continent.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.AddTask(obj, continent, obj.Definition.DeconstructionTime))
|
||||
|
||||
case Mountable.CanDismount(obj : Vehicle, seat_num) =>
|
||||
val player_guid : PlanetSideGUID = tplayer.GUID
|
||||
|
|
@ -3007,9 +3004,8 @@ class WorldSessionActor extends Actor
|
|||
)
|
||||
}
|
||||
|
||||
case msg@VehicleResponse.KickPassenger(seat_num, wasKickedByDriver, vehicle_guid) =>
|
||||
case VehicleResponse.KickPassenger(seat_num, wasKickedByDriver, vehicle_guid) =>
|
||||
// seat_num seems to be correct if passenger is kicked manually by driver, but always seems to return 4 if user is kicked by seat permissions
|
||||
log.info(s"$msg")
|
||||
sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver))
|
||||
if(tplayer_guid == guid) {
|
||||
continent.GUID(vehicle_guid) match {
|
||||
|
|
@ -3100,9 +3096,6 @@ class WorldSessionActor extends Actor
|
|||
galaxyService ! Service.Join(temp_channel) //temporary vehicle-specific channel
|
||||
}
|
||||
|
||||
case VehicleResponse.ForceDismountVehicleCargo(cargo_guid, bailed, requestedByPassenger, kicked) =>
|
||||
CargoBehavior.HandleVehicleCargoDismount(continent, tplayer_guid, cargo_guid, bailed, requestedByPassenger, kicked)
|
||||
|
||||
case VehicleResponse.KickCargo(vehicle, speed, delay) =>
|
||||
if(player.VehicleSeated.nonEmpty && deadState == DeadState.Alive) {
|
||||
if(speed > 0) {
|
||||
|
|
@ -3114,7 +3107,7 @@ class WorldSessionActor extends Actor
|
|||
controlled = Some(reverseSpeed)
|
||||
sendResponse(ServerVehicleOverrideMsg(true, true, true, false, 0, strafe, reverseSpeed, Some(0)))
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
context.system.scheduler.scheduleOnce(delay milliseconds, self, VehicleServiceResponse(toChannel, tplayer_guid, VehicleResponse.KickCargo(vehicle, 0, delay)))
|
||||
context.system.scheduler.scheduleOnce(delay milliseconds, self, VehicleServiceResponse(toChannel, PlanetSideGUID(0), VehicleResponse.KickCargo(vehicle, 0, delay)))
|
||||
}
|
||||
else {
|
||||
controlled = None
|
||||
|
|
@ -3650,7 +3643,7 @@ class WorldSessionActor extends Actor
|
|||
case Some(cargo : Vehicle) if !requestedByPassenger =>
|
||||
continent.GUID(cargo.MountedIn) match {
|
||||
case Some(carrier : Vehicle) =>
|
||||
CargoBehavior.HandleVehicleCargoDismount(continent, player_guid, cargo_guid, bailed, requestedByPassenger, kicked)
|
||||
CargoBehavior.HandleVehicleCargoDismount(continent, cargo_guid, bailed, requestedByPassenger, kicked)
|
||||
case _ => ;
|
||||
}
|
||||
case _ => ;
|
||||
|
|
@ -4218,8 +4211,7 @@ class WorldSessionActor extends Actor
|
|||
case v : Vehicle if seatNum == 0 && v.Flying =>
|
||||
TotalDriverVehicleControl(v)
|
||||
UnAccessContents(v)
|
||||
continent.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(obj), continent))
|
||||
continent.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.AddTask(obj, continent, Some(0 seconds)))
|
||||
v.Actor ! Vehicle.Deconstruct()
|
||||
case _ => ;
|
||||
}
|
||||
case _ => ; //found no vehicle where one was expected; since we're dead, let's not dwell on it
|
||||
|
|
@ -5035,8 +5027,7 @@ class WorldSessionActor extends Actor
|
|||
if((player.VehicleOwned.contains(object_guid) && vehicle.Owner.contains(player.GUID)) ||
|
||||
(player.Faction == vehicle.Faction &&
|
||||
((vehicle.Owner.isEmpty || continent.GUID(vehicle.Owner.get).isEmpty) || vehicle.Destroyed))) {
|
||||
continent.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(vehicle), continent))
|
||||
continent.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.AddTask(vehicle, continent, Some(0 seconds)))
|
||||
vehicle.Actor ! Vehicle.Deconstruct()
|
||||
log.info(s"RequestDestroy: vehicle $vehicle")
|
||||
}
|
||||
else {
|
||||
|
|
@ -6024,8 +6015,7 @@ class WorldSessionActor extends Actor
|
|||
//todo: kick cargo passengers out. To be added after PR #216 is merged
|
||||
obj match {
|
||||
case v : Vehicle if bailType == BailType.Bailed && seat_num == 0 && v.Flying =>
|
||||
continent.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(obj), continent))
|
||||
continent.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.AddTask(obj, continent, Some(0 seconds))) // Immediately deconstruct vehicle
|
||||
v.Actor ! Vehicle.Deconstruct() // Immediately deconstruct vehicle
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
|
|
@ -9724,7 +9714,7 @@ class WorldSessionActor extends Actor
|
|||
case ("MISSING_DRIVER", index) =>
|
||||
val cargo = vehicle.CargoHolds(index).Occupant.get
|
||||
log.error(s"LoadZoneInVehicleAsDriver: eject cargo in hold $index; vehicle missing driver")
|
||||
CargoBehavior.HandleVehicleCargoDismount(pguid, cargo.GUID, cargo, vehicle.GUID, vehicle, false, false, true)
|
||||
CargoBehavior.HandleVehicleCargoDismount(cargo.GUID, cargo, vehicle.GUID, vehicle, false, false, true)
|
||||
case (name, index) =>
|
||||
val cargo = vehicle.CargoHolds(index).Occupant.get
|
||||
continent.VehicleEvents ! VehicleServiceMessage(name, VehicleAction.TransferPassengerChannel(pguid, s"${cargo.Actor}", toChannel, cargo, topLevel))
|
||||
|
|
@ -10016,8 +10006,6 @@ class WorldSessionActor extends Actor
|
|||
sendResponse(PlayerStateShiftMessage(ShiftState(0, dest.Position, player.Orientation.z)))
|
||||
UseRouterTelepadEffect(pguid, sguid, dguid)
|
||||
StopBundlingPackets()
|
||||
// continent.VehicleEvents ! VehicleServiceMessage.Decon(RemoverActor.ClearSpecific(List(router), continent))
|
||||
// continent.VehicleEvents p! VehicleServiceMessage.Decon(RemoverActor.AddTask(router, continent, router.Definition.DeconstructionTime))
|
||||
continent.LocalEvents ! LocalServiceMessage(continent.Id, LocalAction.RouterTelepadTransport(pguid, pguid, sguid, dguid))
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import net.psforever.objects.serverobject.structures.StructureType
|
|||
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.types.{PlanetSideGUID, _}
|
||||
import services.RemoverActor
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -107,7 +108,13 @@ class VehicleSpawnControl4Test extends ActorTest {
|
|||
|
||||
pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle) //order
|
||||
|
||||
probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.DisposeVehicle])
|
||||
val msg = probe.receiveOne(1 minute)
|
||||
assert(
|
||||
msg match {
|
||||
case VehicleServiceMessage.Decon(RemoverActor.AddTask(v, z , _)) => (v == vehicle) && (z == zone)
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
probe.expectNoMsg(5 seconds)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue