diff --git a/common/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala b/common/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala
index cfa37ead..48f62514 100644
--- a/common/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala
+++ b/common/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala
@@ -45,7 +45,7 @@ class ExplosiveDeployableDefinition(private val objectId : Int) extends ComplexD
}
override def Initialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) = {
- obj.Actor = context.actorOf(Props(classOf[ExplosiveDeployableControl], obj), s"${obj.Definition.Name}_${obj.GUID.guid}")
+ obj.Actor = context.actorOf(Props(classOf[ExplosiveDeployableControl], obj), PlanetSideServerObject.UniqueActorName(obj))
}
override def Uninitialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) = {
diff --git a/common/src/main/scala/net/psforever/objects/SensorDeployable.scala b/common/src/main/scala/net/psforever/objects/SensorDeployable.scala
index 18d9d075..22b35ab2 100644
--- a/common/src/main/scala/net/psforever/objects/SensorDeployable.scala
+++ b/common/src/main/scala/net/psforever/objects/SensorDeployable.scala
@@ -30,7 +30,7 @@ class SensorDeployableDefinition(private val objectId : Int) extends ComplexDepl
Packet = new SmallDeployableConverter
override def Initialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) = {
- obj.Actor = context.actorOf(Props(classOf[SensorDeployableControl], obj), s"${obj.Definition.Name}_${obj.GUID.guid}")
+ obj.Actor = context.actorOf(Props(classOf[SensorDeployableControl], obj), PlanetSideServerObject.UniqueActorName(obj))
}
override def Uninitialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) = {
@@ -126,7 +126,7 @@ object SensorDeployableControl {
target.Actor ! JammableUnit.ClearJammeredSound()
target.Actor ! JammableUnit.ClearJammeredStatus()
val zone = target.Zone
- Deployables.AnnounceDestroyDeployable(target, Some(0 seconds))
+ Deployables.AnnounceDestroyDeployable(target, None)
zone.LocalEvents ! LocalServiceMessage(zone.Id, LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", target.GUID, false, 1000))
zone.AvatarEvents ! AvatarServiceMessage(zone.Id, AvatarAction.Destroy(target.GUID, attribution, attribution, target.Position))
}
diff --git a/common/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala b/common/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala
index 15bbc408..ff32b419 100644
--- a/common/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala
+++ b/common/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala
@@ -25,7 +25,7 @@ class ShieldGeneratorDefinition extends ComplexDeployableDefinition(240) {
DeployCategory = DeployableCategory.ShieldGenerators
override def Initialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) = {
- obj.Actor = context.actorOf(Props(classOf[ShieldGeneratorControl], obj), s"${obj.Definition.Name}_${obj.GUID.guid}")
+ obj.Actor = context.actorOf(Props(classOf[ShieldGeneratorControl], obj), PlanetSideServerObject.UniqueActorName(obj))
}
override def Uninitialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) = {
diff --git a/common/src/main/scala/net/psforever/objects/TurretDeployable.scala b/common/src/main/scala/net/psforever/objects/TurretDeployable.scala
index f7c379f3..273acf40 100644
--- a/common/src/main/scala/net/psforever/objects/TurretDeployable.scala
+++ b/common/src/main/scala/net/psforever/objects/TurretDeployable.scala
@@ -49,7 +49,7 @@ class TurretDeployableDefinition(private val objectId : Int) extends ComplexDepl
override def MaxHealth_=(max : Int) : Int = super[ComplexDeployableDefinition].MaxHealth_=(max)
override def Initialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) = {
- obj.Actor = context.actorOf(Props(classOf[TurretControl], obj), s"${obj.Definition.Name}_${obj.GUID.guid}")
+ obj.Actor = context.actorOf(Props(classOf[TurretControl], obj), PlanetSideServerObject.UniqueActorName(obj))
}
override def Uninitialize(obj : PlanetSideServerObject with Deployable, context : ActorContext) = {
diff --git a/common/src/main/scala/net/psforever/objects/ce/TelepadLike.scala b/common/src/main/scala/net/psforever/objects/ce/TelepadLike.scala
index 8ce79ee8..f0566a12 100644
--- a/common/src/main/scala/net/psforever/objects/ce/TelepadLike.scala
+++ b/common/src/main/scala/net/psforever/objects/ce/TelepadLike.scala
@@ -2,6 +2,7 @@
package net.psforever.objects.ce
import akka.actor.ActorContext
+import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle}
import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.objects.vehicles.Utility
@@ -49,6 +50,10 @@ object TelepadLike {
*/
def Setup(obj : Amenity, context : ActorContext) : Unit = {
obj.asInstanceOf[TelepadLike].Router = obj.Owner.GUID
+ import akka.actor.{ActorRef, Props}
+ if(obj.Actor == ActorRef.noSender) {
+ obj.Actor = context.actorOf(Props(classOf[TelepadControl], obj), PlanetSideServerObject.UniqueActorName(obj))
+ }
}
/**
@@ -83,3 +88,16 @@ object TelepadLike {
}
}
}
+
+/**
+ * Telepad-like components don't actually use control agents right now, but,
+ * since the `trait` is used for a `Vehicle` `Utility` entity as well as a `Deployable` entity,
+ * and all utilities are supposed to have control agents with which to interface,
+ * a placeholder like this is easy to reason around.
+ * @param obj an entity that extends `TelepadLike`
+ */
+class TelepadControl(obj : TelepadLike) extends akka.actor.Actor {
+ def receive : akka.actor.Actor.Receive = {
+ case _ => ;
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala b/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala
index ab968aad..01085a38 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala
@@ -3,6 +3,7 @@ package net.psforever.objects.serverobject
import akka.actor.ActorRef
import net.psforever.objects.PlanetSideGameObject
+import net.psforever.objects.entity.NoGUIDException
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.zones.ZoneAware
@@ -34,3 +35,26 @@ abstract class PlanetSideServerObject extends PlanetSideGameObject
actor
}
}
+
+object PlanetSideServerObject {
+ /**
+ * `Actor` entities require unique names over the course of the lifetime of the `ActorSystem` object.
+ * To produce this name, a composition of three strings separated by underscores is assembled.
+ * - the entity's object name;
+ * uniqueness is very low, less than 50, but helps to identify the object
+ * - the entity's globally unique identifier number;
+ * its uniqueness is much greater but still falls within less than 66535 possible integers;
+ * useful for locating that entity;
+ * an `Exception` can be thrown if this field was never set,
+ * but that is a condition for which it is worth throwing the `Exception`
+ * - the current POSIX time in milliseconds;
+ * results will remain reasonably unique;
+ * useful for taking a rough estimation of how long the entity has existed
+ * @throws `NoGUIDException` if the entity has never been registered to a unique identifier system
+ * @param obj the entity for whom the `Actor` object will be created
+ * @return the unique name
+ */
+ def UniqueActorName(obj : PlanetSideGameObject) : String = {
+ s"${obj.Definition.Name}_${obj.GUID.guid}_${System.currentTimeMillis}"
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/MatrixTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/MatrixTerminalDefinition.scala
index a669de7f..b815ce2a 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/MatrixTerminalDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/MatrixTerminalDefinition.scala
@@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.terminals
import akka.actor.ActorContext
import net.psforever.objects.Player
+import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.structures.Amenity
/**
@@ -29,7 +30,7 @@ object MatrixTerminalDefinition {
def Setup(obj : Amenity, context : ActorContext) : Unit = {
import akka.actor.{ActorRef, Props}
if(obj.Actor == ActorRef.noSender) {
- obj.Actor = context.actorOf(Props(classOf[TerminalControl], obj), s"${obj.Definition.Name}_${obj.GUID.guid}")
+ obj.Actor = context.actorOf(Props(classOf[TerminalControl], obj), PlanetSideServerObject.UniqueActorName(obj))
}
}
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala
index 9c4e34d7..5ab86e44 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/OrderTerminalDefinition.scala
@@ -7,6 +7,7 @@ import net.psforever.objects.{Player, Vehicle}
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.loadouts.{InfantryLoadout, VehicleLoadout}
import net.psforever.objects.inventory.InventoryItem
+import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.packet.game.ItemTransactionMessage
import net.psforever.objects.serverobject.terminals.EquipmentTerminalDefinition._
@@ -341,7 +342,7 @@ object OrderTerminalDefinition {
def Setup(obj : Amenity, context : ActorContext) : Unit = {
import akka.actor.{ActorRef, Props}
if(obj.Actor == ActorRef.noSender) {
- obj.Actor = context.actorOf(Props(classOf[TerminalControl], obj), s"${obj.Definition.Name}_${obj.GUID.guid}")
+ obj.Actor = context.actorOf(Props(classOf[TerminalControl], obj), PlanetSideServerObject.UniqueActorName(obj))
}
}
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala
index fd5db2af..65ba0fd0 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala
@@ -2,7 +2,7 @@
package net.psforever.objects.serverobject.terminals
import net.psforever.objects.Player
-import net.psforever.objects.serverobject.CommonMessages
+import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.types.Vector3
import services.Service
@@ -76,7 +76,7 @@ object ProximityTerminal {
def Setup(obj : Amenity, context : ActorContext) : Unit = {
import akka.actor.{ActorRef, Props}
if(obj.Actor == ActorRef.noSender) {
- obj.Actor = context.actorOf(Props(classOf[ProximityTerminalControl], obj), s"${obj.Definition.Name}_${obj.GUID.guid}")
+ obj.Actor = context.actorOf(Props(classOf[ProximityTerminalControl], obj), PlanetSideServerObject.UniqueActorName(obj))
obj.Actor ! Service.Startup()
}
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeDefinition.scala
index 755f64a1..a78fb8dc 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeDefinition.scala
@@ -5,6 +5,7 @@ import akka.actor.ActorContext
import net.psforever.objects.SpawnPointDefinition
import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.definition.converter.SpawnTubeConverter
+import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.structures.Amenity
/**
@@ -25,7 +26,7 @@ object SpawnTubeDefinition {
def Setup(obj : Amenity, context : ActorContext) : Unit = {
import akka.actor.{ActorRef, Props}
if(obj.Actor == ActorRef.noSender) {
- obj.Actor = context.actorOf(Props(classOf[SpawnTubeControl], obj), s"${obj.Definition.Name}_${obj.GUID.guid}")
+ obj.Actor = context.actorOf(Props(classOf[SpawnTubeControl], obj), PlanetSideServerObject.UniqueActorName(obj))
}
}
}
diff --git a/common/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala b/common/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala
index 2e1a5f05..aa6c05be 100644
--- a/common/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala
+++ b/common/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala
@@ -67,7 +67,7 @@ class SquadFeatures(val Squad : Squad) {
private lazy val channel : String = s"${Squad.Faction}-Squad${Squad.GUID.guid}"
def Start(implicit context : ActorContext) : SquadFeatures = {
- switchboard = context.actorOf(Props[SquadSwitchboard], s"squad${Squad.GUID.guid}")
+ switchboard = context.actorOf(Props[SquadSwitchboard], s"squad_${Squad.GUID.guid}_${System.currentTimeMillis}")
waypoints = Array.fill[WaypointData](SquadWaypoints.values.size)(new WaypointData())
this
}
diff --git a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
index 1b0800f0..4451b8c5 100644
--- a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
+++ b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala
@@ -53,32 +53,13 @@ class VehicleControl(vehicle : Vehicle) extends Actor
def Enabled : Receive = checkBehavior
.orElse(deployBehavior)
- .orElse(dismountBehavior)
.orElse(jammableBehavior)
.orElse {
- case Mountable.TryMount(user, seat_num) =>
- val exosuit = user.ExoSuit
- val restriction = vehicle.Seats(seat_num).ArmorRestriction
- val seatGroup = vehicle.SeatPermissionGroup(seat_num).getOrElse(AccessPermissionGroup.Passenger)
- val permission = vehicle.PermissionGroup(seatGroup.id).getOrElse(VehicleLockState.Empire)
- if(
- (if(seatGroup == AccessPermissionGroup.Driver) {
- vehicle.Owner.contains(user.GUID) || vehicle.Owner.isEmpty || permission != VehicleLockState.Locked
- }
- else {
- permission != VehicleLockState.Locked
- }) &&
- (exosuit match {
- case ExoSuitType.MAX => restriction == SeatArmorRestriction.MaxOnly
- case ExoSuitType.Reinforced => restriction == SeatArmorRestriction.NoMax
- case _ => restriction != SeatArmorRestriction.MaxOnly
- })
- ) {
- mountBehavior.apply(Mountable.TryMount(user, seat_num))
- }
- else {
- sender ! Mountable.MountMessages(user, Mountable.CanNotMount(vehicle, seat_num))
- }
+ case msg : Mountable.TryMount =>
+ tryMountBehavior.apply(msg)
+
+ case msg : Mountable.TryDismount =>
+ dismountBehavior.apply(msg)
case Vitality.Damage(damage_func) =>
if(vehicle.Health > 0) {
@@ -122,9 +103,37 @@ class VehicleControl(vehicle : Vehicle) extends Actor
case _ => ;
}
+ val tryMountBehavior : Receive = {
+ case msg @ Mountable.TryMount(user, seat_num) =>
+ val exosuit = user.ExoSuit
+ val restriction = vehicle.Seats(seat_num).ArmorRestriction
+ val seatGroup = vehicle.SeatPermissionGroup(seat_num).getOrElse(AccessPermissionGroup.Passenger)
+ val permission = vehicle.PermissionGroup(seatGroup.id).getOrElse(VehicleLockState.Empire)
+ if(
+ (if(seatGroup == AccessPermissionGroup.Driver) {
+ vehicle.Owner.contains(user.GUID) || vehicle.Owner.isEmpty || permission != VehicleLockState.Locked
+ }
+ else {
+ permission != VehicleLockState.Locked
+ }) &&
+ (exosuit match {
+ case ExoSuitType.MAX => restriction == SeatArmorRestriction.MaxOnly
+ case ExoSuitType.Reinforced => restriction == SeatArmorRestriction.NoMax
+ case _ => restriction != SeatArmorRestriction.MaxOnly
+ })
+ ) {
+ mountBehavior.apply(msg)
+ }
+ else {
+ sender ! Mountable.MountMessages(user, Mountable.CanNotMount(vehicle, seat_num))
+ }
+ }
+
def Disabled : Receive = checkBehavior
- .orElse(dismountBehavior)
.orElse {
+ case msg : Mountable.TryDismount =>
+ dismountBehavior.apply(msg)
+
case Vehicle.Reactivate() =>
context.become(Enabled)
diff --git a/common/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala b/common/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala
index a77911b7..1b5364c8 100644
--- a/common/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala
+++ b/common/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala
@@ -39,13 +39,13 @@ class ZonePopulationActor(zone : Zone, playerMap : TrieMap[Avatar, Option[Player
case Zone.Population.Spawn(avatar, player) =>
PopulationSpawn(avatar, player, playerMap) match {
- case Some(tplayer) =>
+ case Some((tplayer, newToZone)) =>
tplayer.Zone = zone
if(tplayer ne player) {
sender ! Zone.Population.PlayerAlreadySpawned(zone, player)
}
- else {
- player.Actor = context.actorOf(Props(classOf[PlayerControl], player), s"${player.Name}_${player.GUID.guid}")
+ else if(newToZone) {
+ player.Actor = context.actorOf(Props(classOf[PlayerControl], player), s"${player.Name}_${player.GUID.guid}_${System.currentTimeMillis}")
player.Zone = zone
}
case None =>
@@ -110,19 +110,21 @@ object ZonePopulationActor {
* @param avatar an `Avatar` object
* @param player a `Player` object
* @param playerMap the mapping of `Avatar` objects to `Player` objects
- * @return the `Player` object that is associated with the `Avatar` key
+ * @return a `Tuple` object of the `Player` object that is associated with the `Avatar` key
+ * and whether that player was added to the zone for the first time;
+ * `None`, if the player should not be introduced to this zone at this time
*/
- def PopulationSpawn(avatar : Avatar, player : Player, playerMap : TrieMap[Avatar, Option[Player]]) : Option[Player] = {
+ def PopulationSpawn(avatar : Avatar, player : Player, playerMap : TrieMap[Avatar, Option[Player]]) : Option[(Player, Boolean)] = {
playerMap.get(avatar) match {
case None =>
None
case Some(tplayer) =>
tplayer match {
case Some(aplayer) =>
- Some(aplayer)
+ Some(aplayer, false)
case None =>
playerMap(avatar) = Some(player)
- Some(player)
+ Some(player, true)
}
}
}
diff --git a/common/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala b/common/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala
index 265de52b..efc2d01d 100644
--- a/common/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala
+++ b/common/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala
@@ -3,6 +3,7 @@ package net.psforever.objects.zones
import akka.actor.{Actor, ActorRef, Props}
import net.psforever.objects.Vehicle
+import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.vehicles.VehicleControl
import scala.annotation.tailrec
@@ -43,7 +44,7 @@ class ZoneVehicleActor(zone : Zone, vehicleList : ListBuffer[Vehicle]) extends A
else {
vehicleList += vehicle
vehicle.Zone = zone
- vehicle.Actor = context.actorOf(Props(classOf[VehicleControl], vehicle), s"${vehicle.Definition.Name}_${vehicle.GUID.guid}")
+ vehicle.Actor = context.actorOf(Props(classOf[VehicleControl], vehicle), PlanetSideServerObject.UniqueActorName(vehicle))
}
case Zone.Vehicle.Despawn(vehicle) =>
diff --git a/common/src/main/scala/services/vehicle/support/VehicleRemover.scala b/common/src/main/scala/services/vehicle/support/VehicleRemover.scala
index 17c7584b..131eaabb 100644
--- a/common/src/main/scala/services/vehicle/support/VehicleRemover.scala
+++ b/common/src/main/scala/services/vehicle/support/VehicleRemover.scala
@@ -22,52 +22,59 @@ class VehicleRemover extends RemoverActor {
def InitialJob(entry : RemoverActor.Entry) : Unit = { }
def FirstJob(entry : RemoverActor.Entry) : Unit = {
- val vehicle = entry.obj.asInstanceOf[Vehicle]
- val vehicleGUID = vehicle.GUID
- val zoneId = entry.zone.Id
- 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
+ val vehicleGUID = entry.obj.GUID
+ entry.zone.GUID(vehicleGUID) match {
+ case Some(vehicle : Vehicle) if vehicle.HasGUID =>
+ val zoneId = entry.zone.Id
+ 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 _ => ;
}
- context.parent ! VehicleServiceMessage(s"$driverName", VehicleAction.ForceDismountVehicleCargo(PlanetSideGUID(0), vehicleGUID, true, false, false))
+ //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 _ => ;
}
- //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))
- }
- })
}
override def SecondJob(entry : RemoverActor.Entry) : Unit = {
- val vehicle = entry.obj.asInstanceOf[Vehicle]
- 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, vehicle.GUID))
- super.SecondJob(entry)
+ 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 ClearanceTest(entry : RemoverActor.Entry) : Boolean = entry.obj.asInstanceOf[Vehicle].Seats.values.count(_.isOccupied) == 0
diff --git a/common/src/test/scala/objects/UtilityTest.scala b/common/src/test/scala/objects/UtilityTest.scala
index ecb8d376..dcdd50dd 100644
--- a/common/src/test/scala/objects/UtilityTest.scala
+++ b/common/src/test/scala/objects/UtilityTest.scala
@@ -217,7 +217,7 @@ class UtilityInternalTelepadTest extends ActorTest {
system.actorOf(Props(classOf[UtilityTest.SetupControl], obj), "test") ! ""
receiveOne(Duration.create(100, "ms")) //consume and discard
- assert(obj().Actor == ActorRef.noSender)
+ assert(obj().Actor != ActorRef.noSender)
assert(obj().asInstanceOf[Utility.InternalTelepad].Router.contains(veh.GUID))
}
}
diff --git a/common/src/test/scala/objects/number/UniqueNumberSystemTest.scala b/common/src/test/scala/objects/number/UniqueNumberSystemTest.scala
index 04107de8..b8629890 100644
--- a/common/src/test/scala/objects/number/UniqueNumberSystemTest.scala
+++ b/common/src/test/scala/objects/number/UniqueNumberSystemTest.scala
@@ -187,14 +187,14 @@ class UniqueNumberSystemTest5 extends ActorTest() {
assert(src.CountUsed == 0)
uns ! Register(testObj, "pool2")
- val msg1 = receiveOne(Duration.create(500, "ms"))
+ val msg1 = receiveOne(Duration.create(2000, "ms"))
assert(msg1.isInstanceOf[Success[_]])
assert(testObj.HasGUID)
assert(pool2.contains(testObj.GUID.guid))
assert(src.CountUsed == 1)
uns ! Unregister(testObj)
- val msg2 = receiveOne(Duration.create(500, "ms"))
+ val msg2 = receiveOne(Duration.create(2000, "ms"))
assert(msg2.isInstanceOf[Success[_]])
assert(!testObj.HasGUID)
assert(src.CountUsed == 0)
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index fb549bf2..fd603030 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -330,6 +330,21 @@ class WorldSessionActor extends Actor
context.stop(self)
}
+ def ValidObject(id : Int) : Option[PlanetSideGameObject] = ValidObject(Some(PlanetSideGUID(id)))
+
+ def ValidObject(id : PlanetSideGUID) : Option[PlanetSideGameObject] = ValidObject(Some(id))
+
+ def ValidObject(id : Option[PlanetSideGUID]) : Option[PlanetSideGameObject] = continent.GUID(id) match {
+ case out @ Some(obj) if obj.HasGUID =>
+ out
+ case None if id.nonEmpty =>
+ //delete stale entity reference from client
+ sendResponse(ObjectDeleteMessage(id.get, 0))
+ None
+ case _ =>
+ None
+ }
+
def Started : Receive = {
case ServiceManager.LookupResult("accountIntermediary", endpoint) =>
accountIntermediary = endpoint
@@ -3556,7 +3571,7 @@ class WorldSessionActor extends Actor
whenUsedLastMAXName(1) = faction+"hev_antiaircraft"
(0 until 4).foreach( index => {
- if (player.Slot(index).Equipment.isDefined) player.Slot(index).Equipment = None
+ player.Slot(index).Equipment = None
})
player.Inventory.Clear()
player.ExoSuit = ExoSuitType.Standard
@@ -4400,7 +4415,7 @@ class WorldSessionActor extends Actor
case msg @ DropItemMessage(item_guid) =>
log.info(s"DropItem: $msg")
- continent.GUID(item_guid) match {
+ ValidObject(item_guid) match {
case Some(anItem : Equipment) =>
player.FreeHand.Equipment match {
case Some(item) =>
@@ -4419,7 +4434,7 @@ class WorldSessionActor extends Actor
case msg @ PickupItemMessage(item_guid, player_guid, unk1, unk2) =>
log.info(s"PickupItem: $msg")
- continent.GUID(item_guid) match {
+ ValidObject(item_guid) match {
case Some(item : Equipment) =>
player.Fit(item) match {
case Some(_) =>
@@ -4541,7 +4556,7 @@ class WorldSessionActor extends Actor
case msg @ RequestDestroyMessage(object_guid) =>
// TODO: Make sure this is the correct response for all cases
- continent.GUID(object_guid) match {
+ ValidObject(object_guid) match {
case Some(vehicle : Vehicle) =>
if((player.VehicleOwned.contains(object_guid) && vehicle.Owner.contains(player.GUID))
|| (player.Faction == vehicle.Faction
@@ -4684,7 +4699,7 @@ class WorldSessionActor extends Actor
case msg @ LootItemMessage(item_guid, target_guid) =>
log.info(s"LootItem: $msg")
- (continent.GUID(item_guid), continent.GUID(target_guid)) match {
+ (ValidObject(item_guid), ValidObject(target_guid)) match {
case (Some(item : Equipment), Some(target : Container)) =>
//figure out the source
(
@@ -4747,7 +4762,7 @@ class WorldSessionActor extends Actor
//log.info("UseItem: " + msg)
// TODO: Not all fields in the response are identical to source in real packet logs (but seems to be ok)
// TODO: Not all incoming UseItemMessage's respond with another UseItemMessage (i.e. doors only send out GenericObjectStateMsg)
- continent.GUID(object_guid) match {
+ ValidObject(object_guid) match {
case Some(door : Door) =>
if(player.Faction == door.Faction || (continent.Map.DoorToLock.get(object_guid.guid) match {
case Some(lock_guid) =>
@@ -4821,7 +4836,7 @@ class WorldSessionActor extends Actor
accessedContainer = Some(obj)
}
else if(!unk3 && player.isAlive) { //potential kit use
- continent.GUID(item_used_guid) match {
+ ValidObject(item_used_guid) match {
case Some(kit : Kit) =>
player.Find(kit) match {
case Some(index) =>
@@ -4936,7 +4951,7 @@ class WorldSessionActor extends Actor
FindWeapon match {
case Some(tool: Tool) =>
if (tool.Definition == GlobalDefinitions.bank) {
- continent.GUID(object_guid) match {
+ ValidObject(object_guid) match {
case Some(tplayer: Player) =>
if (player.GUID != tplayer.GUID && Vector3.Distance(player.Position, tplayer.Position) < 5 && player.Faction == tplayer.Faction && !player.isMoving && tplayer.MaxArmor > 0 && tplayer.Armor < tplayer.MaxArmor) {
tplayer.Armor += 15
@@ -5262,7 +5277,7 @@ class WorldSessionActor extends Actor
case msg @ UnuseItemMessage(player_guid, object_guid) =>
log.info(s"UnuseItem: $msg")
//TODO check for existing accessedContainer value?
- continent.GUID(object_guid) match {
+ ValidObject(object_guid) match {
case Some(obj : Vehicle) =>
if(obj.AccessingTrunk.contains(player.GUID)) {
obj.AccessingTrunk = None
@@ -5562,7 +5577,7 @@ class WorldSessionActor extends Actor
log.info(s"Hit: $msg")
(hit_info match {
case Some(hitInfo) =>
- continent.GUID(hitInfo.hitobject_guid) match {
+ ValidObject(hitInfo.hitobject_guid) match {
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
Some((target, hitInfo.shot_origin, hitInfo.hit_pos))
case _ =>
@@ -5587,7 +5602,7 @@ class WorldSessionActor extends Actor
projectile.Position = explosion_pos
projectile.Velocity = projectile_vel
//direct_victim_uid
- continent.GUID(direct_victim_uid) match {
+ ValidObject(direct_victim_uid) match {
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, target.Position) match {
case Some(projectile) =>
@@ -5598,7 +5613,7 @@ class WorldSessionActor extends Actor
}
//other victims
targets.foreach(elem => {
- continent.GUID(elem.uid) match {
+ ValidObject(elem.uid) match {
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
ResolveProjectileEntry(projectile, ProjectileResolution.Splash, target, explosion_pos) match {
case Some(projectile) =>
@@ -5623,7 +5638,7 @@ class WorldSessionActor extends Actor
case msg @ LashMessage(seq_time, killer_guid, victim_guid, projectile_guid, pos, unk1) =>
log.info(s"Lash: $msg")
- continent.GUID(victim_guid) match {
+ ValidObject(victim_guid) match {
case Some(target : PlanetSideGameObject with FactionAffinity with Vitality) =>
ResolveProjectileEntry(projectile_guid, ProjectileResolution.Lash, target, pos) match {
case Some(projectile) =>
@@ -5663,7 +5678,7 @@ class WorldSessionActor extends Actor
case msg @ MountVehicleMsg(player_guid, mountable_guid, entry_point) =>
log.info("MountVehicleMsg: "+msg)
- continent.GUID(mountable_guid) match {
+ ValidObject(mountable_guid) match {
case Some(obj : Mountable) =>
obj.GetSeatFromMountPoint(entry_point) match {
case Some(seat_num) =>
@@ -5723,7 +5738,7 @@ class WorldSessionActor extends Actor
//kicking someone else out of a seat; need to own that seat/mountable
player.VehicleOwned match {
case Some(obj_guid) =>
- (continent.GUID(obj_guid), continent.GUID(player_guid)) match {
+ (ValidObject(obj_guid), ValidObject(player_guid)) match {
case (Some(obj : Mountable), Some(tplayer : Player)) =>
obj.PassengerInSeat(tplayer) match {
case Some(seat_num : Int) =>
@@ -5785,7 +5800,7 @@ class WorldSessionActor extends Actor
case msg @ PlanetsideAttributeMessage(object_guid, attribute_type, attribute_value) =>
log.info("PlanetsideAttributeMessage: "+msg)
- continent.GUID(object_guid) match {
+ ValidObject(object_guid) match {
case Some(vehicle : Vehicle) =>
if(player.VehicleOwned.contains(vehicle.GUID)) {
if(9 < attribute_type && attribute_type < 14) {
@@ -8546,12 +8561,12 @@ class WorldSessionActor extends Actor
def HandleDealingDamage(target : PlanetSideGameObject with Vitality, data : ResolvedProjectile) : Unit = {
val func = data.damage_model.Calculate(data)
target match {
- case obj : Player => obj.Actor ! Vitality.Damage(func)
- case obj : Vehicle => obj.Actor ! Vitality.Damage(func)
- case obj : FacilityTurret => obj.Actor ! Vitality.Damage(func)
- case obj : ComplexDeployable => obj.Actor ! Vitality.Damage(func)
+ case obj : Player if obj.Health > 0 => obj.Actor ! Vitality.Damage(func)
+ case obj : Vehicle if obj.Health > 0 => obj.Actor ! Vitality.Damage(func)
+ case obj : FacilityTurret if obj.Health > 0 => obj.Actor ! Vitality.Damage(func)
+ case obj : ComplexDeployable if obj.Health > 0 => obj.Actor ! Vitality.Damage(func)
- case obj : SimpleDeployable =>
+ case obj : SimpleDeployable if obj.Health > 0 =>
//damage is synchronized on `LSA` (results returned to and distributed from this `WSA`)
continent.LocalEvents ! Vitality.DamageOn(obj, func)
case _ => ;
@@ -10754,7 +10769,7 @@ class WorldSessionActor extends Actor
*/
def FindDetectedProjectileTargets(targets : Iterable[PlanetSideGUID]) : Iterable[String] = {
targets
- .map { continent.GUID }
+ .map { ValidObject }
.flatMap {
case Some(obj : Vehicle) if !obj.Cloaked =>
//TODO hint: vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.ProjectileAutoLockAwareness(mode))