mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-03-09 07:00:27 +00:00
Stripped down LivePlayerList functionality, moving player lists onto the appropriate corresponding zones. Corpses are now created, stored, and deleted.
This commit is contained in:
parent
8a21df429b
commit
20b7726653
10 changed files with 526 additions and 264 deletions
|
|
@ -12,6 +12,7 @@ import MDCContextAware.Implicits._
|
|||
import net.psforever.objects.GlobalDefinitions._
|
||||
import services.ServiceManager.Lookup
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.definition.converter.CorpseConverter
|
||||
import net.psforever.objects.equipment._
|
||||
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
|
||||
import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem}
|
||||
|
|
@ -67,40 +68,37 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
var progressBarUpdate : Cancellable = DefaultCancellable.obj
|
||||
|
||||
override def postStop() = {
|
||||
if(clientKeepAlive != null)
|
||||
clientKeepAlive.cancel()
|
||||
localService ! Service.Leave()
|
||||
vehicleService ! Service.Leave()
|
||||
avatarService ! Service.Leave()
|
||||
LivePlayerList.Remove(sessionId) match {
|
||||
case Some(tplayer) =>
|
||||
tplayer.VehicleSeated match {
|
||||
case Some(vehicle_guid) =>
|
||||
//TODO do this at some other time
|
||||
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(tplayer.GUID, 0, true, vehicle_guid))
|
||||
case None => ;
|
||||
}
|
||||
tplayer.VehicleOwned match {
|
||||
case Some(vehicle_guid) =>
|
||||
continent.GUID(vehicle_guid) match {
|
||||
case Some(vehicle : Vehicle) =>
|
||||
vehicle.Owner = None
|
||||
//TODO temporary solution; to un-own, permit driver seat to Empire access level
|
||||
vehicle.PermissionGroup(10, VehicleLockState.Empire.id)
|
||||
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.SeatPermissions(tplayer.GUID, vehicle_guid, 10, VehicleLockState.Empire.id))
|
||||
case _ => ;
|
||||
}
|
||||
case None => ;
|
||||
}
|
||||
|
||||
if(tplayer.HasGUID) {
|
||||
val guid = tplayer.GUID
|
||||
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectDelete(guid, guid))
|
||||
taskResolver ! GUIDTask.UnregisterAvatar(tplayer)(continent.GUID)
|
||||
//TODO normally, the actual player avatar persists a minute or so after the user disconnects
|
||||
}
|
||||
clientKeepAlive.cancel()
|
||||
localService ! Service.Leave()
|
||||
vehicleService ! Service.Leave()
|
||||
avatarService ! Service.Leave()
|
||||
|
||||
LivePlayerList.Remove(sessionId)
|
||||
if(player != null && player.HasGUID) {
|
||||
val player_guid = player.GUID
|
||||
player.VehicleSeated match {
|
||||
case Some(vehicle_guid) =>
|
||||
//TODO do this at some other time
|
||||
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(player_guid, 0, true, vehicle_guid))
|
||||
case None => ;
|
||||
}
|
||||
player.VehicleOwned match {
|
||||
case Some(vehicle_guid) =>
|
||||
continent.GUID(vehicle_guid) match {
|
||||
case Some(vehicle : Vehicle) =>
|
||||
vehicle.Owner = None
|
||||
//TODO temporary solution; to un-own, permit driver seat to Empire access level
|
||||
vehicle.PermissionGroup(10, VehicleLockState.Empire.id)
|
||||
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.SeatPermissions(player_guid, vehicle_guid, 10, VehicleLockState.Empire.id))
|
||||
case _ => ;
|
||||
}
|
||||
case None => ;
|
||||
}
|
||||
continent.Population ! Zone.Population.Release(avatar)
|
||||
continent.Population ! Zone.Population.Leave(avatar)
|
||||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ObjectDelete(player_guid, player_guid))
|
||||
taskResolver ! GUIDTask.UnregisterAvatar(player)(continent.GUID)
|
||||
//TODO normally, the actual player avatar persists a minute or so after the user disconnects
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -151,6 +149,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case game @ GamePacket(_, _, _) =>
|
||||
handlePktContainer(game)
|
||||
// temporary hack to keep the client from disconnecting
|
||||
//it's been a "temporary hack" since 2016 :P
|
||||
case PokeClient() =>
|
||||
sendResponse(KeepAliveMessage())
|
||||
|
||||
|
|
@ -281,6 +280,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
}
|
||||
|
||||
case AvatarResponse.Release(tplayer) =>
|
||||
if(tplayer_guid != guid) {
|
||||
turnPlayerIntoCorpse(tplayer)
|
||||
}
|
||||
|
||||
case AvatarResponse.Reload(item_guid) =>
|
||||
if(tplayer_guid != guid) {
|
||||
sendResponse(ReloadMessage(item_guid, 1, 0))
|
||||
|
|
@ -973,11 +977,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
case Zone.ClientInitialization(zone) =>
|
||||
val continentNumber = zone.Number
|
||||
val poplist = LivePlayerList.ZonePopulation(continentNumber, _ => true)
|
||||
val poplist = zone.Players
|
||||
val popBO = 0 //TODO black ops test (partition)
|
||||
val popTR = poplist.count(_.Faction == PlanetSideEmpire.TR)
|
||||
val popNC = poplist.count(_.Faction == PlanetSideEmpire.NC)
|
||||
val popVS = poplist.count(_.Faction == PlanetSideEmpire.VS)
|
||||
val popTR = poplist.count(_.faction == PlanetSideEmpire.TR)
|
||||
val popNC = poplist.count(_.faction == PlanetSideEmpire.NC)
|
||||
val popVS = poplist.count(_.faction == PlanetSideEmpire.VS)
|
||||
|
||||
zone.Buildings.foreach({ case(id, building) => initBuilding(continentNumber, id, building) })
|
||||
sendResponse(ZonePopulationUpdateMessage(continentNumber, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO))
|
||||
|
|
@ -990,7 +994,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(ZoneForcedCavernConnectionsMessage(continentNumber, 0))
|
||||
sendResponse(HotSpotUpdateMessage(continentNumber, 1, Nil)) //normally set in bulk; should be fine doing per continent
|
||||
|
||||
case Zone.Population.PlayerHasLeft(zone, None) =>
|
||||
log.info(s"$avatar does not have a body on ${zone.Id}")
|
||||
|
||||
case Zone.Population.PlayerHasLeft(zone, Some(tplayer)) =>
|
||||
if(tplayer.isAlive) {
|
||||
log.info(s"$tplayer has left zone ${zone.Id}")
|
||||
}
|
||||
|
||||
case InterstellarCluster.ClientInitializationComplete() =>
|
||||
LivePlayerList.Add(sessionId, avatar)
|
||||
//PropertyOverrideMessage
|
||||
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 1))
|
||||
sendResponse(ReplicationStreamMessage(5, Some(6), Vector(SquadListing()))) //clear squad list
|
||||
|
|
@ -1002,6 +1015,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
log.info(s"Zone $zoneId has been loaded")
|
||||
player.Continent = zoneId
|
||||
continent = zone
|
||||
continent.Population ! Zone.Population.Join(avatar)
|
||||
taskResolver ! RegisterNewAvatar(player)
|
||||
|
||||
case NewPlayerLoaded(tplayer) =>
|
||||
|
|
@ -1009,7 +1023,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
player = tplayer
|
||||
//LoadMapMessage will cause the client to send back a BeginZoningMessage packet (see below)
|
||||
sendResponse(LoadMapMessage(continent.Map.Name, continent.Id, 40100,25,true,3770441820L))
|
||||
AvatarCreate()
|
||||
AvatarCreate() //important! the LoadMapMessage must be processed by the client before the avatar is created
|
||||
|
||||
case PlayerLoaded(tplayer) =>
|
||||
log.info(s"Player $tplayer has been loaded")
|
||||
|
|
@ -1026,7 +1040,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case SetCurrentAvatar(tplayer) =>
|
||||
player = tplayer
|
||||
val guid = tplayer.GUID
|
||||
LivePlayerList.Assign(continent.Number, sessionId, guid)
|
||||
sendResponse(SetCurrentAvatarMessage(guid,0,0))
|
||||
|
||||
(0 until DetailedCharacterData.numberOfImplantSlots(tplayer.BEP)).foreach(slot => {
|
||||
|
|
@ -1256,7 +1269,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case CharacterRequestAction.Delete =>
|
||||
sendResponse(ActionResultMessage(false, Some(1)))
|
||||
case CharacterRequestAction.Select =>
|
||||
LivePlayerList.Add(sessionId, player)
|
||||
//TODO check if can spawn on last continent/location from player?
|
||||
//TODO if yes, get continent guid accessors
|
||||
//TODO if no, get sanctuary guid accessors and reset the player's expectations
|
||||
|
|
@ -1296,20 +1308,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
)
|
||||
})
|
||||
//load active players in zone
|
||||
LivePlayerList.ZonePopulation(continent.Number, _ => true).foreach(char => {
|
||||
continent.LivePlayers.filterNot(_.GUID == player.GUID).foreach(char => {
|
||||
sendResponse(
|
||||
ObjectCreateMessage(ObjectClass.avatar, char.GUID, char.Definition.Packet.ConstructorData(char).get)
|
||||
)
|
||||
})
|
||||
//load corpses in zone
|
||||
continent.Corpses.foreach( turnPlayerIntoCorpse(_) )
|
||||
//load active vehicles in zone
|
||||
continent.Vehicles.foreach(vehicle => {
|
||||
val definition = vehicle.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
definition.ObjectId,
|
||||
vehicle.GUID,
|
||||
definition.Packet.ConstructorData(vehicle).get
|
||||
)
|
||||
ObjectCreateMessage(definition.ObjectId, vehicle.GUID, definition.Packet.ConstructorData(vehicle).get)
|
||||
)
|
||||
//seat vehicle occupants
|
||||
vehicle.Definition.MountPoints.values.foreach(seat_num => {
|
||||
|
|
@ -1362,7 +1372,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
self ! SetCurrentAvatar(player)
|
||||
|
||||
case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yaw_upper, seq_time, unk3, is_crouching, is_jumping, unk4, is_cloaking, unk5, unk6) =>
|
||||
if(!player.isAlive) {
|
||||
if(player.isAlive) {
|
||||
player.Position = pos
|
||||
player.Velocity = vel
|
||||
player.Orientation = Vector3(player.Orientation.x, pitch, yaw)
|
||||
|
|
@ -1434,10 +1444,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
case msg @ ReleaseAvatarRequestMessage() =>
|
||||
log.info(s"ReleaseAvatarRequest: ${player.GUID} on ${continent.Id} has released")
|
||||
//TODO is it easier to delete the player, then re-create them as a corpse?
|
||||
player.Release
|
||||
continent.Population ! Zone.Population.Release(avatar)
|
||||
continent.Population ! Zone.Corpse.Add(player)
|
||||
val knife = player.Slot(4).Equipment.get
|
||||
taskResolver ! RemoveEquipmentFromSlot(player, knife, 4)
|
||||
sendResponse(PlanetsideAttributeMessage(player.GUID, 6, 1))
|
||||
turnPlayerIntoCorpse(player)
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.Release(player, continent))
|
||||
sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, 2, true))
|
||||
|
||||
case msg @ SpawnRequestMessage(u1, u2, u3, u4, u5) =>
|
||||
|
|
@ -1449,7 +1463,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
val tplayer = SpawnRequest(player) //new player
|
||||
tplayer.Position = tube.Position
|
||||
tplayer.Orientation = tube.Orientation
|
||||
log.info(s"SpawnRequestMessage: new player will spawn in ${building.Id} @ tube ${tube.GUID.guid}")
|
||||
log.info(s"SpawnRequestMessage: new player will spawn in ${building.Id} @ ${tube.GUID.guid}")
|
||||
sendResponse(AvatarDeadStateMessage(DeadState.RespawnTime, 10000, 10000, Vector3.Zero, 2, true))
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
|
@ -1507,7 +1521,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
player.Die
|
||||
sendResponse(PlanetsideAttributeMessage(player_guid, 0, 0))
|
||||
sendResponse(PlanetsideAttributeMessage(player_guid, 2, 0))
|
||||
sendResponse(DestroyMessage(player_guid, player_guid, PlanetSideGUID(0), pos))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 0, 0))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 2, 0))
|
||||
sendResponse(DestroyMessage(player_guid, player_guid, PlanetSideGUID(0), pos)) //how many players get this message?
|
||||
sendResponse(AvatarDeadStateMessage(DeadState.Dead, 300000, 300000, pos, 2, true))
|
||||
}
|
||||
|
||||
|
|
@ -1521,8 +1537,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(DropSession(sessionId, "user quit"))
|
||||
}
|
||||
|
||||
if(contents.trim.equals("!loc")) { //dev hack; consider bang-commands to complement slash-commands
|
||||
echoContents = s"pos=${player.Position.x}, ${player.Position.y}, ${player.Position.z}; ori=${player.Orientation.x}, ${player.Orientation.y}, ${player.Orientation.z}"
|
||||
if(contents.trim.equals("!loc")) { //dev hack; consider bang-commands to complement slash-commands in future
|
||||
echoContents = s"zone=${continent.Id} pos=${player.Position.x},${player.Position.y},${player.Position.z}; ori=${player.Orientation.x},${player.Orientation.y},${player.Orientation.z}"
|
||||
log.info(echoContents)
|
||||
}
|
||||
|
||||
|
|
@ -3315,6 +3331,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
val dcdata = packet.DetailedConstructorData(player).get
|
||||
sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, player.GUID, dcdata))
|
||||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.LoadPlayer(player.GUID, packet.ConstructorData(player).get))
|
||||
continent.Population ! Zone.Population.Spawn(avatar, player)
|
||||
log.debug(s"ObjectCreateDetailedMessage: $dcdata")
|
||||
}
|
||||
|
||||
|
|
@ -3335,6 +3352,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
obj
|
||||
}
|
||||
|
||||
def turnPlayerIntoCorpse(tplayer : Player) : Unit = {
|
||||
//sendResponse(PlanetsideAttributeMessage(tplayer.GUID, 6, 1))
|
||||
sendResponse(
|
||||
ObjectCreateDetailedMessage(ObjectClass.avatar, tplayer.GUID, CorpseConverter.converter.DetailedConstructorData(tplayer).get)
|
||||
)
|
||||
}
|
||||
|
||||
def failWithError(error : String) = {
|
||||
log.error(error)
|
||||
sendResponse(ConnectionClose())
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.avatar
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
|
||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||
import net.psforever.types.{ExoSuitType, Vector3}
|
||||
|
|
@ -24,6 +26,7 @@ object AvatarAction {
|
|||
final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action
|
||||
final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
|
||||
final case class PlayerState(player_guid : PlanetSideGUID, msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Action
|
||||
final case class Release(player : Player, zone : Zone) extends Action
|
||||
final case class Reload(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||
final case class WeaponDryFire(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||
// final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.avatar
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
|
||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||
|
|
@ -24,6 +25,7 @@ object AvatarResponse {
|
|||
final case class ObjectHeld(slot : Int) extends Response
|
||||
final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response
|
||||
final case class PlayerState(msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Response
|
||||
final case class Release(player : Player) extends Response
|
||||
final case class Reload(weapon_guid : PlanetSideGUID) extends Response
|
||||
final case class WeaponDryFire(weapon_guid : PlanetSideGUID) extends Response
|
||||
// final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.avatar
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import services.avatar.support.UndertakerActor
|
||||
import services.{GenericEventBus, Service}
|
||||
|
||||
class AvatarService extends Actor {
|
||||
//import AvatarServiceResponse._
|
||||
private val undertaker : ActorRef = context.actorOf(Props[UndertakerActor], "corpse-removal-agent")
|
||||
undertaker ! "startup"
|
||||
|
||||
private [this] val log = org.log4s.getLogger
|
||||
|
||||
override def preStart = {
|
||||
|
|
@ -87,6 +90,11 @@ class AvatarService extends Actor {
|
|||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(msg, spectator, weapon))
|
||||
)
|
||||
case AvatarAction.Release(player, zone) =>
|
||||
undertaker ! UndertakerActor.AddCorpse(player, zone)
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player.GUID, AvatarResponse.Release(player))
|
||||
)
|
||||
case AvatarAction.Reload(player_guid, weapon_guid) =>
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Reload(weapon_guid))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.avatar.support
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Cancellable}
|
||||
import net.psforever.objects.guid.TaskResolver
|
||||
import net.psforever.objects.{DefaultCancellable, Player}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import services.{Service, ServiceManager}
|
||||
import services.ServiceManager.Lookup
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class UndertakerActor extends Actor {
|
||||
private var burial : Cancellable = DefaultCancellable.obj
|
||||
|
||||
private var corpses : List[UndertakerActor.Entry] = List()
|
||||
|
||||
private var taskResolver : ActorRef = Actor.noSender
|
||||
|
||||
private[this] val log = org.log4s.getLogger("Cart Master")
|
||||
|
||||
override def postStop() = {
|
||||
corpses.foreach { BurialTask }
|
||||
}
|
||||
|
||||
def receive : Receive = {
|
||||
case "startup" =>
|
||||
ServiceManager.serviceManager ! Lookup("taskResolver") //ask for a resolver to deal with the GUID system
|
||||
|
||||
case ServiceManager.LookupResult("taskResolver", endpoint) =>
|
||||
taskResolver = endpoint
|
||||
context.become(Processing)
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def Processing : Receive = {
|
||||
case UndertakerActor.AddCorpse(corpse, zone, time) =>
|
||||
if(corpse.isBackpack) {
|
||||
corpses = corpses :+ UndertakerActor.Entry(corpse, zone, time)
|
||||
if(corpses.size == 1) { //we were the only entry so the event must be started from scratch
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
burial = context.system.scheduler.scheduleOnce(UndertakerActor.timeout, self, UndertakerActor.Dispose())
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.warn(s"he's not dead yet - $corpse")
|
||||
}
|
||||
|
||||
case UndertakerActor.Dispose() =>
|
||||
burial.cancel
|
||||
val now : Long = System.nanoTime
|
||||
val (buried, rotting) = PartitionEntries(corpses, now)
|
||||
corpses = rotting
|
||||
buried.foreach { BurialTask }
|
||||
if(rotting.nonEmpty) {
|
||||
val short_timeout : FiniteDuration = math.max(1, UndertakerActor.timeout_time - (now - rotting.head.time)) nanoseconds
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
burial = context.system.scheduler.scheduleOnce(short_timeout, self, UndertakerActor.Dispose())
|
||||
}
|
||||
|
||||
case UndertakerActor.FailureToWork(target, zone, ex) =>
|
||||
log.error(s"$target failed to be properly cleaned up from $zone - $ex")
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def BurialTask(entry : UndertakerActor.Entry) : Unit = {
|
||||
val target = entry.corpse
|
||||
val zone = entry.zone
|
||||
entry.zone.Population ! Zone.Corpse.Remove(target)
|
||||
context.parent ! AvatarServiceMessage(zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, target.GUID)) //call up to the main event system
|
||||
taskResolver ! BurialTask(target, zone)
|
||||
}
|
||||
|
||||
def BurialTask(corpse : Player, zone : Zone) : TaskResolver.GiveTask = {
|
||||
import net.psforever.objects.guid.{GUIDTask, Task}
|
||||
TaskResolver.GiveTask (
|
||||
new Task() {
|
||||
private val localCorpse = corpse
|
||||
private val localZone = zone
|
||||
private val localAnnounce = self
|
||||
|
||||
override def isComplete : Task.Resolution.Value = Task.Resolution.Success
|
||||
|
||||
def Execute(resolver : ActorRef) : Unit = {
|
||||
resolver ! scala.util.Success(this)
|
||||
}
|
||||
|
||||
override def onFailure(ex : Throwable): Unit = {
|
||||
localAnnounce ! UndertakerActor.FailureToWork(localCorpse, localZone, ex)
|
||||
}
|
||||
}, List(GUIDTask.UnregisterAvatar(corpse)(zone.GUID))
|
||||
)
|
||||
}
|
||||
|
||||
private def PartitionEntries(list : List[UndertakerActor.Entry], now : Long) : (List[UndertakerActor.Entry], List[UndertakerActor.Entry]) = {
|
||||
val n : Int = recursivePartitionEntries(list.iterator, now, UndertakerActor.timeout_time)
|
||||
(list.take(n), list.drop(n)) //take and drop so to always return new lists
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the index where the `List` of elements can be divided into two:
|
||||
* a `List` of elements that have exceeded the time limit,
|
||||
* and a `List` of elements that still satisfy the time limit.
|
||||
* @param iter the `Iterator` of entries to divide
|
||||
* @param now the time right now (in nanoseconds)
|
||||
* @param index a persistent record of the index where list division should occur;
|
||||
* defaults to 0
|
||||
* @return the index where division will occur
|
||||
*/
|
||||
@tailrec private def recursivePartitionEntries(iter : Iterator[UndertakerActor.Entry], now : Long, duration : Long, index : Int = 0) : Int = {
|
||||
if(!iter.hasNext) {
|
||||
index
|
||||
}
|
||||
else {
|
||||
val entry = iter.next()
|
||||
if(now - entry.time >= duration) {
|
||||
recursivePartitionEntries(iter, now, duration, index + 1)
|
||||
}
|
||||
else {
|
||||
index
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object UndertakerActor {
|
||||
/** A `Long` for calculation simplicity */
|
||||
private final val timeout_time : Long = 180000000000L //3 min (180s)
|
||||
/** A `FiniteDuration` for `Executor` simplicity */
|
||||
private final val timeout : FiniteDuration = timeout_time nanoseconds
|
||||
|
||||
final case class AddCorpse(corpse : Player, zone : Zone, time : Long = System.nanoTime())
|
||||
|
||||
final case class Entry(corpse : Player, zone : Zone, time : Long = System.nanoTime())
|
||||
|
||||
final case class FailureToWork(corpse : Player, zone : Zone, ex : Throwable)
|
||||
|
||||
final case class Dispose()
|
||||
|
||||
//TODO design mass disposal cases
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue