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:
Fate-JH 2020-04-27 21:22:59 -04:00 committed by GitHub
parent 58134e02fa
commit 9f12cfa625
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 553 additions and 346 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 _ => ;
}
}

View file

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

View file

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

View file

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

View file

@ -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 _ => ;
}
}

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

View file

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

View file

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

View file

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

View file

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