diff --git a/common/src/main/scala/net/psforever/objects/Default.scala b/common/src/main/scala/net/psforever/objects/Default.scala new file mode 100644 index 00000000..33787813 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/Default.scala @@ -0,0 +1,47 @@ +// Copyright (c) 2017 PSForever +package net.psforever.objects + +object Default { + //cancellable + import akka.actor.Cancellable + protected class InternalCancellable extends Cancellable { + override def cancel : Boolean = true + override def isCancelled : Boolean = true + } + private val cancellable : Cancellable = new InternalCancellable + + /** + * Used to initialize the value of a re-usable `Cancellable` object. + * By convention, it always acts like it has been cancelled before and can be cancelled. + * Should be replaced with pertinent `Cancellable` logic through the initialization of an executor. + */ + final def Cancellable : Cancellable = cancellable + + //actor + import akka.actor.{Actor => AkkaActor, ActorRef, ActorSystem, DeadLetter, Props} + /** + * An actor designed to wrap around `deadLetters` and redirect all normal messages to it. + * This measure is more to "protect" `deadLetters` than anything else. + * Even if it is stopped, it still fulfills exactly the same purpose! + * The original target to which the actor is assigned will not be implicitly accredited. + */ + private class DefaultActor extends AkkaActor { + def receive : Receive = { + case msg => context.system.deadLetters ! DeadLetter(msg, sender, self) + } + } + private var defaultRef : ActorRef = ActorRef.noSender + /** + * Instigate the default actor. + * @param sys the actor universe under which this default actor will exist + * @return the new default actor + */ + def apply(sys : ActorSystem) : ActorRef = { + if(defaultRef == ActorRef.noSender) { + defaultRef = sys.actorOf(Props[DefaultActor], name = s"system-default-actor") + } + defaultRef + } + + final def Actor : ActorRef = defaultRef +} diff --git a/common/src/main/scala/net/psforever/objects/DefaultCancellable.scala b/common/src/main/scala/net/psforever/objects/DefaultCancellable.scala deleted file mode 100644 index 394ba9c9..00000000 --- a/common/src/main/scala/net/psforever/objects/DefaultCancellable.scala +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2017 PSForever -package net.psforever.objects - -/** - * Used to initialize the value of a re-usable `Cancellable` object. - * By convention, it always acts like it has been cancelled before and can be cancelled. - * Should be replaced with pertinent `Cancellable` logic through the initialization of an executor. - */ -object DefaultCancellable { - import akka.actor.Cancellable - - protected class InternalCancellable extends Cancellable { - override def cancel : Boolean = true - override def isCancelled : Boolean = true - } - - final val obj : Cancellable = new InternalCancellable -} diff --git a/common/src/main/scala/net/psforever/objects/ImplantSlot.scala b/common/src/main/scala/net/psforever/objects/ImplantSlot.scala index 5fed4a50..5cc624ea 100644 --- a/common/src/main/scala/net/psforever/objects/ImplantSlot.scala +++ b/common/src/main/scala/net/psforever/objects/ImplantSlot.scala @@ -20,7 +20,7 @@ class ImplantSlot { /** whether this implant is ready for use */ private var initialized : Boolean = false /** a cancellable timer that can be used to set an implant as initialized once complete */ - private var initializeTimer: Cancellable = DefaultCancellable.obj + private var initializeTimer: Cancellable = Default.Cancellable /** is this implant active */ private var active : Boolean = false diff --git a/common/src/main/scala/net/psforever/objects/Player.scala b/common/src/main/scala/net/psforever/objects/Player.scala index 87b1da3d..4144415d 100644 --- a/common/src/main/scala/net/psforever/objects/Player.scala +++ b/common/src/main/scala/net/psforever/objects/Player.scala @@ -125,7 +125,7 @@ class Player(private val core : Avatar) extends PlanetSideServerObject def Stamina_=(assignStamina : Int) : Int = { stamina = if(isAlive) { math.min(math.max(0, assignStamina), MaxStamina) } else { 0 } - if(Actor != ActorRef.noSender) { + if(Actor != Default.Actor) { Actor ! Player.StaminaChanged(Stamina) } diff --git a/common/src/main/scala/net/psforever/objects/Vehicle.scala b/common/src/main/scala/net/psforever/objects/Vehicle.scala index 6db09377..00356be4 100644 --- a/common/src/main/scala/net/psforever/objects/Vehicle.scala +++ b/common/src/main/scala/net/psforever/objects/Vehicle.scala @@ -1,7 +1,6 @@ // Copyright (c) 2017 PSForever package net.psforever.objects -import akka.actor.ActorRef import net.psforever.objects.definition.VehicleDefinition import net.psforever.objects.equipment.{Equipment, EquipmentSize, EquipmentSlot, JammableUnit} import net.psforever.objects.inventory.{Container, GridInventory, InventoryTile} @@ -546,14 +545,7 @@ class Vehicle(private val vehicleDef : VehicleDefinition) extends AmenityOwner false } - override def hashCode() : Int = { - Actor match { - case ActorRef.noSender => - super.hashCode() - case actor => - actor.hashCode() - } - } + override def hashCode() : Int = Actor.hashCode() /** * Override the string representation to provide additional information. diff --git a/common/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/common/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala index 552dd021..fa455aa3 100644 --- a/common/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala +++ b/common/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala @@ -2,7 +2,7 @@ package net.psforever.objects.avatar import akka.actor.Actor -import net.psforever.objects.{DefaultCancellable, GlobalDefinitions, ImplantSlot, Player, Players, Tool} +import net.psforever.objects.{Default, GlobalDefinitions, ImplantSlot, Player, Players, Tool} import net.psforever.objects.ballistics.{PlayerSource, ResolvedProjectile, SourceEntry} import net.psforever.objects.definition.ImplantDefinition import net.psforever.objects.equipment.{Ammo, JammableBehavior, JammableUnit} @@ -33,7 +33,7 @@ class PlayerControl(player : Player) extends Actor private [this] val damageLog = org.log4s.getLogger(Damageable.LogChannel) // A collection of timers for each slot to trigger stamina drain on an interval - val implantSlotStaminaDrainTimers = mutable.HashMap(0 -> DefaultCancellable.obj, 1 -> DefaultCancellable.obj, 2 -> DefaultCancellable.obj) + val implantSlotStaminaDrainTimers = mutable.HashMap(0 -> Default.Cancellable, 1 -> Default.Cancellable, 2 -> Default.Cancellable) def receive : Receive = jammableBehavior .orElse(takesDamage) @@ -46,7 +46,7 @@ class PlayerControl(player : Player) extends Actor if(status == 0 && (implantSlot.Active || !implantSlot.Initialized)) { // Cancel stamina drain timer implantSlotStaminaDrainTimers(slot).cancel() - implantSlotStaminaDrainTimers(slot) = DefaultCancellable.obj + implantSlotStaminaDrainTimers(slot) = Default.Cancellable implantSlot.Active = false player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id, AvatarAction.PlanetsideAttribute(player.GUID, 28, player.Implant(slot).id * 2)) // Deactivation sound / effect @@ -60,7 +60,7 @@ class PlayerControl(player : Player) extends Actor // todo: Deactivate implants server side when actions like zoning happen. (Other actions?) log.warn(s"Implant ${slot} is already active, but activating again") implantSlotStaminaDrainTimers(slot).cancel() - implantSlotStaminaDrainTimers(slot) = DefaultCancellable.obj + implantSlotStaminaDrainTimers(slot) = Default.Cancellable } implantSlot.Active = true @@ -104,9 +104,9 @@ class PlayerControl(player : Player) extends Actor if(implantSlot.Installed.isDefined) { player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.SendResponse(player.GUID, AvatarImplantMessage(player.GUID, ImplantAction.Initialization, slot, 1))) implantSlot.Initialized = true - if(implantSlot.InitializeTimer != DefaultCancellable.obj) { + if(implantSlot.InitializeTimer != Default.Cancellable) { implantSlot.InitializeTimer.cancel() - implantSlot.InitializeTimer = DefaultCancellable.obj + implantSlot.InitializeTimer = Default.Cancellable } } @@ -463,9 +463,9 @@ object PlayerControl { val implantSlot = player.ImplantSlot(slot) implantSlot.Initialized = false - if(implantSlot.InitializeTimer != DefaultCancellable.obj) { + if(implantSlot.InitializeTimer != Default.Cancellable) { implantSlot.InitializeTimer.cancel() - implantSlot.InitializeTimer = DefaultCancellable.obj + implantSlot.InitializeTimer = Default.Cancellable } player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.Id, AvatarAction.SendResponse(player.GUID, AvatarImplantMessage(player.GUID, ImplantAction.Initialization, slot, 0))) } 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 f0566a12..aa701983 100644 --- a/common/src/main/scala/net/psforever/objects/ce/TelepadLike.scala +++ b/common/src/main/scala/net/psforever/objects/ce/TelepadLike.scala @@ -2,8 +2,8 @@ package net.psforever.objects.ce import akka.actor.ActorContext +import net.psforever.objects.{Default, PlanetSideGameObject, TelepadDeployable, Vehicle} 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 import net.psforever.objects.zones.Zone @@ -50,8 +50,8 @@ 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) { + import akka.actor.Props + if(obj.Actor == Default.Actor) { obj.Actor = context.actorOf(Props(classOf[TelepadControl], obj), PlanetSideServerObject.UniqueActorName(obj)) } } diff --git a/common/src/main/scala/net/psforever/objects/definition/SimpleDeployableDefinition.scala b/common/src/main/scala/net/psforever/objects/definition/SimpleDeployableDefinition.scala index 07b9c430..277481a5 100644 --- a/common/src/main/scala/net/psforever/objects/definition/SimpleDeployableDefinition.scala +++ b/common/src/main/scala/net/psforever/objects/definition/SimpleDeployableDefinition.scala @@ -1,8 +1,8 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.definition -import akka.actor.{ActorContext, ActorRef} -import net.psforever.objects.PlanetSideGameObject +import akka.actor.ActorContext +import net.psforever.objects.{Default, PlanetSideGameObject} import net.psforever.objects.ce.{Deployable, DeployableCategory, DeployedItem} import net.psforever.objects.definition.converter.SmallDeployableConverter import net.psforever.objects.serverobject.PlanetSideServerObject @@ -68,6 +68,6 @@ object SimpleDeployableDefinition { def SimpleUninitialize(obj : PlanetSideServerObject, context : ActorContext) : Unit = { context.stop(obj.Actor) - obj.Actor = ActorRef.noSender + obj.Actor = Default.Actor } } diff --git a/common/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala b/common/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala index aeb045d1..95433213 100644 --- a/common/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala +++ b/common/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala @@ -2,7 +2,7 @@ package net.psforever.objects.equipment import akka.actor.{Actor, Cancellable} -import net.psforever.objects.{DefaultCancellable, PlanetSideGameObject, Tool} +import net.psforever.objects.{Default, PlanetSideGameObject, Tool} import net.psforever.objects.ballistics.ResolvedProjectile import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.vehicles.MountedWeapons @@ -111,9 +111,9 @@ trait JammableBehavior { /** flag for jammed sound */ protected var jammedSound : Boolean = false /** the sound timer */ - protected var jammeredSoundTimer : Cancellable = DefaultCancellable.obj + protected var jammeredSoundTimer : Cancellable = Default.Cancellable /** the effect timer */ - protected var jammeredStatusTimer : Cancellable = DefaultCancellable.obj + protected var jammeredStatusTimer : Cancellable = Default.Cancellable /** `ZoneAware` is used for callback to the event systems */ def JammableObject : PlanetSideServerObject with JammableUnit with ZoneAware diff --git a/common/src/main/scala/net/psforever/objects/guid/TaskResolver.scala b/common/src/main/scala/net/psforever/objects/guid/TaskResolver.scala index 0a905d06..557bfece 100644 --- a/common/src/main/scala/net/psforever/objects/guid/TaskResolver.scala +++ b/common/src/main/scala/net/psforever/objects/guid/TaskResolver.scala @@ -5,6 +5,7 @@ import java.util.concurrent.TimeoutException import akka.actor.{Actor, ActorRef, Cancellable} import akka.routing.Broadcast +import net.psforever.objects.Default import scala.annotation.tailrec import scala.collection.mutable.ListBuffer @@ -16,7 +17,7 @@ class TaskResolver() extends Actor { /** list of all work currently managed by this resolver */ private val tasks : ListBuffer[TaskResolver.TaskEntry] = new ListBuffer[TaskResolver.TaskEntry] /** scheduled termination of tardy managed work */ - private var timeoutCleanup : Cancellable = TaskResolver.DefaultCancellable + private var timeoutCleanup : Cancellable = Default.Cancellable /** logging utilities; default to tracing */ private[this] val log = org.log4s.getLogger private def trace(msg : String) = log.trace(msg) @@ -109,7 +110,7 @@ class TaskResolver() extends Actor { * @param resolver the `TaskResolver` that distributed this work, thus determining that this work is a sub-task; * by default, no one, as the work is identified as a main task */ - private def QueueSubtasks(task : Task, subtasks : List[TaskResolver.GiveTask], resolver : ActorRef = Actor.noSender) : Unit = { + private def QueueSubtasks(task : Task, subtasks : List[TaskResolver.GiveTask], resolver : ActorRef = ActorRef.noSender) : Unit = { val entry : TaskResolver.TaskEntry = TaskResolver.TaskEntry(task, subtasks.map(task => task.task), resolver) tasks += entry trace(s"enqueue task $task") @@ -347,7 +348,7 @@ object TaskResolver { * //@param isASubtask whether this work is intermediary or the last in a dependency chain * @param supertaskRef the `TaskResolver` that will handle work that depends on the outcome of this work */ - private final case class TaskEntry(task : Task, subtasks : List[Task] = Nil, supertaskRef : ActorRef = Actor.noSender) { + private final case class TaskEntry(task : Task, subtasks : List[Task] = Nil, supertaskRef : ActorRef = ActorRef.noSender) { private var start : Long = 0L private var isExecuting : Boolean = false @@ -368,14 +369,6 @@ object TaskResolver { } } - /** - * A placeholder `Cancellable` object for the time-out checking functionality. - */ - private final val DefaultCancellable = new Cancellable() { - def cancel : Boolean = true - def isCancelled() : Boolean = true - } - /** * Scan across a group of tasks to determine which ones match the target completion status. * @param iter an `Iterator` of enqueued `TaskEntry` indices 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 01085a38..05b7a102 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala @@ -2,41 +2,67 @@ package net.psforever.objects.serverobject import akka.actor.ActorRef -import net.psforever.objects.PlanetSideGameObject +import net.psforever.objects.{Default, PlanetSideGameObject} import net.psforever.objects.entity.NoGUIDException import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.zones.ZoneAware /** * An object layered on top of the standard game object class that maintains an internal `ActorRef`. - * A measure of synchronization can be managed using this `Actor`. + * A measure of synchronization can be managed using this `Actor` as a "controlling agent." */ abstract class PlanetSideServerObject extends PlanetSideGameObject with FactionAffinity with ZoneAware { - private var actor = ActorRef.noSender + private var actor : ActorRef = ActorRef.noSender + private var getActorFunc : ()=>ActorRef = PlanetSideServerObject.getDefaultActor /** * Retrieve a reference to the internal `Actor`. * @return the internal `ActorRef` */ - def Actor : ActorRef = actor + def Actor : ActorRef = getActorFunc() /** * Assign an `Actor` to act for this server object. - * This reference is only set once, that is, as long as the internal `ActorRef` directs to `Actor.noSender` (`null`). + * This reference is only set once, that is, as long as the internal `ActorRef` directs to `ActorRef.noSender` (`null`). * @param control the `Actor` whose functionality will govern this server object * @return the current internal `ActorRef` */ def Actor_=(control : ActorRef) : ActorRef = { - if(actor == ActorRef.noSender || control == ActorRef.noSender) { - actor = control + if(control == Default.Actor) { + ResetControl() } - actor + else { + actor = control + getActorFunc = PlanetSideServerObject.doGetLocalActor(this) + actor + } + } + + def ResetControl() : ActorRef = { + getActorFunc = PlanetSideServerObject.getDefaultActor + val out = actor + actor = ActorRef.noSender + out } } object PlanetSideServerObject { + /** + * Before the internal control agency of a respective server object is ever set, + * a default control agency will be produced. + * @return the value pointed to by `Default.Actor` + */ + private def getDefaultActor() : ActorRef = { Default.Actor } + + /** + * Allow retrieving a reference to the internal `Actor`. + * @see o the server object + * @return the internal `ActorRef` + */ + private def doGetLocalActor(o : PlanetSideServerObject)() : ActorRef = o.actor + /** * `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.
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala index ab6d4f2d..86df17b1 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala @@ -5,7 +5,7 @@ import akka.actor.{ActorContext, Cancellable, Props} import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} import net.psforever.objects.serverobject.pad.process.{VehicleSpawnControlBase, VehicleSpawnControlConcealPlayer} import net.psforever.objects.zones.Zone -import net.psforever.objects.{DefaultCancellable, Player, Vehicle} +import net.psforever.objects.{Default, Player, Vehicle} import services.RemoverActor import services.vehicle.VehicleServiceMessage @@ -32,7 +32,7 @@ import scala.concurrent.duration._ */ class VehicleSpawnControl(pad : VehicleSpawnPad) extends VehicleSpawnControlBase(pad) with FactionAffinityBehavior.Check { /** a reminder sent to future customers */ - var periodicReminder : Cancellable = DefaultCancellable.obj + var periodicReminder : Cancellable = Default.Cancellable /** a list of vehicle orders that have been submitted for this spawn pad */ var orders : List[VehicleSpawnControl.Order] = List.empty[VehicleSpawnControl.Order] /** the current vehicle order being acted upon; diff --git a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala index b58243ea..e0fed464 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala @@ -1,8 +1,8 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.serverobject.pad.process -import akka.actor.{ActorRef, Props} -import net.psforever.objects.Vehicle +import akka.actor.Props +import net.psforever.objects.{Default, Vehicle} import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} import scala.concurrent.ExecutionContext.Implicits.global @@ -29,7 +29,7 @@ class VehicleSpawnControlSeatDriver(pad : VehicleSpawnPad) extends VehicleSpawnC def receive : Receive = { case order @ VehicleSpawnControl.Order(_, vehicle) => - if(vehicle.Actor == ActorRef.noSender) { //wait for a necessary vehicle component to be loaded + if(vehicle.Actor == Default.Actor) { //wait for a necessary vehicle component to be loaded context.system.scheduler.scheduleOnce(50 milliseconds, self, order) } else { diff --git a/common/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala index 5f0b5266..4cd63ebc 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala @@ -3,7 +3,7 @@ package net.psforever.objects.serverobject.painbox import akka.actor.{Actor, Cancellable} import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.structures.Building -import net.psforever.objects.{DefaultCancellable, GlobalDefinitions} +import net.psforever.objects.{Default, GlobalDefinitions} import net.psforever.types.{PlanetSideEmpire, Vector3} import services.avatar.{AvatarAction, AvatarServiceMessage} @@ -12,7 +12,7 @@ import scala.concurrent.duration._ class PainboxControl(painbox: Painbox) extends Actor { private[this] val log = org.log4s.getLogger(s"Painbox") - var painboxTick: Cancellable = DefaultCancellable.obj + var painboxTick: Cancellable = Default.Cancellable var nearestDoor: Option[Door] = None var bBoxMinCorner = Vector3.Zero var bBoxMaxCorner = Vector3.Zero @@ -97,7 +97,7 @@ class PainboxControl(painbox: Painbox) extends Actor { case Painbox.Stop() => context.become(Stopped) painboxTick.cancel - painboxTick = DefaultCancellable.obj + painboxTick = Default.Cancellable case Painbox.Tick() => //todo: Account for overlapping pain fields diff --git a/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala b/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala index 06878023..180293ee 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala @@ -3,8 +3,8 @@ package net.psforever.objects.serverobject.structures import java.util.concurrent.TimeUnit -import akka.actor.{ActorContext, ActorRef} -import net.psforever.objects.{GlobalDefinitions, Player} +import akka.actor.ActorContext +import net.psforever.objects.{Default, GlobalDefinitions, Player} import net.psforever.objects.definition.ObjectDefinition import net.psforever.objects.serverobject.generator.Generator import net.psforever.objects.serverobject.hackable.Hackable @@ -118,7 +118,7 @@ class Building(private val name: String, } def TriggerZoneMapUpdate(): Unit = { - if(Actor != ActorRef.noSender) Actor ! Building.TriggerZoneMapUpdate(Zone.Number) + if(Actor != Default.Actor) Actor ! Building.TriggerZoneMapUpdate(Zone.Number) } def UpdateForceDomeStatus() : Unit = { @@ -141,7 +141,7 @@ class Building(private val name: String, } if(originalStatus != ForceDomeActive) { - if(Actor != ActorRef.noSender) { + if(Actor != Default.Actor) { Zone.LocalEvents ! LocalServiceMessage(Zone.Id, LocalAction.UpdateForceDomeStatus(Service.defaultPlayerGUID, GUID, ForceDomeActive)) Actor ! Building.SendMapUpdate(all_clients = true) } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/structures/BuildingControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/structures/BuildingControl.scala index 7f06e1b0..4818f9c0 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/structures/BuildingControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/structures/BuildingControl.scala @@ -13,8 +13,8 @@ import services.galaxy.{GalaxyAction, GalaxyResponse, GalaxyServiceMessage, Gala class BuildingControl(building : Building) extends Actor with FactionAffinityBehavior.Check { def FactionObject : FactionAffinity = building - var galaxyService : ActorRef = Actor.noSender - var interstellarCluster : ActorRef = Actor.noSender + var galaxyService : ActorRef = ActorRef.noSender + var interstellarCluster : ActorRef = ActorRef.noSender private[this] val log = org.log4s.getLogger override def preStart = { 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 b815ce2a..3ceb4fdd 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 @@ -2,7 +2,7 @@ package net.psforever.objects.serverobject.terminals import akka.actor.ActorContext -import net.psforever.objects.Player +import net.psforever.objects.{Default, Player} import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.structures.Amenity @@ -28,8 +28,8 @@ object MatrixTerminalDefinition { * @param context hook to the local `Actor` system */ def Setup(obj : Amenity, context : ActorContext) : Unit = { - import akka.actor.{ActorRef, Props} - if(obj.Actor == ActorRef.noSender) { + import akka.actor.Props + if(obj.Actor == Default.Actor) { 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 5ab86e44..a4bb0e78 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 @@ -3,7 +3,7 @@ package net.psforever.objects.serverobject.terminals import akka.actor.ActorContext import net.psforever.objects.definition.ImplantDefinition -import net.psforever.objects.{Player, Vehicle} +import net.psforever.objects.{Default, Player, Vehicle} import net.psforever.objects.equipment.Equipment import net.psforever.objects.loadouts.{InfantryLoadout, VehicleLoadout} import net.psforever.objects.inventory.InventoryItem @@ -340,8 +340,8 @@ object OrderTerminalDefinition { * @param context hook to the local `Actor` system */ def Setup(obj : Amenity, context : ActorContext) : Unit = { - import akka.actor.{ActorRef, Props} - if(obj.Actor == ActorRef.noSender) { + import akka.actor.Props + if(obj.Actor == Default.Actor) { 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 65ba0fd0..63aa9479 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 @@ -1,7 +1,7 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.serverobject.terminals -import net.psforever.objects.Player +import net.psforever.objects.{Default, Player} import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} import net.psforever.objects.serverobject.structures.Amenity import net.psforever.types.Vector3 @@ -74,8 +74,8 @@ object ProximityTerminal { * @param context hook to the local `Actor` system */ def Setup(obj : Amenity, context : ActorContext) : Unit = { - import akka.actor.{ActorRef, Props} - if(obj.Actor == ActorRef.noSender) { + import akka.actor.Props + if(obj.Actor == Default.Actor) { 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/terminals/ProximityTerminalControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala index 6c9de987..4cbe945b 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala @@ -30,7 +30,7 @@ class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor def DamageableObject = term def RepairableObject = term - var terminalAction : Cancellable = DefaultCancellable.obj + var terminalAction : Cancellable = Default.Cancellable val callbacks : mutable.ListBuffer[ActorRef] = new mutable.ListBuffer[ActorRef]() val log = org.log4s.getLogger 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 d96011b9..0ad4e09f 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 @@ -2,7 +2,7 @@ package net.psforever.objects.serverobject.tube import akka.actor.ActorContext -import net.psforever.objects.SpawnPointDefinition +import net.psforever.objects.{Default, SpawnPointDefinition} import net.psforever.objects.definition.converter.SpawnTubeConverter import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.structures.{Amenity, AmenityDefinition} @@ -23,8 +23,8 @@ object SpawnTubeDefinition { * @param context hook to the local `Actor` system */ def Setup(obj : Amenity, context : ActorContext) : Unit = { - import akka.actor.{ActorRef, Props} - if(obj.Actor == ActorRef.noSender) { + import akka.actor.Props + if(obj.Actor == Default.Actor) { obj.Actor = context.actorOf(Props(classOf[SpawnTubeControl], obj), PlanetSideServerObject.UniqueActorName(obj)) } } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala index ee273448..5127f7cb 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala @@ -3,7 +3,7 @@ package net.psforever.objects.serverobject.turret import akka.actor.Actor import net.psforever.objects.ballistics.ResolvedProjectile -import net.psforever.objects.{DefaultCancellable, GlobalDefinitions, Player, Tool} +import net.psforever.objects.{Default, GlobalDefinitions, Player, Tool} import net.psforever.objects.equipment.{Ammo, JammableMountedWeapons} import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.mount.MountableBehavior @@ -41,7 +41,7 @@ class FacilityTurretControl(turret : FacilityTurret) extends Actor def RepairableObject = turret // Used for timing ammo recharge for vanu turrets in caves - var weaponAmmoRechargeTimer = DefaultCancellable.obj + var weaponAmmoRechargeTimer = Default.Cancellable def receive : Receive = checkBehavior .orElse(jammableBehavior) @@ -65,7 +65,7 @@ class FacilityTurretControl(turret : FacilityTurret) extends Actor } case FacilityTurret.WeaponDischarged() => - if(weaponAmmoRechargeTimer != DefaultCancellable.obj) { + if(weaponAmmoRechargeTimer != Default.Cancellable) { weaponAmmoRechargeTimer.cancel() } @@ -83,9 +83,9 @@ class FacilityTurretControl(turret : FacilityTurret) extends Actor } } - if(weapon.Magazine == weapon.MaxMagazine && weaponAmmoRechargeTimer != DefaultCancellable.obj) { + if(weapon.Magazine == weapon.MaxMagazine && weaponAmmoRechargeTimer != Default.Cancellable) { weaponAmmoRechargeTimer.cancel() - weaponAmmoRechargeTimer = DefaultCancellable.obj + weaponAmmoRechargeTimer = Default.Cancellable } case _ => ; 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 aa6c05be..da071ea1 100644 --- a/common/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala +++ b/common/src/main/scala/net/psforever/objects/teamwork/SquadFeatures.scala @@ -1,7 +1,7 @@ // Copyright (c) 2019 PSForever package net.psforever.objects.teamwork -import akka.actor.{Actor, ActorContext, ActorRef, Props} +import akka.actor.{ActorContext, ActorRef, Props} import net.psforever.types.SquadWaypoints import services.teamwork.SquadService.WaypointData import services.teamwork.SquadSwitchboard @@ -74,7 +74,7 @@ class SquadFeatures(val Squad : Squad) { def Stop : SquadFeatures = { switchboard ! akka.actor.PoisonPill - switchboard = Actor.noSender + switchboard = ActorRef.noSender waypoints = Array.empty this } diff --git a/common/src/main/scala/net/psforever/objects/vehicles/CargoBehavior.scala b/common/src/main/scala/net/psforever/objects/vehicles/CargoBehavior.scala index 5f7c9795..ed5d25e8 100644 --- a/common/src/main/scala/net/psforever/objects/vehicles/CargoBehavior.scala +++ b/common/src/main/scala/net/psforever/objects/vehicles/CargoBehavior.scala @@ -15,8 +15,8 @@ import scala.concurrent.duration._ trait CargoBehavior { _ : Actor => - private var cargoMountTimer : Cancellable = DefaultCancellable.obj - private var cargoDismountTimer : Cancellable = DefaultCancellable.obj + private var cargoMountTimer : Cancellable = Default.Cancellable + private var cargoDismountTimer : Cancellable = Default.Cancellable /* gate-keep mounting behavior so that unit does not try to dismount as cargo, or mount different vehicle */ private var isMounting : Option[PlanetSideGUID] = None diff --git a/common/src/main/scala/net/psforever/objects/vehicles/Utility.scala b/common/src/main/scala/net/psforever/objects/vehicles/Utility.scala index 11829053..039c3cc0 100644 --- a/common/src/main/scala/net/psforever/objects/vehicles/Utility.scala +++ b/common/src/main/scala/net/psforever/objects/vehicles/Utility.scala @@ -269,6 +269,6 @@ object InternalTelepadDefinition { def SimpleUninitialize(obj : PlanetSideServerObject, context : ActorContext) : Unit = { context.stop(obj.Actor) - obj.Actor = ActorRef.noSender + obj.Actor = Default.Actor } } 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 6a62b822..2d4d7e6f 100644 --- a/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala +++ b/common/src/main/scala/net/psforever/objects/vehicles/VehicleControl.scala @@ -52,7 +52,7 @@ class VehicleControl(vehicle : Vehicle) extends Actor /** cheap flag for whether the vehicle is decaying */ var decaying : Boolean = false /** primary vehicle decay timer */ - var decayTimer : Cancellable = DefaultCancellable.obj + var decayTimer : Cancellable = Default.Cancellable def receive : Receive = Enabled @@ -62,7 +62,7 @@ class VehicleControl(vehicle : Vehicle) extends Actor decayTimer.cancel vehicle.Utilities.values.foreach { util => context.stop(util().Actor) - util().Actor = ActorRef.noSender + util().Actor = Default.Actor } } diff --git a/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala b/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala index f08082ed..b07a45f1 100644 --- a/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala +++ b/common/src/main/scala/net/psforever/objects/zones/SphereOfInfluenceActor.scala @@ -1,7 +1,7 @@ package net.psforever.objects.zones import akka.actor.{Actor, Cancellable} -import net.psforever.objects.{DefaultCancellable, Player} +import net.psforever.objects.{Default, Player} import net.psforever.objects.definition.ObjectDefinition import net.psforever.objects.serverobject.structures.{Building, SphereOfInfluence} import net.psforever.types.Vector3 @@ -12,7 +12,7 @@ import scala.concurrent.duration._ class SphereOfInfluenceActor(zone: Zone) extends Actor { var sois : Iterable[(Building, Int)] = Nil - var populateTick : Cancellable = DefaultCancellable.obj + var populateTick : Cancellable = Default.Cancellable //private[this] val log = org.log4s.getLogger(s"${zone.Id.capitalize}-SphereOfInfluenceActor") def receive : Receive = Stopped diff --git a/common/src/main/scala/net/psforever/objects/zones/Zone.scala b/common/src/main/scala/net/psforever/objects/zones/Zone.scala index 4e2b49b2..08c29e87 100644 --- a/common/src/main/scala/net/psforever/objects/zones/Zone.scala +++ b/common/src/main/scala/net/psforever/objects/zones/Zone.scala @@ -55,13 +55,13 @@ import scala.util.Try */ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { /** Governs general synchronized external requests. */ - private var actor = ActorRef.noSender + private var actor = Default.Actor /** Actor that handles SOI related functionality, for example if a player is in a SOI **/ - private var soi = ActorRef.noSender + private var soi = Default.Actor /** Used by the globally unique identifier system to coordinate requests. */ - private var accessor : ActorRef = ActorRef.noSender + private var accessor : ActorRef = Default.Actor /** The basic support structure for the globally unique number system used by this `Zone`. */ private var guid : NumberPoolHub = new NumberPoolHub(new LimitedNumberSource(65536)) /** A synchronized `List` of items (`Equipment`) dropped by players on the ground and can be collected again. */ @@ -69,19 +69,19 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { /** */ private val vehicles : ListBuffer[Vehicle] = ListBuffer[Vehicle]() /** Used by the `Zone` to coordinate `Equipment` dropping and collection requests. */ - private var ground : ActorRef = ActorRef.noSender + private var ground : ActorRef = Default.Actor /** */ private val constructions : ListBuffer[PlanetSideGameObject with Deployable] = ListBuffer[PlanetSideGameObject with Deployable]() /** */ - private var deployables : ActorRef = ActorRef.noSender + private var deployables : ActorRef = Default.Actor /** */ - private var transport : ActorRef = ActorRef.noSender + private var transport : ActorRef = Default.Actor /** */ private val players : TrieMap[Avatar, Option[Player]] = TrieMap[Avatar, Option[Player]]() /** */ private val corpses : ListBuffer[Player] = ListBuffer[Player]() /** */ - private var population : ActorRef = ActorRef.noSender + private var population : ActorRef = Default.Actor private var buildings : PairMap[Int, Building] = PairMap.empty[Int, Building] @@ -92,7 +92,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { /** key - spawn zone id, value - buildings belonging to spawn zone */ private var spawnGroups : Map[Building, List[SpawnPoint]] = PairMap[Building, List[SpawnPoint]]() /** */ - private var projector : ActorRef = ActorRef.noSender + private var projector : ActorRef = Default.Actor /** */ private var hotspots : ListBuffer[HotSpotInfo] = ListBuffer[HotSpotInfo]() /** */ @@ -102,11 +102,11 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { /** calculate a duration from a given interaction's participants */ private var hotspotTimeFunc : (SourceEntry, SourceEntry)=>FiniteDuration = Zone.HotSpot.Rules.NoTime /** */ - private var avatarEvents : ActorRef = ActorRef.noSender + private var avatarEvents : ActorRef = Default.Actor /** */ - private var localEvents : ActorRef = ActorRef.noSender + private var localEvents : ActorRef = Default.Actor /** */ - private var vehicleEvents : ActorRef = ActorRef.noSender + private var vehicleEvents : ActorRef = Default.Actor /** * Establish the basic accessible conditions necessary for a functional `Zone`.
@@ -126,7 +126,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { * @param context a reference to an `ActorContext` necessary for `Props` */ def Init(implicit context : ActorContext) : Unit = { - if(accessor == ActorRef.noSender) { + if(accessor == Default.Actor) { SetupNumberPools() accessor = context.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], this.guid, UniqueNumberSystem.AllocateNumberPoolActors(this.guid))), s"$Id-uns") ground = context.actorOf(Props(classOf[ZoneGroundActor], this, equipmentOnGround), s"$Id-ground") @@ -187,7 +187,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { * @see `ZoneActor` */ def Actor_=(zoneActor : ActorRef) : ActorRef = { - if(actor == ActorRef.noSender) { + if(actor == Default.Actor) { actor = zoneActor } Actor @@ -226,7 +226,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { * @return synchronized reference to the globally unique identifier system */ def GUID(hub : NumberPoolHub) : Boolean = { - if(actor == ActorRef.noSender && guid.Pools.values.foldLeft(0)(_ + _.Count) == 0) { + if(actor == Default.Actor && guid.Pools.values.foldLeft(0)(_ + _.Count) == 0) { import org.fusesource.jansi.Ansi.Color.RED import org.fusesource.jansi.Ansi.ansi println(ansi().fgBright(RED).a(s"""Caution: replacement of the number pool system for zone $Id; function is for testing purposes only""").reset()) @@ -248,7 +248,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { * `false`, if the new pool can not be created because the system has already been started */ def AddPool(name : String, pool : Seq[Int]) : Boolean = { - if(accessor == ActorRef.noSender) { + if(accessor == Default.Actor) { guid.AddPool(name, pool.toList) true } @@ -266,7 +266,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) { * `false`, if the new pool can not be removed because the system has already been started */ def RemovePool(name : String) : Boolean = { - if(accessor == ActorRef.noSender) { + if(accessor == Default.Actor) { guid.RemovePool(name) true } diff --git a/common/src/main/scala/net/psforever/objects/zones/ZoneHotSpotProjector.scala b/common/src/main/scala/net/psforever/objects/zones/ZoneHotSpotProjector.scala index be4fd1a0..9aed54e7 100644 --- a/common/src/main/scala/net/psforever/objects/zones/ZoneHotSpotProjector.scala +++ b/common/src/main/scala/net/psforever/objects/zones/ZoneHotSpotProjector.scala @@ -2,7 +2,7 @@ package net.psforever.objects.zones import akka.actor.{Actor, ActorRef, Cancellable, Props} -import net.psforever.objects.DefaultCancellable +import net.psforever.objects.Default import net.psforever.types.{PlanetSideEmpire, Vector3} import services.ServiceManager @@ -59,7 +59,7 @@ class ZoneHotSpotProjector(zone : Zone, hotspots : ListBuffer[HotSpotInfo], blan /** a hook for the `GalaxyService` used to broadcast messages */ var galaxy : ActorRef = ActorRef.noSender /** the timer for the blanking process */ - var blanking : Cancellable = DefaultCancellable.obj + var blanking : Cancellable = Default.Cancellable /** how long to wait in between blanking periods while hotspots decay */ var blankingDelay : FiniteDuration = blankingTime 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 1f7bdf0a..f6bae4bb 100644 --- a/common/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala +++ b/common/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala @@ -3,7 +3,7 @@ package net.psforever.objects.zones import akka.actor.{Actor, ActorRef, Props} import net.psforever.objects.avatar.PlayerControl -import net.psforever.objects.{Avatar, Player} +import net.psforever.objects.{Avatar, Default, Player} import scala.annotation.tailrec import scala.collection.concurrent.TrieMap @@ -47,7 +47,7 @@ class ZonePopulationActor(zone : Zone, playerMap : TrieMap[Avatar, Option[Player sender ! Zone.Population.PlayerAlreadySpawned(zone, player) } else if(newToZone) { - player.Actor = context.actorOf(Props(classOf[PlayerControl], player), s"${player.CharId}_${player.GUID.guid}_${System.currentTimeMillis}") + player.Actor = context.actorOf(Props(classOf[PlayerControl], player), name = GetPlayerControlName(player, None)) player.Zone = zone } case None => @@ -63,10 +63,15 @@ class ZonePopulationActor(zone : Zone, playerMap : TrieMap[Avatar, Option[Player } case Zone.Corpse.Add(player) => - CorpseAdd(player, corpseList) + if(CorpseAdd(player, corpseList)) { + player.Actor = context.actorOf(Props(classOf[PlayerControl], player), name = s"corpse_of_${GetPlayerControlName(player, None)}") + player.Zone = zone + } case Zone.Corpse.Remove(player) => - CorpseRemove(player, corpseList) + if(CorpseRemove(player, corpseList)) { + PlayerLeave(player) + } case _ => ; } @@ -168,17 +173,19 @@ object ZonePopulationActor { * @param player a `Player` object * @param corpseList a list of `Player` objects */ - def CorpseRemove(player : Player, corpseList : ListBuffer[Player]) : Unit = { + def CorpseRemove(player : Player, corpseList : ListBuffer[Player]) : Boolean = { recursiveFindCorpse(corpseList.iterator, player) match { - case None => ; + case None => + false case Some(index) => corpseList.remove(index) + true } } def PlayerLeave(player : Player) : Unit = { player.Actor ! akka.actor.PoisonPill - player.Actor = ActorRef.noSender + player.Actor = Default.Actor } /** @@ -202,4 +209,14 @@ object ZonePopulationActor { } } } + + def GetPlayerControlName(player : Player, old : Option[ActorRef]) : String = { + old match { + case Some(control) => + val nameNumber = control.toString.split("/").last //split on '/' + nameNumber.split("#").head //split on '#' + case None => ; + s"${player.CharId}_${player.GUID.guid}_${System.currentTimeMillis}" //new + } + } } 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 4fd31757..be4bdbce 100644 --- a/common/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala +++ b/common/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala @@ -1,8 +1,8 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.zones -import akka.actor.{Actor, ActorRef, Props} -import net.psforever.objects.Vehicle +import akka.actor.{Actor, Props} +import net.psforever.objects.{Default, Vehicle} import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.vehicles.VehicleControl @@ -38,7 +38,7 @@ class ZoneVehicleActor(zone : Zone, vehicleList : ListBuffer[Vehicle]) extends A else if(vehicleList.contains(vehicle)) { sender ! Zone.Vehicle.CanNotSpawn(zone, vehicle, "already in zone") } - else if(vehicle.Actor != ActorRef.noSender) { + else if(vehicle.Actor != Default.Actor) { sender ! Zone.Vehicle.CanNotSpawn(zone, vehicle, "already in another zone") } else { @@ -52,7 +52,7 @@ class ZoneVehicleActor(zone : Zone, vehicleList : ListBuffer[Vehicle]) extends A case Some(index) => vehicleList.remove(index) context.stop(vehicle.Actor) - vehicle.Actor = ActorRef.noSender + vehicle.Actor = Default.Actor case None => ; sender ! Zone.Vehicle.CanNotDespawn(zone, vehicle, "can not find") } diff --git a/common/src/main/scala/services/RemoverActor.scala b/common/src/main/scala/services/RemoverActor.scala index 58415aa5..7e200c76 100644 --- a/common/src/main/scala/services/RemoverActor.scala +++ b/common/src/main/scala/services/RemoverActor.scala @@ -1,10 +1,10 @@ // Copyright (c) 2017 PSForever package services -import akka.actor.{Actor, ActorRef, Cancellable} +import akka.actor.{ActorRef, Cancellable} import net.psforever.objects.guid.TaskResolver import net.psforever.objects.zones.Zone -import net.psforever.objects.{DefaultCancellable, PlanetSideGameObject} +import net.psforever.objects.{Default, PlanetSideGameObject} import net.psforever.types.Vector3 import services.support.{SimilarityComparator, SupportActor, SupportActorCaseConversions} @@ -33,7 +33,7 @@ abstract class RemoverActor extends SupportActor[RemoverActor.Entry] { /** * The timer that checks whether entries in the first pool are still eligible for that pool. */ - var firstTask : Cancellable = DefaultCancellable.obj + var firstTask : Cancellable = Default.Cancellable /** * The first pool of objects waiting to be processed for removal. */ @@ -42,13 +42,13 @@ abstract class RemoverActor extends SupportActor[RemoverActor.Entry] { /** * The timer that checks whether entries in the second pool are still eligible for that pool. */ - var secondTask : Cancellable = DefaultCancellable.obj + var secondTask : Cancellable = Default.Cancellable /** * The second pool of objects waiting to be processed for removal. */ var secondHeap : List[RemoverActor.Entry] = List() - protected var taskResolver : ActorRef = Actor.noSender + protected var taskResolver : ActorRef = ActorRef.noSender val sameEntryComparator = new SimilarityComparator[RemoverActor.Entry]() { def Test(entry1 : RemoverActor.Entry, entry2 : RemoverActor.Entry) : Boolean = { diff --git a/common/src/main/scala/services/ServiceManager.scala b/common/src/main/scala/services/ServiceManager.scala index 91dcb449..bf2628e1 100644 --- a/common/src/main/scala/services/ServiceManager.scala +++ b/common/src/main/scala/services/ServiceManager.scala @@ -1,12 +1,12 @@ +// Copyright (c) 2017 PSForever package services -// Copyright (c) 2017 PSForever import akka.actor.{Actor, ActorIdentity, ActorRef, ActorSystem, Identify, Props} import scala.collection.mutable object ServiceManager { - var serviceManager = Actor.noSender + var serviceManager = ActorRef.noSender def boot(implicit system : ActorSystem) = { serviceManager = system.actorOf(Props[ServiceManager], "service") diff --git a/common/src/main/scala/services/account/AccountPersistenceService.scala b/common/src/main/scala/services/account/AccountPersistenceService.scala index e0bdf217..3c7f290d 100644 --- a/common/src/main/scala/services/account/AccountPersistenceService.scala +++ b/common/src/main/scala/services/account/AccountPersistenceService.scala @@ -190,7 +190,7 @@ class PersistenceMonitor(name : String, squadService : ActorRef, taskResolver : /** the last-reported game coordinate position of this player */ var lastPosition : Vector3 = Vector3.Zero /** the ongoing amount of permissible inactivity */ - var timer : Cancellable = DefaultCancellable.obj + var timer : Cancellable = Default.Cancellable /** the sparingly-used log */ val log = org.log4s.getLogger @@ -309,7 +309,7 @@ class PersistenceMonitor(name : String, squadService : ActorRef, taskResolver : player.Position = Vector3.Zero player.Health = 0 inZone.GUID(player.VehicleOwned) match { - case Some(vehicle : Vehicle) if vehicle.OwnerName.contains(player.Name) && vehicle.Actor != ActorRef.noSender => + case Some(vehicle : Vehicle) if vehicle.OwnerName.contains(player.Name) && vehicle.Actor != Default.Actor => vehicle.Actor ! Vehicle.Ownership(None) case _ => ; } diff --git a/common/src/main/scala/services/local/support/DoorCloseActor.scala b/common/src/main/scala/services/local/support/DoorCloseActor.scala index 771e47a8..2b497751 100644 --- a/common/src/main/scala/services/local/support/DoorCloseActor.scala +++ b/common/src/main/scala/services/local/support/DoorCloseActor.scala @@ -2,7 +2,7 @@ package services.local.support import akka.actor.{Actor, Cancellable} -import net.psforever.objects.{DefaultCancellable, Player} +import net.psforever.objects.{Default, Player} import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.zones.Zone @@ -18,7 +18,7 @@ import scala.concurrent.duration._ */ class DoorCloseActor() extends Actor { /** The periodic `Executor` that checks for doors to be closed */ - private var doorCloserTrigger : Cancellable = DefaultCancellable.obj + private var doorCloserTrigger : Cancellable = Default.Cancellable /** A `List` of currently open doors */ private var openDoors : List[DoorCloseActor.DoorEntry] = Nil //private[this] val log = org.log4s.getLogger diff --git a/common/src/main/scala/services/local/support/HackCaptureActor.scala b/common/src/main/scala/services/local/support/HackCaptureActor.scala index 18387e8f..e01d13cf 100644 --- a/common/src/main/scala/services/local/support/HackCaptureActor.scala +++ b/common/src/main/scala/services/local/support/HackCaptureActor.scala @@ -1,7 +1,7 @@ package services.local.support import akka.actor.{Actor, Cancellable} -import net.psforever.objects.DefaultCancellable +import net.psforever.objects.Default import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.terminals.CaptureTerminal @@ -14,7 +14,7 @@ import scala.concurrent.duration.{FiniteDuration, _} class HackCaptureActor extends Actor { private [this] val log = org.log4s.getLogger - private var clearTrigger : Cancellable = DefaultCancellable.obj + private var clearTrigger : Cancellable = Default.Cancellable /** A `List` of currently hacked server objects */ private var hackedObjects : List[HackCaptureActor.HackEntry] = Nil diff --git a/common/src/main/scala/services/local/support/HackClearActor.scala b/common/src/main/scala/services/local/support/HackClearActor.scala index a53becd8..53fcfae2 100644 --- a/common/src/main/scala/services/local/support/HackClearActor.scala +++ b/common/src/main/scala/services/local/support/HackClearActor.scala @@ -4,7 +4,7 @@ package services.local.support import java.util.concurrent.TimeUnit import akka.actor.{Actor, Cancellable} -import net.psforever.objects.DefaultCancellable +import net.psforever.objects.Default import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} import net.psforever.objects.zones.Zone @@ -20,7 +20,7 @@ import scala.concurrent.duration._ */ class HackClearActor() extends Actor { /** The periodic `Executor` that checks for server objects to be unhacked */ - private var clearTrigger : Cancellable = DefaultCancellable.obj + private var clearTrigger : Cancellable = Default.Cancellable /** A `List` of currently hacked server objects */ private var hackedObjects : List[HackClearActor.HackEntry] = Nil private[this] val log = org.log4s.getLogger diff --git a/common/src/main/scala/services/local/support/RouterTelepadActivation.scala b/common/src/main/scala/services/local/support/RouterTelepadActivation.scala index 25f54881..f471e573 100644 --- a/common/src/main/scala/services/local/support/RouterTelepadActivation.scala +++ b/common/src/main/scala/services/local/support/RouterTelepadActivation.scala @@ -9,7 +9,7 @@ import services.support.{SimilarityComparator, SupportActor} import scala.concurrent.duration._ class RouterTelepadActivation extends SupportActor[RouterTelepadActivation.Entry] { - var activationTask : Cancellable = DefaultCancellable.obj + var activationTask : Cancellable = Default.Cancellable var telepadList : List[RouterTelepadActivation.Entry] = List() val sameEntryComparator = new SimilarityComparator[RouterTelepadActivation.Entry]() { def Test(entry1 : RouterTelepadActivation.Entry, entry2 : RouterTelepadActivation.Entry) : Boolean = { diff --git a/common/src/main/scala/services/vehicle/support/TurretUpgrader.scala b/common/src/main/scala/services/vehicle/support/TurretUpgrader.scala index 0d09607b..d0e27daf 100644 --- a/common/src/main/scala/services/vehicle/support/TurretUpgrader.scala +++ b/common/src/main/scala/services/vehicle/support/TurretUpgrader.scala @@ -1,8 +1,8 @@ // Copyright (c) 2017 PSForever package services.vehicle.support -import akka.actor.{Actor, ActorRef, Cancellable} -import net.psforever.objects.{AmmoBox, DefaultCancellable, PlanetSideGameObject, Tool} +import akka.actor.{ActorRef, Cancellable} +import net.psforever.objects.{AmmoBox, Default, PlanetSideGameObject, Tool} import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver} import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.turret.{FacilityTurret, TurretUpgrade, WeaponTurret} @@ -16,11 +16,11 @@ import services.{Service, ServiceManager} import scala.concurrent.duration._ class TurretUpgrader extends SupportActor[TurretUpgrader.Entry] { - var task : Cancellable = DefaultCancellable.obj + var task : Cancellable = Default.Cancellable var list : List[TurretUpgrader.Entry] = List() - private var taskResolver : ActorRef = Actor.noSender + private var taskResolver : ActorRef = ActorRef.noSender val sameEntryComparator = new SimilarityComparator[TurretUpgrader.Entry]() { def Test(entry1 : TurretUpgrader.Entry, entry2 : TurretUpgrader.Entry) : Boolean = { diff --git a/common/src/main/scala/services/vehicle/support/VehicleRemover.scala b/common/src/main/scala/services/vehicle/support/VehicleRemover.scala index 1f57e85f..7ddab7a2 100644 --- a/common/src/main/scala/services/vehicle/support/VehicleRemover.scala +++ b/common/src/main/scala/services/vehicle/support/VehicleRemover.scala @@ -7,7 +7,7 @@ import net.psforever.objects.guid.GUIDTask.UnregisterVehicle import services.{RemoverActor, ServiceManager} class VehicleRemover extends Actor { - var taskResolver : ActorRef = Actor.noSender + var taskResolver : ActorRef = ActorRef.noSender override def preStart() : Unit = { super.preStart() diff --git a/common/src/test/scala/objects/BuildingTest.scala b/common/src/test/scala/objects/BuildingTest.scala index cec8b518..c605ffda 100644 --- a/common/src/test/scala/objects/BuildingTest.scala +++ b/common/src/test/scala/objects/BuildingTest.scala @@ -1,9 +1,9 @@ // Copyright (c) 2017 PSForever package objects -import akka.actor.{ActorRef, Props} +import akka.actor.Props import base.ActorTest -import net.psforever.objects.GlobalDefinitions +import net.psforever.objects.{Default, GlobalDefinitions} import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.doors.{Door, DoorControl} import net.psforever.objects.serverobject.structures._ @@ -72,7 +72,7 @@ class BuildingTest extends Specification { "construct" in { val bldg = Building("Building", 0, 10, Zone.Nowhere, StructureType.Building) bldg.MapId mustEqual 10 - bldg.Actor mustEqual ActorRef.noSender + bldg.Actor mustEqual Default.Actor bldg.Amenities mustEqual Nil bldg.Zone mustEqual Zone.Nowhere bldg.Faction mustEqual PlanetSideEmpire.NEUTRAL @@ -107,7 +107,7 @@ class WarpGateTest extends Specification { "construct" in { val bldg = WarpGate("WarpGate", 0, 10, Zone.Nowhere, GlobalDefinitions.warpgate) bldg.MapId mustEqual 10 - bldg.Actor mustEqual ActorRef.noSender + bldg.Actor mustEqual Default.Actor bldg.Amenities mustEqual Nil bldg.Zone mustEqual Zone.Nowhere bldg.Faction mustEqual PlanetSideEmpire.NEUTRAL @@ -120,7 +120,7 @@ class BuildingControl1Test extends ActorTest { "construct" in { val bldg = Building("Building", 0, 10, Zone.Nowhere, StructureType.Building) bldg.Actor = system.actorOf(Props(classOf[BuildingControl], bldg), "test") - assert(bldg.Actor != ActorRef.noSender) + assert(bldg.Actor != Default.Actor) } } } diff --git a/common/src/test/scala/objects/DefaultTest.scala b/common/src/test/scala/objects/DefaultTest.scala new file mode 100644 index 00000000..224a5e08 --- /dev/null +++ b/common/src/test/scala/objects/DefaultTest.scala @@ -0,0 +1,49 @@ +// Copyright (c) 2020 PSForever +package objects + +import akka.actor.DeadLetter +import akka.testkit.TestProbe +import base.ActorTest +import net.psforever.objects.Default +import org.specs2.mutable.Specification + +import scala.concurrent.duration._ + +class DefaultTest extends Specification { + "Default.Cancellable" should { + "always act like it can be cancelled successfully" in { + Default.Cancellable.cancel mustEqual true + } + + "always act like it was cancelled successfully" in { + Default.Cancellable.isCancelled mustEqual true + } + } +} + +class DefaultActorStartedTest extends ActorTest { + "Default.Actor" should { + "send messages to deadLetters" in { + //after being started + Default(system) + val probe = new TestProbe(system) + system.eventStream.subscribe(probe.ref, classOf[DeadLetter]) + Default.Actor ! "hello world" + val msg1 = probe.receiveOne(250 milliseconds) + assert(msg1.isInstanceOf[DeadLetter]) + assert(msg1.asInstanceOf[DeadLetter].message equals "hello world") + + //if it was stopped + system.stop(Default.Actor) + Default.Actor ! "hello world" + val msg2 = probe.receiveOne(250 milliseconds) + assert(msg2.isInstanceOf[DeadLetter]) + assert(msg2.asInstanceOf[DeadLetter].message equals "hello world") + } + } +} + +object DefaultActorTest { + //due to being a singleton, the original original value of the Default.Actor is cached here + val Original = Default.Actor +} diff --git a/common/src/test/scala/objects/DeployableTest.scala b/common/src/test/scala/objects/DeployableTest.scala index a60456c1..cc318f97 100644 --- a/common/src/test/scala/objects/DeployableTest.scala +++ b/common/src/test/scala/objects/DeployableTest.scala @@ -592,11 +592,11 @@ class TurretControlInitializeTest extends ActorTest { "initialize" in { val obj = new TurretDeployable(GlobalDefinitions.spitfire_turret) obj.GUID = PlanetSideGUID(1) - assert(obj.Actor == ActorRef.noSender) + assert(obj.Actor == Default.Actor) val init = system.actorOf(Props(classOf[DeployableTest.TurretInitializer], obj), "init_turret_test") init ! "initialize" expectNoMessage(200 milliseconds) - assert(obj.Actor != ActorRef.noSender) + assert(obj.Actor != Default.Actor) } } } @@ -609,11 +609,11 @@ class TurretControlUninitializeTest extends ActorTest { obj.GUID = PlanetSideGUID(1) init ! "initialize" expectNoMessage(200 milliseconds) - assert(obj.Actor != ActorRef.noSender) + assert(obj.Actor != Default.Actor) init ! "uninitialize" expectNoMessage(200 milliseconds) - assert(obj.Actor == ActorRef.noSender) + assert(obj.Actor == Default.Actor) } } } diff --git a/common/src/test/scala/objects/DeploymentTest.scala b/common/src/test/scala/objects/DeploymentTest.scala index ef11da72..c0bf4bf2 100644 --- a/common/src/test/scala/objects/DeploymentTest.scala +++ b/common/src/test/scala/objects/DeploymentTest.scala @@ -1,7 +1,7 @@ // Copyright (c) 2017 PSForever package objects -import akka.actor.{Actor, ActorSystem, Props} +import akka.actor.{Actor, ActorRef, ActorSystem, Props} import base.ActorTest import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.{GlobalDefinitions, Vehicle} @@ -46,7 +46,7 @@ class DeploymentBehavior1Test extends ActorTest { "Deployment" should { "construct" in { val obj = DeploymentTest.SetUpAgent - assert(obj.Actor != Actor.noSender) + assert(obj.Actor != ActorRef.noSender) assert(obj.DeploymentState == DriveState.Mobile) } } diff --git a/common/src/test/scala/objects/DoorTest.scala b/common/src/test/scala/objects/DoorTest.scala index 2a5bc597..b488aa48 100644 --- a/common/src/test/scala/objects/DoorTest.scala +++ b/common/src/test/scala/objects/DoorTest.scala @@ -1,9 +1,9 @@ // Copyright (c) 2017 PSForever package objects -import akka.actor.{ActorRef, ActorSystem, Props} +import akka.actor.{ActorSystem, Props} import base.ActorTest -import net.psforever.objects.{Avatar, GlobalDefinitions, Player} +import net.psforever.objects.{Avatar, Default, GlobalDefinitions, Player} import net.psforever.objects.serverobject.doors.{Door, DoorControl} import net.psforever.objects.serverobject.structures.{Building, StructureType} import net.psforever.objects.zones.Zone @@ -59,7 +59,7 @@ class DoorControl1Test extends ActorTest { "construct" in { val door = Door(GlobalDefinitions.door) door.Actor = system.actorOf(Props(classOf[DoorControl], door), "door") - assert(door.Actor != ActorRef.noSender) + assert(door.Actor != Default.Actor) } } } diff --git a/common/src/test/scala/objects/FacilityTurretTest.scala b/common/src/test/scala/objects/FacilityTurretTest.scala index 72d9664d..23f6ab9b 100644 --- a/common/src/test/scala/objects/FacilityTurretTest.scala +++ b/common/src/test/scala/objects/FacilityTurretTest.scala @@ -1,10 +1,10 @@ // Copyright (c) 2017 PSForever package objects -import akka.actor.{ActorRef, Props} +import akka.actor.Props import akka.testkit.TestProbe import base.ActorTest -import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Tool} +import net.psforever.objects.{Avatar, Default, GlobalDefinitions, Player, Tool} import net.psforever.objects.definition.ToolDefinition import net.psforever.objects.guid.NumberPoolHub import net.psforever.objects.guid.source.LimitedNumberSource @@ -94,7 +94,7 @@ class FacilityTurretControl1Test extends ActorTest { "construct" in { val obj = FacilityTurret(GlobalDefinitions.manned_turret) obj.Actor = system.actorOf(Props(classOf[FacilityTurretControl], obj), "turret-control") - assert(obj.Actor != ActorRef.noSender) + assert(obj.Actor != Default.Actor) } } } diff --git a/common/src/test/scala/objects/IFFLockTest.scala b/common/src/test/scala/objects/IFFLockTest.scala index fe60a530..a82973ad 100644 --- a/common/src/test/scala/objects/IFFLockTest.scala +++ b/common/src/test/scala/objects/IFFLockTest.scala @@ -1,10 +1,10 @@ // Copyright (c) 2017 PSForever package objects -import akka.actor.{ActorRef, ActorSystem, Props} +import akka.actor.{ActorSystem, Props} import base.ActorTest +import net.psforever.objects.{Avatar, Default, GlobalDefinitions, Player} import net.psforever.objects.serverobject.CommonMessages -import net.psforever.objects.{Avatar, GlobalDefinitions, Player} import net.psforever.objects.serverobject.locks.{IFFLock, IFFLockControl} import net.psforever.objects.serverobject.structures.{Building, StructureType} import net.psforever.objects.zones.Zone @@ -48,7 +48,7 @@ class IFFLockControl1Test extends ActorTest { "construct" in { val lock = IFFLock(GlobalDefinitions.lock_external) lock.Actor = system.actorOf(Props(classOf[IFFLockControl], lock), "lock-control") - assert(lock.Actor != ActorRef.noSender) + assert(lock.Actor != Default.Actor) } } } diff --git a/common/src/test/scala/objects/LockerTest.scala b/common/src/test/scala/objects/LockerTest.scala index b1f9d017..82f0b12c 100644 --- a/common/src/test/scala/objects/LockerTest.scala +++ b/common/src/test/scala/objects/LockerTest.scala @@ -1,7 +1,7 @@ // Copyright (c) 2017 PSForever package objects -import akka.actor.{ActorRef, Props} +import akka.actor.Props import base.ActorTest import net.psforever.objects.GlobalDefinitions import net.psforever.objects.serverobject.affinity.FactionAffinity @@ -19,8 +19,8 @@ class LockerTest extends Specification { "Locker" should { "construct" in { - val locker = new Locker() - locker.Actor mustEqual ActorRef.noSender + new Locker() + ok } } } diff --git a/common/src/test/scala/objects/PlayerControlTest.scala b/common/src/test/scala/objects/PlayerControlTest.scala index 7a2a4667..90338965 100644 --- a/common/src/test/scala/objects/PlayerControlTest.scala +++ b/common/src/test/scala/objects/PlayerControlTest.scala @@ -187,7 +187,7 @@ class PlayerControlRepairTest extends ActorTest { assert(originalArmor < player2.MaxArmor) player2.Actor ! CommonMessages.Use(player1, Some(tool)) - val msg_avatar = avatarProbe.receiveN(5, 500 milliseconds) + val msg_avatar = avatarProbe.receiveN(5, 1000 milliseconds) assert( msg_avatar.head match { case AvatarServiceMessage("TestCharacter1", AvatarAction.SendResponse(_, InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _))) => true diff --git a/common/src/test/scala/objects/SpawnTubeTest.scala b/common/src/test/scala/objects/SpawnTubeTest.scala index 96b37921..89cf686f 100644 --- a/common/src/test/scala/objects/SpawnTubeTest.scala +++ b/common/src/test/scala/objects/SpawnTubeTest.scala @@ -1,9 +1,9 @@ // Copyright (c) 2017 PSForever package objects -import akka.actor.{ActorRef, Props} +import akka.actor.Props import base.ActorTest -import net.psforever.objects.GlobalDefinitions +import net.psforever.objects.{Default, GlobalDefinitions} import net.psforever.objects.serverobject.tube.{SpawnTube, SpawnTubeControl, SpawnTubeDefinition} import org.specs2.mutable.Specification @@ -18,7 +18,7 @@ class SpawnTubeTest extends Specification { "SpawnTube" should { "construct" in { val obj = SpawnTube(GlobalDefinitions.ams_respawn_tube) - obj.Actor mustEqual ActorRef.noSender + obj.Actor mustEqual Default.Actor obj.Definition mustEqual GlobalDefinitions.ams_respawn_tube } } @@ -29,7 +29,7 @@ class SpawnTubeControlTest extends ActorTest { "construct" in { val obj = SpawnTube(GlobalDefinitions.ams_respawn_tube) obj.Actor = system.actorOf(Props(classOf[SpawnTubeControl], obj), "spawn-tube") - assert(obj.Actor != ActorRef.noSender) + assert(obj.Actor != Default.Actor) } } } diff --git a/common/src/test/scala/objects/UtilityTest.scala b/common/src/test/scala/objects/UtilityTest.scala index c7f200ab..afd88a43 100644 --- a/common/src/test/scala/objects/UtilityTest.scala +++ b/common/src/test/scala/objects/UtilityTest.scala @@ -20,7 +20,7 @@ class UtilityTest extends Specification { obj.UtilType mustEqual UtilityType.order_terminala obj().isInstanceOf[Terminal] mustEqual true obj().asInstanceOf[Terminal].Definition.ObjectId mustEqual 613 - obj().asInstanceOf[Terminal].Actor mustEqual ActorRef.noSender + obj().asInstanceOf[Terminal].Actor mustEqual Default.Actor } "create an order_terminalb object" in { @@ -28,7 +28,7 @@ class UtilityTest extends Specification { obj.UtilType mustEqual UtilityType.order_terminalb obj().isInstanceOf[Terminal] mustEqual true obj().asInstanceOf[Terminal].Definition.ObjectId mustEqual 614 - obj().asInstanceOf[Terminal].Actor mustEqual ActorRef.noSender + obj().asInstanceOf[Terminal].Actor mustEqual Default.Actor } "create a matrix_terminalc object" in { @@ -36,7 +36,7 @@ class UtilityTest extends Specification { obj.UtilType mustEqual UtilityType.matrix_terminalc obj().isInstanceOf[Terminal] mustEqual true obj().asInstanceOf[Terminal].Definition.ObjectId mustEqual 519 - obj().asInstanceOf[Terminal].Actor mustEqual ActorRef.noSender + obj().asInstanceOf[Terminal].Actor mustEqual Default.Actor } "create an ams_respawn_tube object" in { @@ -44,7 +44,7 @@ class UtilityTest extends Specification { obj.UtilType mustEqual UtilityType.ams_respawn_tube obj().isInstanceOf[SpawnTube] mustEqual true obj().asInstanceOf[SpawnTube].Definition.ObjectId mustEqual 49 - obj().asInstanceOf[SpawnTube].Actor mustEqual ActorRef.noSender + obj().asInstanceOf[SpawnTube].Actor mustEqual Default.Actor } "create a teleportpad_terminal object" in { @@ -52,7 +52,7 @@ class UtilityTest extends Specification { obj.UtilType mustEqual UtilityType.teleportpad_terminal obj().isInstanceOf[Terminal] mustEqual true obj().asInstanceOf[Terminal].Definition.ObjectId mustEqual 853 - obj().asInstanceOf[Terminal].Actor mustEqual ActorRef.noSender + obj().asInstanceOf[Terminal].Actor mustEqual Default.Actor } "produce a telepad object through the teleportpad_terminal" in { @@ -78,7 +78,7 @@ class UtilityTest extends Specification { obj.UtilType mustEqual UtilityType.internal_router_telepad_deployable obj().isInstanceOf[Utility.InternalTelepad] mustEqual true obj().asInstanceOf[Utility.InternalTelepad].Definition.ObjectId mustEqual 744 - obj().asInstanceOf[Utility.InternalTelepad].Actor mustEqual ActorRef.noSender + obj().asInstanceOf[Utility.InternalTelepad].Actor mustEqual Default.Actor } "internal_router_telepad_deployable can keep track of an object's GUID (presumedly, it's a Telepad)" in { @@ -139,11 +139,11 @@ class UtilityTerminalATest extends ActorTest { "wire an order_terminala Actor" in { val obj = Utility(UtilityType.order_terminala, UtilityTest.vehicle) obj().GUID = PlanetSideGUID(1) - assert(obj().Actor == ActorRef.noSender) + assert(obj().Actor == Default.Actor) system.actorOf(Props(classOf[UtilityTest.SetupControl], obj), "test") ! "" receiveOne(Duration.create(500, "ms")) //consume and discard - assert(obj().Actor != ActorRef.noSender) + assert(obj().Actor != Default.Actor) } } } @@ -153,11 +153,11 @@ class UtilityTerminalBTest extends ActorTest { "wire an order_terminalb Actor" in { val obj = Utility(UtilityType.order_terminalb, UtilityTest.vehicle) obj().GUID = PlanetSideGUID(1) - assert(obj().Actor == ActorRef.noSender) + assert(obj().Actor == Default.Actor) system.actorOf(Props(classOf[UtilityTest.SetupControl], obj), "test") ! "" receiveOne(Duration.create(500, "ms")) //consume and discard - assert(obj().Actor != ActorRef.noSender) + assert(obj().Actor != Default.Actor) } } } @@ -167,11 +167,11 @@ class UtilityTerminalCTest extends ActorTest { "wire a matrix_terminalc Actor" in { val obj = Utility(UtilityType.matrix_terminalc, UtilityTest.vehicle) obj().GUID = PlanetSideGUID(1) - assert(obj().Actor == ActorRef.noSender) + assert(obj().Actor == Default.Actor) system.actorOf(Props(classOf[UtilityTest.SetupControl], obj), "test") ! "" receiveOne(Duration.create(500, "ms")) //consume and discard - assert(obj().Actor != ActorRef.noSender) + assert(obj().Actor != Default.Actor) } } } @@ -181,11 +181,11 @@ class UtilityRespawnTubeTest extends ActorTest { "wire an ams_respawn_tube Actor" in { val obj = Utility(UtilityType.ams_respawn_tube, UtilityTest.vehicle) obj().GUID = PlanetSideGUID(1) - assert(obj().Actor == ActorRef.noSender) + assert(obj().Actor == Default.Actor) system.actorOf(Props(classOf[UtilityTest.SetupControl], obj), "test") ! "" receiveOne(Duration.create(500, "ms")) //consume and discard - assert(obj().Actor != ActorRef.noSender) + assert(obj().Actor != Default.Actor) } } } @@ -195,11 +195,11 @@ class UtilityTelepadTerminalTest extends ActorTest { "wire a teleportpad_terminal Actor" in { val obj = Utility(UtilityType.teleportpad_terminal, UtilityTest.vehicle) obj().GUID = PlanetSideGUID(1) - assert(obj().Actor == ActorRef.noSender) + assert(obj().Actor == Default.Actor) system.actorOf(Props(classOf[UtilityTest.SetupControl], obj), "test") ! "" receiveOne(Duration.create(500, "ms")) //consume and discard - assert(obj().Actor != ActorRef.noSender) + assert(obj().Actor != Default.Actor) } } } @@ -211,12 +211,12 @@ class UtilityInternalTelepadTest extends ActorTest { veh.GUID = PlanetSideGUID(101) val obj = Utility(UtilityType.internal_router_telepad_deployable, veh) obj().GUID = PlanetSideGUID(1) - assert(obj().Actor == ActorRef.noSender) + assert(obj().Actor == Default.Actor) assert(obj().asInstanceOf[Utility.InternalTelepad].Router.contains(veh.GUID)) system.actorOf(Props(classOf[UtilityTest.SetupControl], obj), "test") ! "" receiveOne(Duration.create(500, "ms")) //consume and discard - assert(obj().Actor != ActorRef.noSender) + assert(obj().Actor != Default.Actor) assert(obj().asInstanceOf[Utility.InternalTelepad].Router.contains(veh.GUID)) } } diff --git a/common/src/test/scala/objects/VehicleSpawnPadTest.scala b/common/src/test/scala/objects/VehicleSpawnPadTest.scala index 3d56e304..469fcba1 100644 --- a/common/src/test/scala/objects/VehicleSpawnPadTest.scala +++ b/common/src/test/scala/objects/VehicleSpawnPadTest.scala @@ -1,9 +1,8 @@ // Copyright (c) 2017 PSForever package objects -import akka.actor.ActorRef +import net.psforever.objects.{Default, GlobalDefinitions} import net.psforever.objects.serverobject.pad.VehicleSpawnPad -import net.psforever.objects.GlobalDefinitions import org.specs2.mutable.Specification class VehicleSpawnPadTest extends Specification { @@ -16,7 +15,7 @@ class VehicleSpawnPadTest extends Specification { "VehicleSpawnPad" should { "construct" in { val obj = VehicleSpawnPad(GlobalDefinitions.mb_pad_creation) - obj.Actor mustEqual ActorRef.noSender + obj.Actor mustEqual Default.Actor obj.Definition mustEqual GlobalDefinitions.mb_pad_creation } } diff --git a/common/src/test/scala/objects/ZoneTest.scala b/common/src/test/scala/objects/ZoneTest.scala index 1b814178..533c5c6e 100644 --- a/common/src/test/scala/objects/ZoneTest.scala +++ b/common/src/test/scala/objects/ZoneTest.scala @@ -92,10 +92,6 @@ class ZoneTest extends Specification { "Zone" should { "construct" in { val zone = new Zone("home3", map13, 13) - zone.GUID mustEqual ActorRef.noSender - zone.Ground mustEqual ActorRef.noSender - zone.Transport mustEqual ActorRef.noSender - //zone also has a unique identifier system but it can't be accessed without its the Actor GUID being initialized zone.EquipmentOnGround mustEqual List.empty[Equipment] zone.Vehicles mustEqual List.empty[Vehicle] zone.Players mustEqual List.empty[Player] diff --git a/common/src/test/scala/objects/number/RegisterTest.scala b/common/src/test/scala/objects/number/RegisterTest.scala index ef9314cb..fbce828e 100644 --- a/common/src/test/scala/objects/number/RegisterTest.scala +++ b/common/src/test/scala/objects/number/RegisterTest.scala @@ -1,7 +1,7 @@ // Copyright (c) 2017 PSForever package objects.number -import akka.actor.Actor +import akka.actor.ActorRef import net.psforever.objects.guid.actor.Register import org.specs2.mutable.Specification @@ -18,11 +18,11 @@ class RegisterTest extends Specification { } "construct (object, callback)" in { - val reg = Register(obj, Actor.noSender) + val reg = Register(obj, ActorRef.noSender) reg.obj mustEqual obj reg.number.isEmpty mustEqual true reg.name.isEmpty mustEqual true - reg.callback.contains(Actor.noSender) mustEqual true + reg.callback.contains(ActorRef.noSender) mustEqual true } "construct (object, suggested number)" in { @@ -34,11 +34,11 @@ class RegisterTest extends Specification { } "construct (object, suggested number, callback)" in { - val reg = Register(obj, 5, Actor.noSender) + val reg = Register(obj, 5, ActorRef.noSender) reg.obj mustEqual obj reg.number.contains(5) mustEqual true reg.name.isEmpty mustEqual true - reg.callback.contains(Actor.noSender) mustEqual true + reg.callback.contains(ActorRef.noSender) mustEqual true } "construct (object, pool name)" in { @@ -50,11 +50,11 @@ class RegisterTest extends Specification { } "construct (object, pool name, callback)" in { - val reg = Register(obj, "pool", Actor.noSender) + val reg = Register(obj, "pool", ActorRef.noSender) reg.obj mustEqual obj reg.number.isEmpty mustEqual true reg.name.contains("pool") mustEqual true - reg.callback.contains(Actor.noSender) mustEqual true + reg.callback.contains(ActorRef.noSender) mustEqual true } } } diff --git a/common/src/test/scala/objects/terminal/ImplantTerminalMechTest.scala b/common/src/test/scala/objects/terminal/ImplantTerminalMechTest.scala index 387728d1..d9193af9 100644 --- a/common/src/test/scala/objects/terminal/ImplantTerminalMechTest.scala +++ b/common/src/test/scala/objects/terminal/ImplantTerminalMechTest.scala @@ -1,8 +1,9 @@ // Copyright (c) 2017 PSForever package objects.terminal -import akka.actor.{ActorRef, ActorSystem, Props} +import akka.actor.{ActorSystem, Props} import base.ActorTest +import net.psforever.objects.{Avatar, Default, GlobalDefinitions, Player} import net.psforever.objects.definition.SeatDefinition import net.psforever.objects.guid.NumberPoolHub import net.psforever.objects.guid.source.LimitedNumberSource @@ -12,7 +13,6 @@ import net.psforever.objects.serverobject.structures.{Building, StructureType} import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.objects.vehicles.Seat import net.psforever.objects.zones.{Zone, ZoneMap} -import net.psforever.objects.{Avatar, GlobalDefinitions, Player} import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3} import org.specs2.mutable.Specification @@ -35,7 +35,7 @@ class ImplantTerminalMechTest extends Specification { "Implant_Terminal_Mech" should { "construct" in { val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech) - obj.Actor mustEqual ActorRef.noSender + obj.Actor mustEqual Default.Actor obj.Definition mustEqual GlobalDefinitions.implant_terminal_mech obj.Seats.keySet mustEqual Set(0) obj.Seats(0).isInstanceOf[Seat] mustEqual true @@ -65,7 +65,7 @@ class ImplantTerminalMechControl1Test extends ActorTest { "construct" in { val obj = ImplantTerminalMech(GlobalDefinitions.implant_terminal_mech) obj.Actor = system.actorOf(Props(classOf[ImplantTerminalMechControl], obj), "mech") - assert(obj.Actor != ActorRef.noSender) + assert(obj.Actor != Default.Actor) } } } diff --git a/common/src/test/scala/service/LocalServiceTest.scala b/common/src/test/scala/service/LocalServiceTest.scala index cd4dafad..3ef697c2 100644 --- a/common/src/test/scala/service/LocalServiceTest.scala +++ b/common/src/test/scala/service/LocalServiceTest.scala @@ -6,6 +6,7 @@ import base.ActorTest import net.psforever.objects.{GlobalDefinitions, SensorDeployable, Vehicle} import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.terminals.{ProximityTerminal, Terminal} +import net.psforever.objects.vehicles.VehicleControl import net.psforever.objects.zones.Zone import net.psforever.packet.game._ import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3} @@ -206,6 +207,7 @@ class ToggleTeleportSystemTest extends ActorTest { "LocalService" should { "pass ToggleTeleportSystem" in { val router = Vehicle(GlobalDefinitions.router) + router.Actor = system.actorOf(Props(classOf[VehicleControl], router), "test-router") val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") service ! Service.Join("test") service ! LocalServiceMessage("test", LocalAction.ToggleTeleportSystem(PlanetSideGUID(10), router, None)) diff --git a/common/src/test/scala/service/VehicleServiceTest.scala b/common/src/test/scala/service/VehicleServiceTest.scala index 9a5ffce7..e59343f3 100644 --- a/common/src/test/scala/service/VehicleServiceTest.scala +++ b/common/src/test/scala/service/VehicleServiceTest.scala @@ -4,6 +4,7 @@ package service import akka.actor.Props import base.ActorTest import net.psforever.objects._ +import net.psforever.objects.vehicles.VehicleControl import net.psforever.objects.zones.Zone import net.psforever.types.{PlanetSideGUID, _} import services.{Service, ServiceManager} @@ -171,6 +172,7 @@ class KickPassengerTest extends ActorTest { class LoadVehicleTest extends ActorTest { ServiceManager.boot(system) val vehicle = Vehicle(GlobalDefinitions.quadstealth) + vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "test-vehicle") val cdata = vehicle.Definition.Packet.ConstructorData(vehicle).get "VehicleService" should { @@ -260,6 +262,7 @@ class TransferPassengerChannelTest extends ActorTest { "pass TransferPassengerChannel" in { val service = system.actorOf(Props(classOf[VehicleService], Zone.Nowhere), "v-service") val fury = Vehicle(GlobalDefinitions.fury) + fury.Actor = system.actorOf(Props(classOf[VehicleControl], fury), "test-fury") service ! Service.Join("test") service ! VehicleServiceMessage("test", VehicleAction.TransferPassengerChannel(PlanetSideGUID(10), "old_channel", "new_channel", fury, PlanetSideGUID(11))) expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.TransferPassengerChannel("old_channel", "new_channel", fury, PlanetSideGUID(11)))) diff --git a/pslogin/src/main/scala/LoginSessionActor.scala b/pslogin/src/main/scala/LoginSessionActor.scala index c279082f..1e667374 100644 --- a/pslogin/src/main/scala/LoginSessionActor.scala +++ b/pslogin/src/main/scala/LoginSessionActor.scala @@ -12,7 +12,7 @@ import MDCContextAware.Implicits._ import com.github.mauricio.async.db.general.ArrayRowData import com.github.mauricio.async.db.{Connection, QueryResult} import net.psforever.objects.Account -import net.psforever.objects.DefaultCancellable +import net.psforever.objects.Default import net.psforever.types.PlanetSideEmpire import net.psforever.WorldConfig import services.ServiceManager @@ -40,9 +40,9 @@ class LoginSessionActor extends Actor with MDCContextAware { var sessionId : Long = 0 var leftRef : ActorRef = ActorRef.noSender var rightRef : ActorRef = ActorRef.noSender - var accountIntermediary : ActorRef = Actor.noSender + var accountIntermediary : ActorRef = ActorRef.noSender - var updateServerListTask : Cancellable = DefaultCancellable.obj + var updateServerListTask : Cancellable = Default.Cancellable var ipAddress : String = "" var hostName : String = "" diff --git a/pslogin/src/main/scala/PacketCodingActor.scala b/pslogin/src/main/scala/PacketCodingActor.scala index 310e3c7b..42559447 100644 --- a/pslogin/src/main/scala/PacketCodingActor.scala +++ b/pslogin/src/main/scala/PacketCodingActor.scala @@ -5,7 +5,7 @@ import scodec.Attempt.{Failure, Successful} import scodec.bits._ import org.log4s.MDC import MDCContextAware.Implicits._ -import net.psforever.objects.DefaultCancellable +import net.psforever.objects.Default import net.psforever.packet.control.{HandleGamePacket, _} import scala.annotation.tailrec @@ -52,7 +52,7 @@ class PacketCodingActor extends Actor with MDCContextAware { // Due to the fact the client can send `RelatedA` packets out of order, we need to keep a buffer of which subslots arrived correctly, order them // and then act accordingly to send the missing subslot packet after a specified timeout private var relatedALog : ArrayBuffer[Int] = ArrayBuffer() - private var relatedABufferTimeout : Cancellable = DefaultCancellable.obj + private var relatedABufferTimeout : Cancellable = Default.Cancellable def AddSlottedPacketToLog(subslot: Int, packet : ByteVector): Unit = { val log_limit = 500 // Number of SlottedMetaPackets to keep in history @@ -386,7 +386,7 @@ class PacketCodingActor extends Actor with MDCContextAware { // The client has indicated it's received up to a certain subslot, that means we can purge the log of any subslots prior to and including the confirmed subslot // Find where this subslot is stored in the packet log (if at all) and drop anything to the left of it, including itself - if(relatedABufferTimeout.isCancelled || relatedABufferTimeout == DefaultCancellable.obj) { + if(relatedABufferTimeout.isCancelled || relatedABufferTimeout == Default.Cancellable) { val pos = slottedPacketLog.keySet.toArray.indexOf(subslot) if(pos != -1) { slottedPacketLog = slottedPacketLog.drop(pos+1) diff --git a/pslogin/src/main/scala/PsLogin.scala b/pslogin/src/main/scala/PsLogin.scala index f68d4535..1978b2ad 100644 --- a/pslogin/src/main/scala/PsLogin.scala +++ b/pslogin/src/main/scala/PsLogin.scala @@ -13,6 +13,7 @@ import ch.qos.logback.core.util.StatusPrinter import com.typesafe.config.ConfigFactory import net.psforever.config.{Invalid, Valid} import net.psforever.crypto.CryptoInterface +import net.psforever.objects.Default import net.psforever.objects.zones._ import net.psforever.objects.guid.TaskResolver import net.psforever.psadmin.PsAdminActor @@ -225,6 +226,7 @@ object PsLogin { /** Start up the main actor system. This "system" is the home for all actors running on this server */ system = ActorSystem("PsLogin") + Default(system) logger.info("Starting actor pipelines") /** Create pipelines for the login and world servers diff --git a/pslogin/src/main/scala/SessionRouter.scala b/pslogin/src/main/scala/SessionRouter.scala index 8b7fc805..f911e31d 100644 --- a/pslogin/src/main/scala/SessionRouter.scala +++ b/pslogin/src/main/scala/SessionRouter.scala @@ -48,7 +48,7 @@ class SessionRouter(role : String, pipeline : List[SessionPipeline]) extends Act val sessionById = mutable.Map[Long, Session]() val sessionByActor = mutable.Map[ActorRef, Session]() val closePacket = PacketCoding.EncodePacket(ConnectionClose()).require.bytes - var accountIntermediary : ActorRef = Actor.noSender + var accountIntermediary : ActorRef = ActorRef.noSender var sessionId = 0L // this is a connection session, not an actual logged in session ID var inputRef : ActorRef = ActorRef.noSender diff --git a/pslogin/src/main/scala/TcpListener.scala b/pslogin/src/main/scala/TcpListener.scala index 92fa8260..39280caf 100644 --- a/pslogin/src/main/scala/TcpListener.scala +++ b/pslogin/src/main/scala/TcpListener.scala @@ -25,7 +25,7 @@ class TcpListener[T <: Actor](actorClass : Class[T], var sessionId = 0L var bytesRecevied = 0L var bytesSent = 0L - var nextActor : ActorRef = Actor.noSender + var nextActor : ActorRef = ActorRef.noSender def receive = { case Tcp.Bound(local) => diff --git a/pslogin/src/main/scala/UdpListener.scala b/pslogin/src/main/scala/UdpListener.scala index dffc15f1..73a145e3 100644 --- a/pslogin/src/main/scala/UdpListener.scala +++ b/pslogin/src/main/scala/UdpListener.scala @@ -38,7 +38,7 @@ class UdpListener(nextActorProps : Props, var bytesRecevied = 0L var bytesSent = 0L - var nextActor : ActorRef = Actor.noSender + var nextActor : ActorRef = ActorRef.noSender def receive = { case Udp.Bound(local) => diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 2cea782e..e180a64f 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -5,10 +5,7 @@ import com.github.mauricio.async.db.general.ArrayRowData import com.github.mauricio.async.db.{Connection, QueryResult} import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicInteger - -import net.psforever.WorldConfig import org.log4s.{Logger, MDC} - import scala.annotation.{switch, tailrec} import scala.collection.mutable.LongMap import scala.concurrent.{Await, Future, Promise} @@ -62,6 +59,7 @@ import net.psforever.packet.game._ import net.psforever.packet.game.objectcreate.{ConstructorData, DetailedCharacterData, DroppedItemData, ObjectClass, ObjectCreateMessageParent, PlacementData} import net.psforever.packet.game.{HotSpotInfo => PacketHotSpotInfo} import net.psforever.types._ +import net.psforever.WorldConfig import services.{RemoverActor, Service, ServiceManager} import services.account.{AccountPersistenceService, PlayerToken, ReceiveAccountData, RetrieveAccountData} import services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage, AvatarServiceResponse} @@ -89,8 +87,8 @@ class WorldSessionActor extends Actor var chatService : ActorRef = ActorRef.noSender var galaxyService : ActorRef = ActorRef.noSender var squadService : ActorRef = ActorRef.noSender - var taskResolver : ActorRef = Actor.noSender - var cluster : ActorRef = Actor.noSender + var taskResolver : ActorRef = ActorRef.noSender + var cluster : ActorRef = ActorRef.noSender var continent : Zone = Zone.Nowhere var account : Account = null var player : Player = null @@ -163,9 +161,13 @@ class WorldSessionActor extends Actor var squadSetup : () => Unit = FirstTimeSquadSetup var squadUpdateCounter : Int = 0 val queuedSquadActions : Seq[() => Unit] = Seq(SquadUpdates, NoSquadUpdates, NoSquadUpdates, NoSquadUpdates) - /** Keeps track of the number of PlayerStateMessageUpstream messages received by the client + /** Upstream message counter
+ * Checks for server acknowledgement of the following messages:
+ * `PlayerStateMessageUpstream`
+ * `VehicleStateMessage` (driver seat only)
+ * `ChildObjectStateMessage` (any seat but driver)
* As they should arrive roughly every 250 milliseconds this allows for a very crude method of scheduling tasks up to four times per second */ - private var playerStateMessageUpstreamCount = 0 + private var upstreamMessageCount : Int = 0 var zoningType : Zoning.Method.Value = Zoning.Method.None var zoningChatMessageType : ChatMessageType.Value = ChatMessageType.CMT_QUIT var zoningStatus : Zoning.Status.Value = Zoning.Status.None @@ -174,17 +176,26 @@ class WorldSessionActor extends Actor lazy val unsignedIntMaxValue : Long = Int.MaxValue.toLong * 2L + 1L var serverTime : Long = 0 var amsSpawnPoints : List[SpawnPoint] = Nil + /** a flag for the zone having finished loading during zoning + * `None` when no zone is loaded + * `Some(true)` when a zone has successfully loaded + * `Some(false)` when the loading process has failed or was executed but did not complete for some reason + * */ + var zoneLoaded : Option[Boolean] = None + /** a flag that forces the current zone to reload itself during a zoning operation */ + var zoneReload : Boolean = false + var turnCounter : PlanetSideGUID=>Unit = TurnCounterDuringInterim - var clientKeepAlive : Cancellable = DefaultCancellable.obj - var progressBarUpdate : Cancellable = DefaultCancellable.obj - var reviveTimer : Cancellable = DefaultCancellable.obj - var respawnTimer : Cancellable = DefaultCancellable.obj - var cargoMountTimer : Cancellable = DefaultCancellable.obj - var cargoDismountTimer : Cancellable = DefaultCancellable.obj - var antChargingTick : Cancellable = DefaultCancellable.obj - var antDischargingTick : Cancellable = DefaultCancellable.obj - var zoningTimer : Cancellable = DefaultCancellable.obj - var zoningReset : Cancellable = DefaultCancellable.obj + var clientKeepAlive : Cancellable = Default.Cancellable + var progressBarUpdate : Cancellable = Default.Cancellable + var reviveTimer : Cancellable = Default.Cancellable + var respawnTimer : Cancellable = Default.Cancellable + var cargoMountTimer : Cancellable = Default.Cancellable + var cargoDismountTimer : Cancellable = Default.Cancellable + var antChargingTick : Cancellable = Default.Cancellable + var antDischargingTick : Cancellable = Default.Cancellable + var zoningTimer : Cancellable = Default.Cancellable + var zoningReset : Cancellable = Default.Cancellable /** * Convert a boolean value into an integer value. * Use: `true:Int` or `false:Int` @@ -832,14 +843,14 @@ class WorldSessionActor extends Actor case Zone.Population.PlayerHasLeft(zone, Some(tplayer)) => if(tplayer.isAlive) { - log.info(s"$tplayer has left zone ${zone.Id}") + log.info(s"${tplayer.Name} has left zone ${zone.Id}") } case Zone.Population.PlayerCanNotSpawn(zone, tplayer) => - log.warn(s"$tplayer can not spawn in zone ${zone.Id}; why?") + log.warn(s"${tplayer.Name} can not spawn in zone ${zone.Id}; why?") case Zone.Population.PlayerAlreadySpawned(zone, tplayer) => - log.warn(s"$tplayer is already spawned on zone ${zone.Id}; a clerical error?") + log.warn(s"${tplayer.Name} is already spawned on zone ${zone.Id}; a clerical error?") case Zone.Lattice.SpawnPoint(zone_id, spawn_tube) => CancelZoningProcess() @@ -917,7 +928,7 @@ class WorldSessionActor extends Actor PutItemOnGround(item, pos, orient) case Zone.Ground.CanNotDropItem(zone, item, reason) => - log.warn(s"DropItem: $player tried to drop a $item on the ground, but $reason") + log.warn(s"DropItem: ${player.Name} tried to drop a $item on the ground, but $reason") if(!item.HasGUID) { log.warn(s"DropItem: zone ${continent.Id} contents may be in disarray") } @@ -951,9 +962,9 @@ class WorldSessionActor extends Actor case Zone.Ground.CanNotPickupItem(zone, item_guid, _) => zone.GUID(item_guid) match { case Some(item) => - log.warn(s"DropItem: finding a $item on the ground was suggested, but $player can not reach it") + log.warn(s"DropItem: finding a $item on the ground was suggested, but ${player.Name} can not reach it") case None => - log.warn(s"DropItem: finding an item ($item_guid) on the ground was suggested, but $player can not see it") + log.warn(s"DropItem: finding an item ($item_guid) on the ground was suggested, but ${player.Name} can not see it") sendResponse(ObjectDeleteMessage(item_guid, 0)) } @@ -1504,6 +1515,9 @@ class WorldSessionActor extends Actor val weaponsEnabled = (continent.Map.Name != "map11" && continent.Map.Name != "map12" && continent.Map.Name != "map13") sendResponse(LoadMapMessage(continent.Map.Name, continent.Id, 40100, 25, weaponsEnabled, continent.Map.Checksum)) setupAvatarFunc() //important! the LoadMapMessage must be processed by the client before the avatar is created + turnCounter = TurnCounterDuringInterim + context.system.scheduler.scheduleOnce(delay = 2000 millisecond, self, SetCurrentAvatar(tplayer, 200)) + upstreamMessageCount = 0 persist() case PlayerLoaded(tplayer) => @@ -1511,8 +1525,9 @@ class WorldSessionActor extends Actor log.info(s"Player ${tplayer.Name} will respawn") player = tplayer setupAvatarFunc() + upstreamMessageCount = 0 persist() - self ! SetCurrentAvatar(tplayer) + self ! SetCurrentAvatar(tplayer, 200) case PlayerFailedToLoad(tplayer) => player.Continent match { @@ -1520,12 +1535,66 @@ class WorldSessionActor extends Actor failWithError(s"${tplayer.Name} failed to load anywhere") } - case SetCurrentAvatar(tplayer) => - if(tplayer.Actor == ActorRef.noSender) { - respawnTimer = context.system.scheduler.scheduleOnce(100 milliseconds, self, SetCurrentAvatar(tplayer)) + /* + The user is either already in the current zone and merely transporting himself from one location to another, + also called "dying", or occasionally "deconstructing," + or is completely switching in between zones. + These correspond to the message NewPlayerLoaded for the case of "dying" or the latter zone switching case, + and PlayerLoaded for "deconstruction." + In the latter case, the user must wait for the zone to be recognized as loaded for the server + and this is performed through the send LoadMapMessage, receive BeginZoningMessage exchange + The user's player should have already been registered into the new zone + and is at some stage of being added to the zone in which they will have control agency in that zone. + Whether or not the zone is loaded in the earlier case depends on the destination with respect to the current location. + Once all of the following is (assumed) accomplished, + the servwer will attempt to declare that user's player the avatar of the user's client. + Reception of certain packets that represent "reported user activity" after that marks the end of avatar loading. + If the maximum number of unsuccessful attempts is reached, some course of action is taken. + If the player dies, the process does not need to continue. + He may or may not be accompanied by a vehicle at any stage of this process. + */ + case SetCurrentAvatar(tplayer, max_attempts, attempt) => + respawnTimer.cancel + val waitingOnUpstream = upstreamMessageCount == 0 + if(attempt >= max_attempts && waitingOnUpstream) { + zoneLoaded match { + case None | Some(false) => + log.warn("SetCurrentAvatar: failed to load intended destination zone; routing to faction sanctuary") + RequestSanctuaryZoneSpawn(tplayer, continent.Number) + case _ => + log.warn("SetCurrentAvatar: the zone loaded but elements remain unready; restarting the process ...") + val pos = shiftPosition.getOrElse(player.Position) + val orient = shiftOrientation.getOrElse(player.Orientation) + sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, pos, player.Faction, true)) + val toZoneId = continent.Id + tplayer.Die + continent.Population ! Zone.Population.Leave(avatar) //does not matter if it doesn't work + zoneLoaded = None + zoneReload = true + LoadZonePhysicalSpawnPoint(toZoneId, pos, orient, respawnTime = 0L) + } } - else { - HandleSetCurrentAvatar(tplayer) + else if(tplayer.isAlive) { + if( + zoneLoaded.contains(true) && + tplayer.HasGUID && tplayer.Actor != Default.Actor && (continent.GUID(tplayer.VehicleSeated) match { + case Some(o : Vehicle) => o.HasGUID && o.Actor != Default.Actor && !o.Destroyed + case _ => true + }) + ) { + if(waitingOnUpstream) { + beginZoningSetCurrentAvatarFunc(tplayer) + respawnTimer = context.system.scheduler.scheduleOnce( + delay = (if(attempt <= max_attempts / 2) 10 else 5) seconds, + self, + SetCurrentAvatar(tplayer, max_attempts, attempt + max_attempts / 3) + ) + } + //if not the condition above, player has started playing normally + } + else { + respawnTimer = context.system.scheduler.scheduleOnce(500 milliseconds, self, SetCurrentAvatar(tplayer, max_attempts, attempt + 1)) + } } case NtuCharging(tplayer, vehicle) => @@ -1737,7 +1806,7 @@ class WorldSessionActor extends Actor case _ => //fall back to sanctuary/prior? - log.error(s"LoginInfo: player $playerName could not be found in game world") + log.error(s"LoginInfo: player ${player.Name}Name could not be found in game world") self ! PlayerToken.LoginInfo(playerName, Zone.Nowhere, pos) } @@ -1898,7 +1967,7 @@ class WorldSessionActor extends Actor /** * Use the zoning process using some spawnable entity in the destination zone. - * @param zone the destination zone + * @param zone the destination zone * @param spawnPosition the destination spawn position * @param spawnOrientation the destination spawn orientation */ @@ -2586,7 +2655,7 @@ class WorldSessionActor extends Actor CancelZoningProcessWithDescriptiveReason("cancel_mount") val obj_guid : PlanetSideGUID = obj.GUID val player_guid : PlanetSideGUID = tplayer.GUID - log.info(s"MountVehicleMsg: $player_guid mounts $obj_guid @ $seat_num") + log.info(s"MountVehicleMsg: ${player.Name}_guid mounts $obj_guid @ $seat_num") CancelAllProximityUnits() sendResponse(PlanetsideAttributeMessage(obj_guid, 0, obj.Health)) sendResponse(PlanetsideAttributeMessage(obj_guid, 68, obj.Shields)) //shield health @@ -2658,16 +2727,16 @@ class WorldSessionActor extends Actor log.warn(s"DismountVehicleMsg: $obj is some generic mountable object and nothing will happen") case Mountable.CanNotMount(obj : Vehicle, seat_num) => - log.warn(s"MountVehicleMsg: $tplayer attempted to mount $obj's seat $seat_num, but was not allowed") + log.warn(s"MountVehicleMsg: ${tplayer.Name} attempted to mount $obj's seat $seat_num, but was not allowed") if(obj.SeatPermissionGroup(seat_num).contains(AccessPermissionGroup.Driver)) { sendResponse(ChatMsg(ChatMessageType.CMT_OPEN, false, "", "You are not the driver of this vehicle.", None)) } case Mountable.CanNotMount(obj : Mountable, seat_num) => - log.warn(s"MountVehicleMsg: $tplayer attempted to mount $obj's seat $seat_num, but was not allowed") + log.warn(s"MountVehicleMsg: ${tplayer.Name} attempted to mount $obj's seat $seat_num, but was not allowed") case Mountable.CanNotDismount(obj, seat_num) => - log.warn(s"DismountVehicleMsg: $tplayer attempted to dismount $obj's seat $seat_num, but was not allowed") + log.warn(s"DismountVehicleMsg: ${tplayer.Name} attempted to dismount $obj's seat $seat_num, but was not allowed") } } @@ -2866,7 +2935,7 @@ class WorldSessionActor extends Actor lastTerminalOrderFulfillment = true case Terminal.InfantryLoadout(exosuit, subtype, holsters, inventory) => - log.info(s"$tplayer wants to change equipment loadout to their option #${msg.unk1 + 1}") + log.info(s"${tplayer.Name} wants to change equipment loadout to their option #${msg.unk1 + 1}") sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Loadout, true)) //sanitize exo-suit for change val originalSuit = player.ExoSuit @@ -2906,7 +2975,7 @@ class WorldSessionActor extends Actor } } else { - log.warn(s"$tplayer no longer has permission to wear the exo-suit type $exosuit; will wear $fallbackSuit instead") + log.warn(s"${tplayer.Name} no longer has permission to wear the exo-suit type $exosuit; will wear $fallbackSuit instead") (fallbackSuit, fallbackSubtype) } //update suit interally (holsters must be empty before this point) @@ -3014,7 +3083,7 @@ class WorldSessionActor extends Actor lastTerminalOrderFulfillment = true case Terminal.VehicleLoadout(definition, weapons, inventory) => - log.info(s"$tplayer wants to change their vehicle equipment loadout to their option #${msg.unk1 + 1}") + log.info(s"${tplayer.Name} wants to change their vehicle equipment loadout to their option #${msg.unk1 + 1}") FindLocalVehicle match { case Some(vehicle) => sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Loadout, true)) @@ -3112,7 +3181,7 @@ class WorldSessionActor extends Actor case Terminal.LearnImplant(implant) => val terminal_guid = msg.terminal_guid val implant_type = implant.Type - val message = s"Implants: $tplayer wants to learn $implant_type" + val message = s"Implants: ${tplayer.Name} wants to learn $implant_type" val (interface, slotNumber) = tplayer.VehicleSeated match { case Some(mech_guid) => ( @@ -3166,13 +3235,13 @@ class WorldSessionActor extends Actor } if(interface.contains(terminal_guid.guid) && slotNumber.isDefined) { val slot = slotNumber.get - log.info(s"$tplayer is selling $implant_type - take from slot $slot") + log.info(s"${tplayer.Name} is selling $implant_type - take from slot $slot") player.Actor ! Player.UninitializeImplant(slot) sendResponse(AvatarImplantMessage(tplayer.GUID, ImplantAction.Remove, slot, 0)) sendResponse(ItemTransactionResultMessage(terminal_guid, TransactionType.Sell, true)) } else { - val message = s"$tplayer can not sell $implant_type" + val message = s"${tplayer.Name} can not sell $implant_type" if(interface.isEmpty) { log.warn(s"$message - not interacting with a terminal") } @@ -3230,7 +3299,7 @@ class WorldSessionActor extends Actor } case None => - log.error(s"$tplayer wanted to spawn a vehicle, but there was no spawn pad associated with terminal ${msg.terminal_guid} to accept it") + log.error(s"${tplayer.Name} wanted to spawn a vehicle, but there was no spawn pad associated with terminal ${msg.terminal_guid} to accept it") } lastTerminalOrderFulfillment = true @@ -3764,6 +3833,7 @@ class WorldSessionActor extends Actor //player died during setup; probably a relog player.Actor ! Player.Die() } + upstreamMessageCount = 0 } /** @@ -3772,7 +3842,7 @@ class WorldSessionActor extends Actor * @param tplayer the target player */ def SetCurrentAvatarNormally(tplayer : Player) : Unit = { - self ! SetCurrentAvatar(tplayer) + HandleSetCurrentAvatar(tplayer) } /** @@ -3787,6 +3857,7 @@ class WorldSessionActor extends Actor */ def SetCurrentAvatarUponDeployment(tplayer : Player) : Unit = { beginZoningSetCurrentAvatarFunc = SetCurrentAvatarNormally + upstreamMessageCount = 0 continent.Actor ! Zone.Lattice.RequestSpawnPoint(continent.Number, tplayer, 0) } @@ -4061,6 +4132,7 @@ class WorldSessionActor extends Actor case msg@BeginZoningMessage() => log.info("Reticulating splines ...") + zoneLoaded = None val continentId = continent.Id traveler.zone = continentId val faction = player.Faction @@ -4358,7 +4430,8 @@ class WorldSessionActor extends Actor } } continent.VehicleEvents ! VehicleServiceMessage(continent.Id, VehicleAction.UpdateAmsSpawnPoint(continent)) - beginZoningSetCurrentAvatarFunc(player) + upstreamMessageCount = 0 + zoneLoaded = Some(true) case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yaw_upper, seq_time, unk3, is_crouching, is_jumping, jump_thrust, is_cloaking, unk5, unk6) => if (player.death_by == -1) { @@ -4366,14 +4439,14 @@ class WorldSessionActor extends Actor Thread.sleep(300) sendResponse(DropSession(sessionId, "kick by GM")) } - playerStateMessageUpstreamCount += 1 + turnCounter(avatar_guid) val isMoving = WorldEntity.isMoving(vel) val isMovingPlus = isMoving || is_jumping || jump_thrust if(isMovingPlus) { CancelZoningProcessWithDescriptiveReason("cancel_motion") } - if(deadState == DeadState.Alive && playerStateMessageUpstreamCount % 2 == 0) { // Regen stamina roughly every 500ms + if(deadState == DeadState.Alive && upstreamMessageCount % 2 == 0) { // Regen stamina roughly every 500ms if(player.skipStaminaRegenForTurns > 0) { //do not renew stamina for a while player.skipStaminaRegenForTurns -= 1 @@ -4432,7 +4505,15 @@ class WorldSessionActor extends Actor case msg@ChildObjectStateMessage(object_guid, pitch, yaw) => //the majority of the following check retrieves information to determine if we are in control of the child FindContainedWeapon match { - case (Some(_), Some(tool)) => + case (Some(o), Some(tool)) => + (o match { + case mount : Mountable => mount.PassengerInSeat(player) + case _ => None + }) match { + case None | Some(0) => ; + case Some(_) => + turnCounter(player.GUID) + } if(tool.GUID == object_guid) { //TODO set tool orientation? player.Orientation = Vector3(0f, pitch, yaw) @@ -4445,7 +4526,7 @@ class WorldSessionActor extends Actor log.warn(s"ChildObjectState: ${player.Name} can not find any controllable agent, let alone #${object_guid.guid}") case (None, _) => ; //TODO status condition of "playing getting out of vehicle to allow for late packets without warning - //log.warn(s"ChildObjectState: player $player not related to anything with a controllable agent") + //log.warn(s"ChildObjectState: player ${player.Name} not related to anything with a controllable agent") } if (player.death_by == -1) { sendResponse(ChatMsg(ChatMessageType.UNK_71, true, "", "Your account has been logged out by a Customer Service Representative.", None)) @@ -4457,8 +4538,9 @@ class WorldSessionActor extends Actor if(deadState == DeadState.Alive) { GetVehicleAndSeat() match { case (Some(obj), Some(0)) => - val seat = obj.Seats(0) //we're driving the vehicle + turnCounter(player.GUID) + val seat = obj.Seats(0) player.Position = pos //convenient if(seat.ControlledWeapon.isEmpty) { player.Orientation = Vector3.z(ang.z) //convenient @@ -4503,7 +4585,7 @@ class WorldSessionActor extends Actor } case msg@VehicleSubStateMessage(vehicle_guid, player_guid, vehicle_pos, vehicle_ang, vel, unk1, unk2) => - //log.info(s"VehicleSubState: $vehicle_guid, $player_guid, $vehicle_pos, $vehicle_ang, $vel, $unk1, $unk2") + //log.info(s"VehicleSubState: $vehicle_guid, ${player.Name}_guid, $vehicle_pos, $vehicle_ang, $vel, $unk1, $unk2") case msg@ProjectileStateMessage(projectile_guid, shot_pos, shot_vel, shot_orient, seq, end, target_guid) => //log.trace(s"ProjectileState: $msg") @@ -5207,13 +5289,13 @@ class WorldSessionActor extends Actor continent.Ground ! Zone.Ground.DropItem(item, player.Position, player.Orientation) } case None => - log.warn(s"DropItem: $player wanted to drop a $anItem, but it wasn't at hand") + log.warn(s"DropItem: ${player.Name} wanted to drop a $anItem, but it wasn't at hand") } case Some(obj) => //TODO LLU - log.warn(s"DropItem: $player wanted to drop a $obj, but that isn't possible") + log.warn(s"DropItem: ${player.Name} wanted to drop a $obj, but that isn't possible") case None => sendResponse(ObjectDeleteMessage(item_guid, 0)) //this is fine; item doesn't exist to the server anyway - log.warn(s"DropItem: $player wanted to drop an item ($item_guid), but it was nowhere to be found") + log.warn(s"DropItem: ${player.Name} wanted to drop an item ($item_guid), but it was nowhere to be found") } case msg@PickupItemMessage(item_guid, player_guid, unk1, unk2) => @@ -5227,7 +5309,7 @@ class WorldSessionActor extends Actor sendResponse(ActionResultMessage.Fail(16)) //error code? } case _ => - log.warn(s"PickupItem: $player requested an item that doesn't exist in this zone; assume client-side garbage data") + log.warn(s"PickupItem: ${player.Name} requested an item that doesn't exist in this zone; assume client-side garbage data") sendResponse(ObjectDeleteMessage(item_guid, 0)) } @@ -5288,7 +5370,7 @@ class WorldSessionActor extends Actor val before = player.DrawnSlot if(before != held_holsters) { if(player.ExoSuit == ExoSuitType.MAX && held_holsters != 0) { - log.info(s"ObjectHeld: $player is denied changing hands to $held_holsters as a MAX") + log.info(s"ObjectHeld: ${player.Name} is denied changing hands to $held_holsters as a MAX") player.DrawnSlot = 0 sendResponse(ObjectHeldMessage(avatar_guid, 0, true)) } @@ -5603,7 +5685,7 @@ class WorldSessionActor extends Actor CancelZoningProcessWithDescriptiveReason("cancel_use") if(obj.isBackpack) { if(equipment.isEmpty) { - log.info(s"UseItem: $player looting the corpse of $obj") + log.info(s"UseItem: ${player.Name} looting the corpse of $obj") sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) accessedContainer = Some(obj) } @@ -5716,7 +5798,7 @@ class WorldSessionActor extends Actor CancelZoningProcessWithDescriptiveReason("cancel_use") locker.Actor ! CommonMessages.Use(player, Some(item)) case None if locker.Faction == player.Faction || !locker.HackedBy.isEmpty => - log.trace(s"UseItem: $player accessing a locker") + log.trace(s"UseItem: ${player.Name} accessing a locker") CancelZoningProcessWithDescriptiveReason("cancel_use") val container = player.Locker accessedContainer = Some(container) @@ -6208,7 +6290,7 @@ class WorldSessionActor extends Actor } } else { - log.warn(s"WeaponFireMessage: $player's ${tool.Definition.Name} projectile is too far from owner position at time of discharge ($distanceToOwner > $acceptableDistanceToOwner); suspect") + log.warn(s"WeaponFireMessage: ${player.Name}'s ${tool.Definition.Name} projectile is too far from owner position at time of discharge ($distanceToOwner > $acceptableDistanceToOwner); suspect") } } case _ => ; @@ -6370,7 +6452,7 @@ class WorldSessionActor extends Actor case out @ Some(_ : Mountable) => out case _ => - dismountWarning(s"DismountVehicleMsg: player $player_guid not considered seated in a mountable entity") + dismountWarning(s"DismountVehicleMsg: player ${player.Name}_guid not considered seated in a mountable entity") None }) match { case Some(obj : Mountable) => @@ -6395,7 +6477,7 @@ class WorldSessionActor extends Actor } case None => - dismountWarning(s"DismountVehicleMsg: can not find where player $player_guid is seated in mountable ${player.VehicleSeated}") + dismountWarning(s"DismountVehicleMsg: can not find where player ${player.Name}_guid is seated in mountable ${player.VehicleSeated}") } case _ => dismountWarning(s"DismountVehicleMsg: can not find mountable entity ${player.VehicleSeated}") @@ -6418,17 +6500,17 @@ class WorldSessionActor extends Actor case Some(seat_num : Int) => obj.Actor ! Mountable.TryDismount(tplayer, seat_num) case None => - dismountWarning(s"DismountVehicleMsg: can not find where other player $player_guid is seated in mountable $obj_guid") + dismountWarning(s"DismountVehicleMsg: can not find where other player ${player.Name}_guid is seated in mountable $obj_guid") } case (None, _) => ; - log.warn(s"DismountVehicleMsg: $player can not find his vehicle") + log.warn(s"DismountVehicleMsg: ${player.Name} can not find his vehicle") case (_, None) => ; - log.warn(s"DismountVehicleMsg: player $player_guid could not be found to kick") + log.warn(s"DismountVehicleMsg: player ${player.Name}_guid could not be found to kick") case _ => log.warn(s"DismountVehicleMsg: object is either not a Mountable or not a Player") } case None => - log.warn(s"DismountVehicleMsg: $player does not own a vehicle") + log.warn(s"DismountVehicleMsg: ${player.Name} does not own a vehicle") } } @@ -6445,7 +6527,7 @@ class WorldSessionActor extends Actor } } else { - log.warn(s"DeployRequest: $player does not own the deploying $vehicle_guid object") + log.warn(s"DeployRequest: ${player.Name} does not own the deploying $vehicle_guid object") } case msg @ AvatarGrenadeStateMessage(player_guid, state) => @@ -6523,7 +6605,7 @@ class WorldSessionActor extends Actor } } else { - log.warn(s"Vehicle attributes: $player does not own vehicle ${vehicle.GUID} and can not change it") + log.warn(s"Vehicle attributes: ${player.Name} does not own vehicle ${vehicle.GUID} and can not change it") } case _ => log.warn(s"echo unknown attributes behavior") @@ -9320,7 +9402,7 @@ class WorldSessionActor extends Actor val player_guid : PlanetSideGUID = tplayer.GUID val obj_guid : PlanetSideGUID = obj.GUID PlayerActionsToCancel() - log.info(s"MountVehicleMsg: $player_guid mounts $obj @ $seatNum") + log.info(s"MountVehicleMsg: ${player.Name}_guid mounts $obj @ $seatNum") sendResponse(ObjectAttachMessage(obj_guid, player_guid, seatNum)) continent.VehicleEvents ! VehicleServiceMessage(continent.Id, VehicleAction.MountVehicle(player_guid, obj_guid, seatNum)) } @@ -9353,7 +9435,7 @@ 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 if obj.CanDamage && obj.Actor != ActorRef.noSender => + case obj : Player if obj.CanDamage && obj.Actor != Default.Actor => if(obj.spectator) { player.death_by = -1 // little thing for auto kick } @@ -10006,7 +10088,7 @@ class WorldSessionActor extends Actor * @return a tuple composed of an `ActorRef` destination and a message to send to that destination */ def LoadZoneAsPlayer(tplayer : Player, zone_id : String) : (ActorRef, Any) = { - if(zone_id == continent.Id) { + if(!zoneReload && zone_id == continent.Id) { if(player.isBackpack) { //important! test the actor-wide player ref, not the parameter //respawning from unregistered player (taskResolver, RegisterAvatar(tplayer)) @@ -10115,7 +10197,7 @@ class WorldSessionActor extends Actor continent.VehicleEvents ! VehicleServiceMessage(name, VehicleAction.TransferPassengerChannel(pguid, s"${cargo.Actor}", toChannel, cargo, topLevel)) } // - if(zone_id == continent.Id) { + if(!zoneReload && zone_id == continent.Id) { if(vehicle.Definition == GlobalDefinitions.droppod) { //instant action droppod in the same zone (taskResolver, RegisterDroppod(vehicle, player)) @@ -10175,7 +10257,7 @@ class WorldSessionActor extends Actor **/ def LoadZoneInVehicleAsPassenger(vehicle : Vehicle, zone_id : String) : (ActorRef, Any) = { log.info(s"LoadZoneInVehicleAsPassenger: ${player.Name} is the passenger of a ${vehicle.Definition.Name}") - if(zone_id == continent.Id) { + if(!zoneReload && zone_id == continent.Id) { //transferring a vehicle between spawn points (warp gates) in the same zone (self, PlayerLoaded(player)) } @@ -10226,9 +10308,11 @@ class WorldSessionActor extends Actor * It also sets up actions for the new zone loading process. */ def LoadZoneCommonTransferActivity() : Unit = { + zoneLoaded = None + zoneReload = false if(player.VehicleOwned.nonEmpty && player.VehicleSeated != player.VehicleOwned) { continent.GUID(player.VehicleOwned) match { - case Some(vehicle : Vehicle) if vehicle.Actor != ActorRef.noSender => + case Some(vehicle : Vehicle) if vehicle.Actor != Default.Actor => vehicle.Actor ! Vehicle.Ownership(None) case _ => ; } @@ -11427,6 +11511,35 @@ class WorldSessionActor extends Actor } } + /** + * The upstream counter accumulates when the server receives sp[ecific messages from the client. + * It counts upwards until it reach maximum value, and then starts over. + * When it starts over, which should take an exceptionally long time to achieve, + * it starts counting at one rather than zero. + * @param p the player's globally unique identifier number + */ + def NormalTurnCounter(p : PlanetSideGUID) : Unit = { + upstreamMessageCount = 1 + upstreamMessageCount % Int.MaxValue + } + + /** + * During the interim period between the avatar being in one place/zone + * and completing the process of transitioning to another place/zone, + * the upstream message counter is zero'd + * awaiting new activity from the client. + * Until new upstream messages that pass some tests against their data start being reported, + * the counter does not accumulate properly. + * @param guid the player's globally unique identifier number + */ + def TurnCounterDuringInterim(guid : PlanetSideGUID) : Unit = { + if(player.GUID == guid && player.Zone == continent) { + turnCounter = NormalTurnCounter + } + else { + upstreamMessageCount = 0 + } + } + def failWithError(error : String) = { log.error(error) sendResponse(ConnectionClose()) @@ -11553,7 +11666,7 @@ object WorldSessionActor { private final case class PlayerFailedToLoad(tplayer : Player) private final case class CreateCharacter(name : String, head : Int, voice : CharacterVoice.Value, gender : CharacterGender.Value, empire : PlanetSideEmpire.Value) private final case class ListAccountCharacters() - private final case class SetCurrentAvatar(tplayer : Player) + private final case class SetCurrentAvatar(tplayer : Player, max_attempts : Int, attempt : Int = 0) private final case class ZoningReset() final val ftes = (