From 3a9058bee9bdbfa695a22431055e7140b88c3b0a Mon Sep 17 00:00:00 2001 From: FateJH Date: Sun, 6 May 2018 16:09:30 -0400 Subject: [PATCH] attempting to solve issue of corpses with no guid; catching vehicles with no guid as well --- .../src/main/scala/WorldSessionActor.scala | 5 +- .../avatar/support/CorpseRemovalActor.scala | 77 ++++++++++++++----- .../vehicle/support/DeconstructionActor.scala | 52 ++++++++++--- 3 files changed, 105 insertions(+), 29 deletions(-) diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index b7e803aa8..e5a212b9d 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -127,9 +127,9 @@ class WorldSessionActor extends Actor with MDCContextAware { continent.Population ! Zone.Population.Release(avatar) player.VehicleSeated match { case None => - continent.Population ! Zone.Corpse.Add(player) FriskCorpse(player) //TODO eliminate dead letters if(!WellLootedCorpse(player)) { + continent.Population ! Zone.Corpse.Add(player) avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.Release(player, continent)) taskResolver ! GUIDTask.UnregisterLocker(player.Locker)(continent.GUID) //rest of player will be cleaned up with corpses } @@ -304,6 +304,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case AvatarResponse.ObjectDelete(item_guid, unk) => if(tplayer_guid != guid) { + log.info(s"Made to delete item $item_guid") sendResponse(ObjectDeleteMessage(item_guid, unk)) } @@ -1601,10 +1602,10 @@ class WorldSessionActor extends Actor with MDCContextAware { continent.Population ! Zone.Population.Release(avatar) player.VehicleSeated match { case None => - continent.Population ! Zone.Corpse.Add(player) //TODO move back out of this match case when changing below issue FriskCorpse(player) if(!WellLootedCorpse(player)) { TurnPlayerIntoCorpse(player) + continent.Population ! Zone.Corpse.Add(player) //TODO move back out of this match case when changing below issue avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.Release(player, continent)) } else { //no items in inventory; leave no corpse diff --git a/pslogin/src/main/scala/services/avatar/support/CorpseRemovalActor.scala b/pslogin/src/main/scala/services/avatar/support/CorpseRemovalActor.scala index ace1fc920..5b916d822 100644 --- a/pslogin/src/main/scala/services/avatar/support/CorpseRemovalActor.scala +++ b/pslogin/src/main/scala/services/avatar/support/CorpseRemovalActor.scala @@ -15,17 +15,25 @@ import scala.concurrent.duration._ class CorpseRemovalActor extends Actor { private var burial : Cancellable = DefaultCancellable.obj - private var corpses : List[CorpseRemovalActor.Entry] = List() + private var decomposition : Cancellable = DefaultCancellable.obj + private var buriedCorpses : List[CorpseRemovalActor.Entry] = List() + private var taskResolver : ActorRef = Actor.noSender private[this] val log = org.log4s.getLogger override def postStop() = { //Cart Master: See you on Thursday. - corpses.foreach { BurialTask } - corpses = Nil + burial.cancel + decomposition.cancel + + corpses.foreach(corpse => { + BurialTask(corpse) + LastRitesTask(corpse) + }) + buriedCorpses.foreach { LastRitesTask } } def receive : Receive = { @@ -72,23 +80,33 @@ class CorpseRemovalActor extends Actor { CorpseRemovalActor.recursiveFindCorpse(corpses.iterator, targets.head) match { case None => ; case Some(index) => - BurialTask(corpses(index)) + if(index == 0) { + burial.cancel + } + decomposition.cancel + buriedCorpses = buriedCorpses :+ corpses(index) corpses = corpses.take(index) ++ corpses.drop(index+1) + if(index == 0) { + RetimeFirstTask() + } + import scala.concurrent.ExecutionContext.Implicits.global + decomposition = context.system.scheduler.scheduleOnce(500 milliseconds, self, CorpseRemovalActor.TryDelete()) } } else { log.debug(s"multiple target corpses submitted for early cleanup: $targets") + burial.cancel + decomposition.cancel //cumbersome partition //a - find targets from corpses - (for { + buriedCorpses = buriedCorpses ++ (for { a <- targets b <- corpses if b.corpse == a && b.corpse.Continent.equals(a.Continent) && b.corpse.HasGUID && a.HasGUID && b.corpse.GUID == a.GUID - } yield b).foreach { BurialTask } - //b - corpses after the found targets are - //removed (note: cull any non-GUID entries while at it) + } yield b) + //b - corpses after the found targets are removed (note: cull any non-GUID entries while at it) corpses = (for { a <- targets b <- corpses @@ -99,15 +117,33 @@ class CorpseRemovalActor extends Actor { } yield b).sortBy(_.timeAlive) } RetimeFirstTask() + import scala.concurrent.ExecutionContext.Implicits.global + decomposition = context.system.scheduler.scheduleOnce(500 milliseconds, self, CorpseRemovalActor.TryDelete()) } - case CorpseRemovalActor.Dispose() => + case CorpseRemovalActor.StartDelete() => burial.cancel + decomposition.cancel val now : Long = System.nanoTime val (buried, rotting) = corpses.partition(entry => { now - entry.time >= entry.timeAlive }) corpses = rotting + buriedCorpses = buriedCorpses ++ buried buried.foreach { BurialTask } RetimeFirstTask() + if(buriedCorpses.nonEmpty) { + import scala.concurrent.ExecutionContext.Implicits.global + burial = context.system.scheduler.scheduleOnce(500 milliseconds, self, CorpseRemovalActor.TryDelete()) + } + + case CorpseRemovalActor.TryDelete() => + decomposition.cancel + val (decomposed, rotting) = buriedCorpses.partition(entry => { !entry.zone.Corpses.contains(entry.corpse) }) + buriedCorpses = rotting + decomposed.foreach { LastRitesTask } + if(rotting.nonEmpty) { + import scala.concurrent.ExecutionContext.Implicits.global + decomposition = context.system.scheduler.scheduleOnce(500 milliseconds, self, CorpseRemovalActor.TryDelete()) + } case CorpseRemovalActor.FailureToWork(target, zone, ex) => //Cart Master: Oh, I can't take him like that. It's against regulations. @@ -122,21 +158,24 @@ class CorpseRemovalActor extends Actor { if(corpses.nonEmpty) { val short_timeout : FiniteDuration = math.max(1, corpses.head.timeAlive - (now - corpses.head.time)) nanoseconds import scala.concurrent.ExecutionContext.Implicits.global - burial = context.system.scheduler.scheduleOnce(short_timeout, self, CorpseRemovalActor.Dispose()) + burial = context.system.scheduler.scheduleOnce(short_timeout, self, CorpseRemovalActor.StartDelete()) } } def BurialTask(entry : CorpseRemovalActor.Entry) : Unit = { - //Cart master: Nine pence. val target = entry.corpse - val zone = entry.zone - target.Position = Vector3.Zero //somewhere it will not disturb anything entry.zone.Population ! Zone.Corpse.Remove(target) - context.parent ! AvatarServiceMessage(zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, target.GUID)) - taskResolver ! BurialTask(target, zone) + context.parent ! AvatarServiceMessage(entry.zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, target.GUID)) } - def BurialTask(corpse : Player, zone : Zone) : TaskResolver.GiveTask = { + def LastRitesTask(entry : CorpseRemovalActor.Entry) : Unit = { + //Cart master: Nine pence. + val target = entry.corpse + target.Position = Vector3.Zero //somewhere it will not disturb anything + taskResolver ! LastRitesTask(target, entry.zone) + } + + def LastRitesTask(corpse : Player, zone : Zone) : TaskResolver.GiveTask = { import net.psforever.objects.guid.{GUIDTask, Task} TaskResolver.GiveTask ( new Task() { @@ -170,9 +209,11 @@ object CorpseRemovalActor { final case class Entry(corpse : Player, zone : Zone, timeAlive : Long = CorpseRemovalActor.time, time : Long = System.nanoTime()) - final case class FailureToWork(corpse : Player, zone : Zone, ex : Throwable) + private final case class FailureToWork(corpse : Player, zone : Zone, ex : Throwable) - final case class Dispose() + private final case class StartDelete() + + private final case class TryDelete() /** * A recursive function that finds and removes a specific player from a list of players. diff --git a/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala b/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala index 9d7a3efad..9b69fbe40 100644 --- a/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala +++ b/pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala @@ -30,10 +30,19 @@ class DeconstructionActor extends Actor { private var scrappingProcess : Cancellable = DefaultCancellable.obj /** A `List` of currently doomed vehicles */ private var vehicles : List[DeconstructionActor.VehicleEntry] = Nil + /** The periodic `Executor` that cleans up the next vehicle on the list */ + private var heapEmptyProcess : Cancellable = DefaultCancellable.obj + /** A `List` of vehicles that have been removed from the game world and are awaiting deconstruction. */ + private var vehicleScrapHeap : List[DeconstructionActor.VehicleEntry] = Nil /** The manager that helps unregister the vehicle from its current GUID scope */ private var taskResolver : ActorRef = Actor.noSender //private[this] val log = org.log4s.getLogger + override def postStop() : Unit = { + super.postStop() + scrappingProcess.cancel + heapEmptyProcess.cancel + } def receive : Receive = { /* @@ -70,27 +79,46 @@ class DeconstructionActor extends Actor { }) if(vehicles.size == 1) { //we were the only entry so the event must be started from scratch import scala.concurrent.ExecutionContext.Implicits.global - scrappingProcess = context.system.scheduler.scheduleOnce(DeconstructionActor.timeout, self, DeconstructionActor.TryDeleteVehicle()) + scrappingProcess = context.system.scheduler.scheduleOnce(DeconstructionActor.timeout, self, DeconstructionActor.StartDeleteVehicle()) + } + + case DeconstructionActor.StartDeleteVehicle() => + scrappingProcess.cancel + heapEmptyProcess.cancel + val now : Long = System.nanoTime + val (vehiclesToScrap, vehiclesRemain) = PartitionEntries(vehicles, now) + vehicles = vehiclesRemain //entries from original list before partition + vehicleScrapHeap = vehicleScrapHeap ++ vehiclesToScrap //may include existing entries + vehiclesToScrap.foreach(entry => { + val vehicle = entry.vehicle + val zone = entry.zone + zone.Transport ! Zone.Vehicle.Despawn(vehicle) + context.parent ! DeconstructionActor.DeleteVehicle(vehicle.GUID, zone.Id) //call up to the main event system + }) + if(vehiclesRemain.nonEmpty) { + val short_timeout : FiniteDuration = math.max(1, DeconstructionActor.timeout_time - (now - vehiclesRemain.head.time)) nanoseconds + import scala.concurrent.ExecutionContext.Implicits.global + scrappingProcess = context.system.scheduler.scheduleOnce(short_timeout, self, DeconstructionActor.StartDeleteVehicle()) + } + if(vehicleScrapHeap.nonEmpty) { + import scala.concurrent.ExecutionContext.Implicits.global + heapEmptyProcess = context.system.scheduler.scheduleOnce(500 milliseconds, self, DeconstructionActor.TryDeleteVehicle()) } case DeconstructionActor.TryDeleteVehicle() => - scrappingProcess.cancel - val now : Long = System.nanoTime - val (vehiclesToScrap, vehiclesRemain) = PartitionEntries(vehicles, now) - vehicles = vehiclesRemain + heapEmptyProcess.cancel + val (vehiclesToScrap, vehiclesRemain) = vehicleScrapHeap.partition(entry => !entry.zone.Vehicles.contains(entry.vehicle)) + vehicleScrapHeap = vehiclesRemain vehiclesToScrap.foreach(entry => { val vehicle = entry.vehicle val zone = entry.zone vehicle.Position = Vector3.Zero //somewhere it will not disturb anything - entry.zone.Transport ! Zone.Vehicle.Despawn(vehicle) - context.parent ! DeconstructionActor.DeleteVehicle(vehicle.GUID, zone.Id) //call up to the main event system taskResolver ! DeconstructionTask(vehicle, zone) }) if(vehiclesRemain.nonEmpty) { - val short_timeout : FiniteDuration = math.max(1, DeconstructionActor.timeout_time - (now - vehiclesRemain.head.time)) nanoseconds import scala.concurrent.ExecutionContext.Implicits.global - scrappingProcess = context.system.scheduler.scheduleOnce(short_timeout, self, DeconstructionActor.TryDeleteVehicle()) + heapEmptyProcess = context.system.scheduler.scheduleOnce(500 milliseconds, self, DeconstructionActor.TryDeleteVehicle()) } case DeconstructionActor.FailureToDeleteVehicle(localVehicle, localZone, ex) => @@ -195,6 +223,12 @@ object DeconstructionActor { final case class DeleteVehicle(vehicle_guid : PlanetSideGUID, zone_id : String) /** * Internal message used to signal a test of the queued vehicle information. + * Remove all deconstructing vehicles from the game world. + */ + private final case class StartDeleteVehicle() + /** + * Internal message used to signal a test of the queued vehicle information. + * Remove all deconstructing vehicles from the zone's globally unique identifier system. */ private final case class TryDeleteVehicle()