mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +00:00
moved current object dropping functionality over to AvatarService entirely; adjusting special support actor messaging for AvatarService; modified calls for DroppedItemRemover and CorpseRemoverActor; Player now has a more sensible check for its VisibleSlots
This commit is contained in:
parent
d35536da06
commit
36b9d81e6c
|
|
@ -129,7 +129,12 @@ class Player(private val core : Avatar) extends PlanetSideGameObject with Factio
|
|||
|
||||
def MaxArmor : Int = exosuit.MaxArmor
|
||||
|
||||
def VisibleSlots : Set[Int] = if(exosuit.SuitType == ExoSuitType.MAX) { Set(0) } else { Set(0,1,2,3,4) }
|
||||
def VisibleSlots : Set[Int] = if(exosuit.SuitType == ExoSuitType.MAX) {
|
||||
Set(0)
|
||||
}
|
||||
else {
|
||||
(0 to 4).filterNot(index => holsters(index).Size == EquipmentSize.Blocked).toSet
|
||||
}
|
||||
|
||||
override def Slot(slot : Int) : EquipmentSlot = {
|
||||
if(inventory.Offset <= slot && slot <= inventory.LastIndex) {
|
||||
|
|
|
|||
|
|
@ -141,14 +141,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
else { //no items in inventory; leave no corpse
|
||||
val player_guid = player.GUID
|
||||
player.Position = Vector3.Zero //save character before doing this
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid, 0))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid))
|
||||
taskResolver ! GUIDTask.UnregisterAvatar(player)(continent.GUID)
|
||||
}
|
||||
|
||||
case Some(vehicle_guid) =>
|
||||
val player_guid = player.GUID
|
||||
player.Position = Vector3.Zero //save character before doing this
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid, 0))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid))
|
||||
taskResolver ! GUIDTask.UnregisterAvatar(player)(continent.GUID)
|
||||
DismountVehicleOnLogOut()
|
||||
}
|
||||
|
|
@ -288,29 +288,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(GenericObjectActionMessage(guid, 36))
|
||||
}
|
||||
|
||||
case AvatarResponse.EquipmentInHand(target, slot, item) =>
|
||||
case msg @ AvatarResponse.DropItem(pkt) =>
|
||||
if(tplayer_guid != guid) {
|
||||
val definition = item.Definition
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
definition.ObjectId,
|
||||
item.GUID,
|
||||
ObjectCreateMessageParent(target, slot),
|
||||
definition.Packet.ConstructorData(item).get
|
||||
)
|
||||
)
|
||||
sendResponse(pkt)
|
||||
}
|
||||
|
||||
case msg @ AvatarResponse.EquipmentOnGround(pos, orient, item_id, item_guid, item_data) =>
|
||||
case AvatarResponse.EquipmentInHand(pkt) =>
|
||||
if(tplayer_guid != guid) {
|
||||
log.info(s"now dropping a $msg")
|
||||
sendResponse(
|
||||
ObjectCreateMessage(
|
||||
item_id,
|
||||
item_guid,
|
||||
DroppedItemData(PlacementData(pos, Vector3(0f, 0f, orient.z)), item_data)
|
||||
)
|
||||
)
|
||||
sendResponse(pkt)
|
||||
}
|
||||
|
||||
case AvatarResponse.LoadPlayer(pdata) =>
|
||||
|
|
@ -416,11 +401,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case LocalResponse.DoorCloses(door_guid) => //door closes for everyone
|
||||
sendResponse(GenericObjectStateMsg(door_guid, 17))
|
||||
|
||||
case LocalResponse.DropItem(id, item_guid, data) =>
|
||||
if(tplayer_guid != guid) {
|
||||
sendResponse(ObjectCreateMessage(id, item_guid, data))
|
||||
}
|
||||
|
||||
case LocalResponse.HackClear(target_guid, unk1, unk2) =>
|
||||
sendResponse(HackMessage(0, target_guid, guid, 0, unk1, HackState.HackCleared, unk2))
|
||||
|
||||
|
|
@ -429,11 +409,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
sendResponse(HackMessage(0, target_guid, guid, 100, unk1, HackState.Hacked, unk2))
|
||||
}
|
||||
|
||||
case LocalResponse.ObjectDelete(item_guid, unk) =>
|
||||
if(tplayer_guid != guid) {
|
||||
sendResponse(ObjectDeleteMessage(item_guid, unk))
|
||||
}
|
||||
|
||||
case LocalResponse.ProximityTerminalEffect(object_guid, effectState) =>
|
||||
if(tplayer_guid != guid) {
|
||||
sendResponse(ProximityTerminalUseMessage(PlanetSideGUID(0), object_guid, effectState))
|
||||
|
|
@ -1390,8 +1365,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case None =>
|
||||
PlanetSideGUID(0) //object is being introduced into the world upon drop
|
||||
}
|
||||
localService ! RemoverActor.AddTask(item, continent, Some(20 seconds))
|
||||
localService ! LocalServiceMessage(continent.Id, LocalAction.DropItem(exclusionId, item))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.DropItem(exclusionId, item, continent))
|
||||
|
||||
case Zone.Ground.CanNotDropItem(item) =>
|
||||
log.warn(s"DropItem: $player tried to drop a $item on the ground, but he missed")
|
||||
|
|
@ -1404,7 +1378,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case Zone.Ground.ItemInHand(item) =>
|
||||
player.Fit(item) match {
|
||||
case Some(slotNum) =>
|
||||
localService ! RemoverActor.ClearSpecific(List(item), continent)
|
||||
val item_guid = item.GUID
|
||||
val player_guid = player.GUID
|
||||
player.Slot(slotNum).Equipment = item
|
||||
|
|
@ -1417,13 +1390,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
definition.Packet.DetailedConstructorData(item).get
|
||||
)
|
||||
)
|
||||
avatarService ! AvatarServiceMessage(continent.Id, if(player.VisibleSlots.contains(slotNum)) {
|
||||
AvatarAction.EquipmentInHand(player_guid, player_guid, slotNum, item)
|
||||
}
|
||||
else {
|
||||
AvatarAction.ObjectDelete(player_guid, item_guid)
|
||||
})
|
||||
sendResponse(ActionResultMessage.Pass)
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PickupItem(player_guid, continent, player, slotNum, item))
|
||||
case None =>
|
||||
continent.Ground ! Zone.Ground.DropItem(item, item.Position, item.Orientation) //restore previous state
|
||||
}
|
||||
|
|
@ -3947,7 +3914,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case _ => ;
|
||||
//Player does not require special case; the act of dropping forces the item and icon to change
|
||||
}
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.EquipmentOnGround(player_guid, pos, orient, objDef.ObjectId, item2_guid, objDef.Packet.ConstructorData(item2).get))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.DropItem(player_guid, item2, continent))
|
||||
}
|
||||
|
||||
case None => ;
|
||||
|
|
@ -4440,8 +4407,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
*/
|
||||
def TryDisposeOfLootedCorpse(obj : Player) : Boolean = {
|
||||
if(WellLootedCorpse(obj)) {
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
context.system.scheduler.scheduleOnce(1 second, avatarService, AvatarServiceMessage.RemoveSpecificCorpse(List(obj)))
|
||||
avatarService ! AvatarServiceMessage.Corpse(RemoverActor.HurrySpecific(List(obj), continent))
|
||||
true
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -10,32 +10,73 @@ import net.psforever.types.Vector3
|
|||
import scala.annotation.tailrec
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* The base class for a type of "destruction `Actor`" intended to be used for delaying object cleanup activity.
|
||||
* Objects submitted to this process should be registered to a global unique identified system for a given region
|
||||
* as is specified in their submission.<br>
|
||||
* <br>
|
||||
* Two waiting lists are used to pool the objects being removed.
|
||||
* The first list is a basic pooling list that precludes any proper removal actions
|
||||
* and is almost expressly for delaying the process.
|
||||
* Previously-submitted tasks can be removed from this list so long as a matching object can be found.
|
||||
* Tasks in this list can also be expedited into the second list without having to consider delays.
|
||||
* After being migrated to the secondary list, the object is considered beyond the point of no return.
|
||||
* Followup activity will lead to its inevitable unregistering and removal.<br>
|
||||
* <br>
|
||||
* Functions have been provided for `override` in order to interject the appropriate cleanup operations.
|
||||
* The activity itself is typically removing the object in question from a certain list,
|
||||
* dismissing it with a mass distribution of `ObjectDeleteMessage` packets,
|
||||
* and finally unregistering it.
|
||||
* Some types of object have (de-)implementation variations which should be made explicit through the overrides.
|
||||
*/
|
||||
abstract class RemoverActor extends Actor {
|
||||
protected var firstTask : Cancellable = DefaultCancellable.obj
|
||||
protected var firstHeap : List[RemoverActor.Entry] = List()
|
||||
/**
|
||||
* The timer that checks whether entries in the first pool are still eligible for that pool.
|
||||
*/
|
||||
var firstTask : Cancellable = DefaultCancellable.obj
|
||||
/**
|
||||
* The first pool of objects waiting to be processed for removal.
|
||||
*/
|
||||
var firstHeap : List[RemoverActor.Entry] = List()
|
||||
|
||||
protected var secondTask : Cancellable = DefaultCancellable.obj
|
||||
protected var secondHeap : List[RemoverActor.Entry] = List()
|
||||
/**
|
||||
* The timer that checks whether entries in the second pool are still eligible for that pool.
|
||||
*/
|
||||
var secondTask : Cancellable = DefaultCancellable.obj
|
||||
/**
|
||||
* The second pool of objects waiting to be processed for removal.
|
||||
*/
|
||||
var secondHeap : List[RemoverActor.Entry] = List()
|
||||
|
||||
protected var taskResolver : ActorRef = Actor.noSender
|
||||
private var taskResolver : ActorRef = Actor.noSender
|
||||
|
||||
protected[this] val log = org.log4s.getLogger
|
||||
private[this] val log = org.log4s.getLogger
|
||||
|
||||
/**
|
||||
* Send the initial message that requests a task resolver for assisting in the removal process.
|
||||
*/
|
||||
override def preStart() : Unit = {
|
||||
super.preStart()
|
||||
self ! RemoverActor.Startup()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sufficiently clean up the current contents of these waiting removal jobs.
|
||||
* Cancel all timers, rush all entries in the lists through their individual steps, then empty the lists.
|
||||
* This is an improved `HurryAll`, but still faster since it also railroads entries through the second queue as well.
|
||||
*/
|
||||
override def postStop() = {
|
||||
super.postStop()
|
||||
firstTask.cancel
|
||||
secondTask.cancel
|
||||
|
||||
firstHeap.foreach(entry => {
|
||||
FirstJob(entry)
|
||||
SecondJob(entry)
|
||||
})
|
||||
secondHeap.foreach { SecondJob }
|
||||
firstHeap = Nil
|
||||
secondHeap = Nil
|
||||
taskResolver = ActorRef.noSender
|
||||
}
|
||||
|
||||
def receive : Receive = {
|
||||
|
|
@ -79,47 +120,32 @@ abstract class RemoverActor extends Actor {
|
|||
}
|
||||
|
||||
case RemoverActor.HurrySpecific(targets, zone) =>
|
||||
CullTargetsFromFirstHeap(targets, zone) match {
|
||||
case Nil => ;
|
||||
case list =>
|
||||
secondTask.cancel
|
||||
list.foreach { FirstJob }
|
||||
secondHeap = list ++ secondHeap
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
secondTask = context.system.scheduler.scheduleOnce(SecondStandardDuration, self, RemoverActor.TryDelete())
|
||||
}
|
||||
HurrySpecific(targets, zone)
|
||||
|
||||
case RemoverActor.HurryAll() =>
|
||||
firstTask.cancel
|
||||
firstHeap.foreach { FirstJob }
|
||||
secondHeap = secondHeap ++ firstHeap
|
||||
firstHeap = Nil
|
||||
secondTask.cancel
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
secondTask = context.system.scheduler.scheduleOnce(SecondStandardDuration, self, RemoverActor.TryDelete())
|
||||
HurryAll()
|
||||
|
||||
case RemoverActor.ClearSpecific(targets, zone) =>
|
||||
CullTargetsFromFirstHeap(targets, zone)
|
||||
ClearSpecific(targets, zone)
|
||||
|
||||
case RemoverActor.ClearAll() =>
|
||||
firstTask.cancel
|
||||
firstHeap = Nil
|
||||
ClearAll()
|
||||
|
||||
//private messages
|
||||
//private messages from RemoverActor to RemoverActor
|
||||
case RemoverActor.StartDelete() =>
|
||||
firstTask.cancel
|
||||
secondTask.cancel
|
||||
val now : Long = System.nanoTime
|
||||
val (in, out) = firstHeap.partition(entry => { now - entry.time >= entry.duration })
|
||||
firstHeap = out
|
||||
secondHeap = secondHeap ++ in
|
||||
secondHeap = secondHeap ++ in.map { RepackageEntry }
|
||||
in.foreach { FirstJob }
|
||||
RetimeFirstTask()
|
||||
if(secondHeap.nonEmpty) {
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
secondTask = context.system.scheduler.scheduleOnce(SecondStandardDuration, self, RemoverActor.TryDelete())
|
||||
}
|
||||
log.trace(s"item removal task has found ${secondHeap.size} items to remove")
|
||||
log.trace(s"item removal task has found ${in.size} items to remove")
|
||||
|
||||
case RemoverActor.TryDelete() =>
|
||||
secondTask.cancel
|
||||
|
|
@ -134,13 +160,79 @@ abstract class RemoverActor extends Actor {
|
|||
|
||||
case RemoverActor.FailureToWork(entry, ex) =>
|
||||
log.error(s"${entry.obj} from ${entry.zone} not properly unregistered - $ex")
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expedite some entries from the first pool into the second.
|
||||
* @param targets a list of objects to pick
|
||||
* @param zone the zone in which these objects must be discovered;
|
||||
* all targets must be in this zone, with the assumption that this is the zone where they were registered
|
||||
*/
|
||||
def HurrySpecific(targets : List[PlanetSideGameObject], zone : Zone) : Unit = {
|
||||
CullTargetsFromFirstHeap(targets, zone) match {
|
||||
case Nil => ;
|
||||
case list =>
|
||||
secondTask.cancel
|
||||
list.foreach { FirstJob }
|
||||
secondHeap = secondHeap ++ list.map { RepackageEntry }
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
secondTask = context.system.scheduler.scheduleOnce(SecondStandardDuration, self, RemoverActor.TryDelete())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expedite all entries from the first pool into the second.
|
||||
*/
|
||||
def HurryAll() : Unit = {
|
||||
firstTask.cancel
|
||||
firstHeap.foreach { FirstJob }
|
||||
secondHeap = secondHeap ++ firstHeap.map { RepackageEntry }
|
||||
firstHeap = Nil
|
||||
secondTask.cancel
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
secondTask = context.system.scheduler.scheduleOnce(SecondStandardDuration, self, RemoverActor.TryDelete())
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove specific entries from the first pool.
|
||||
*/
|
||||
def ClearSpecific(targets : List[PlanetSideGameObject], zone : Zone) : Unit = {
|
||||
CullTargetsFromFirstHeap(targets, zone)
|
||||
}
|
||||
|
||||
/**
|
||||
* No entries in the first pool.
|
||||
*/
|
||||
def ClearAll() : Unit = {
|
||||
firstTask.cancel
|
||||
firstHeap = Nil
|
||||
}
|
||||
|
||||
/**
|
||||
* Retime an individual entry by recreating it.
|
||||
* @param entry an existing entry
|
||||
* @return a new entry, containing the same object and zone information;
|
||||
* this new entry is always set to last for the duration of the second pool
|
||||
*/
|
||||
private def RepackageEntry(entry : RemoverActor.Entry) : RemoverActor.Entry = {
|
||||
RemoverActor.Entry(entry.obj, entry.zone, SecondStandardDuration.toNanos)
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the first pool of entries awaiting removal processing.
|
||||
* If any entry has the same object as one of the targets and belongs to the same zone, remove it from the first pool.
|
||||
* If no targets are selected (an empty list), all discovered targets within the appropriate zone are removed.
|
||||
* @param targets a list of objects to pick
|
||||
* @param zone the zone in which these objects must be discovered;
|
||||
* all targets must be in this zone, with the assumption that this is the zone where they were registered
|
||||
* @return all of the discovered entries
|
||||
*/
|
||||
private def CullTargetsFromFirstHeap(targets : List[PlanetSideGameObject], zone : Zone) : List[RemoverActor.Entry] = {
|
||||
if(targets.nonEmpty) {
|
||||
firstTask.cancel
|
||||
val culledEntries = if(targets.size == 1) {
|
||||
log.debug(s"a target submitted for early cleanup: ${targets.head}")
|
||||
val culledEntries = if(targets.nonEmpty) {
|
||||
if(targets.size == 1) {
|
||||
log.debug(s"a target submitted: ${targets.head}")
|
||||
//simple selection
|
||||
RemoverActor.recursiveFind(firstHeap.iterator, RemoverActor.Entry(targets.head, zone, 0)) match {
|
||||
case None => ;
|
||||
|
|
@ -152,12 +244,12 @@ abstract class RemoverActor extends Actor {
|
|||
}
|
||||
}
|
||||
else {
|
||||
log.trace(s"multiple targets submitted for early cleanup: $targets")
|
||||
log.trace(s"multiple targets submitted: $targets")
|
||||
//cumbersome partition
|
||||
//a - find targets from entries
|
||||
val locatedTargets = for {
|
||||
a <- targets.map(RemoverActor.Entry(_, zone, 0))
|
||||
b <- firstHeap
|
||||
b <- firstHeap//.filter(entry => entry.zone == zone)
|
||||
if b.obj.HasGUID && a.obj.HasGUID && RemoverActor.Similarity(b, a)
|
||||
} yield b
|
||||
if(locatedTargets.nonEmpty) {
|
||||
|
|
@ -173,6 +265,15 @@ abstract class RemoverActor extends Actor {
|
|||
Nil
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.trace(s"all targets within the specified zone $zone will be submitted")
|
||||
//no specific targets; split on all targets in the given zone instead
|
||||
val (in, out) = firstHeap.partition(entry => entry.zone == zone)
|
||||
firstHeap = out.sortBy(_.duration)
|
||||
in
|
||||
}
|
||||
if(culledEntries.nonEmpty) {
|
||||
RetimeFirstTask()
|
||||
culledEntries
|
||||
}
|
||||
|
|
@ -181,6 +282,12 @@ abstract class RemoverActor extends Actor {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common function to reset the first task's delayed execution.
|
||||
* Cancels the scheduled timer and will only restart the timer if there is at least one entry in the first pool.
|
||||
* @param now the time (in nanoseconds);
|
||||
* defaults to the current time (in nanoseconds)
|
||||
*/
|
||||
def RetimeFirstTask(now : Long = System.nanoTime) : Unit = {
|
||||
firstTask.cancel
|
||||
if(firstHeap.nonEmpty) {
|
||||
|
|
@ -220,53 +327,152 @@ abstract class RemoverActor extends Actor {
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Default time for entries waiting in the first list.
|
||||
* Override.
|
||||
* @return the time as a `FiniteDuration` object (to be later transformed into nanoseconds)
|
||||
*/
|
||||
def FirstStandardDuration : FiniteDuration
|
||||
|
||||
/**
|
||||
* Default time for entries waiting in the second list.
|
||||
* Override.
|
||||
* @return the time as a `FiniteDuration` object (to be later transformed into nanoseconds)
|
||||
*/
|
||||
def SecondStandardDuration : FiniteDuration
|
||||
|
||||
/**
|
||||
* Determine whether or not the resulting entry is valid for this removal process.
|
||||
* The primary purpose of this function should be to determine if the appropriate type of object is being submitted.
|
||||
* Override.
|
||||
* @param entry the entry
|
||||
* @return `true`, if it can be processed; `false`, otherwise
|
||||
*/
|
||||
def InclusionTest(entry : RemoverActor.Entry) : Boolean
|
||||
|
||||
/**
|
||||
* Performed when the entry is initially added to the first list.
|
||||
* Override.
|
||||
* @param entry the entry
|
||||
*/
|
||||
def InitialJob(entry : RemoverActor.Entry) : Unit
|
||||
|
||||
/**
|
||||
* Performed when the entry is shifted from the first list to the second list.
|
||||
* Override.
|
||||
* @param entry the entry
|
||||
*/
|
||||
def FirstJob(entry : RemoverActor.Entry) : Unit
|
||||
|
||||
/**
|
||||
* Performed to determine when an entry can be shifted off from the second list.
|
||||
* Override.
|
||||
* @param entry the entry
|
||||
*/
|
||||
def ClearanceTest(entry : RemoverActor.Entry) : Boolean
|
||||
|
||||
/**
|
||||
* The specific action that is necessary to complete the removal process.
|
||||
* Override.
|
||||
* @see `GUIDTask`
|
||||
* @param entry the entry
|
||||
*/
|
||||
def DeletionTask(entry : RemoverActor.Entry) : TaskResolver.GiveTask
|
||||
}
|
||||
|
||||
object RemoverActor {
|
||||
/**
|
||||
* na
|
||||
* All information necessary to apply to the removal process to produce an effect.
|
||||
* Internally, all entries have a "time created" field.
|
||||
* @param obj the target
|
||||
* @param zone the zone in which this target is registered
|
||||
* @param duration how much longer the target will exist (in nanoseconds)
|
||||
* @param time when this entry was created (in nanoseconds)
|
||||
* @param duration how much longer the target will exist in its current state (in nanoseconds)
|
||||
*/
|
||||
case class Entry(obj : PlanetSideGameObject, zone : Zone, duration : Long, time : Long = System.nanoTime)
|
||||
case class Entry(obj : PlanetSideGameObject, zone : Zone, duration : Long) {
|
||||
/** The time when this entry was created (in nanoseconds) */
|
||||
val time : Long = System.nanoTime
|
||||
}
|
||||
|
||||
/**
|
||||
* A message that prompts the retrieval of a `TaskResolver` for us in the removal process.
|
||||
*/
|
||||
case class Startup()
|
||||
|
||||
/**
|
||||
* Message to submit an object to the removal process.
|
||||
* @see `FirstStandardDuration`
|
||||
* @param obj the target
|
||||
* @param zone the zone in which this target is registered
|
||||
* @param duration how much longer the target will exist in its current state (in nanoseconds);
|
||||
* a default time duration is provided by implementation
|
||||
*/
|
||||
case class AddTask(obj : PlanetSideGameObject, zone : Zone, duration : Option[FiniteDuration] = None)
|
||||
|
||||
/**
|
||||
* "Hurrying" shifts entries with the discovered objects (in the same `zone`)
|
||||
* through their first task and into the second pool.
|
||||
* If the list of targets is empty, all discovered objects in the given zone will be considered targets.
|
||||
* @param targets a list of objects to match
|
||||
* @param zone the zone in which these objects exist;
|
||||
* the assumption is that all these target objects are registered to this zone
|
||||
*/
|
||||
case class HurrySpecific(targets : List[PlanetSideGameObject], zone : Zone)
|
||||
|
||||
/**
|
||||
* "Hurrying" shifts all entries through their first task and into the second pool.
|
||||
*/
|
||||
case class HurryAll()
|
||||
|
||||
/**
|
||||
* "Clearing" cancels entries with the discovered objects (in the same `zone`)
|
||||
* if they are discovered in the first pool of objects.
|
||||
* Those entries will no longer be affected by any actions performed by the removal process until re-submitted.
|
||||
* If the list of targets is empty, all discovered objects in the given zone will be considered targets.
|
||||
* @param targets a list of objects to match
|
||||
* @param zone the zone in which these objects exist;
|
||||
* the assumption is that all these target objects are registered to this zone
|
||||
*/
|
||||
case class ClearSpecific(targets : List[PlanetSideGameObject], zone : Zone)
|
||||
|
||||
/**
|
||||
* "Clearing" cancels all entries if they are discovered in the first pool of objects.
|
||||
* Those entries will no longer be affected by any actions performed by the removal process until re-submitted.
|
||||
*/
|
||||
case class ClearAll()
|
||||
|
||||
/**
|
||||
* Message that indicates that the final stage of the remover process has failed.
|
||||
* Since the last step is generally unregistering the object, it could be a critical error.
|
||||
* @param entry the entry that was not properly removed
|
||||
* @param ex the reason the last entry was not properly removed
|
||||
*/
|
||||
protected final case class FailureToWork(entry : RemoverActor.Entry, ex : Throwable)
|
||||
|
||||
/**
|
||||
* Internal message to flag operations by data in the first list if it has been in that list long enough.
|
||||
*/
|
||||
private final case class StartDelete()
|
||||
|
||||
/**
|
||||
* Internal message to flag operations by data in the second list if it has been in that list long enough.
|
||||
*/
|
||||
private final case class TryDelete()
|
||||
|
||||
/**
|
||||
* Match two entries by object and by zone information.
|
||||
* @param entry1 the first entry
|
||||
* @param entry2 the second entry
|
||||
* @return if they match
|
||||
*/
|
||||
private def Similarity(entry1 : RemoverActor.Entry, entry2 : RemoverActor.Entry) : Boolean = {
|
||||
entry1.obj == entry2.obj && entry1.zone == entry2.zone && entry1.obj.GUID == entry2.obj.GUID
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index of an entry in the list of entries.
|
||||
* @param iter an `Iterator` of entries
|
||||
* @param target the specific entry to be found
|
||||
* @param index the incrementing index value
|
||||
* @return the index of the entry in the list, if a match to the target is found
|
||||
*/
|
||||
@tailrec private def recursiveFind(iter : Iterator[RemoverActor.Entry], target : RemoverActor.Entry, index : Int = 0) : Option[Int] = {
|
||||
if(!iter.hasNext) {
|
||||
None
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.avatar
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.{PlanetSideGameObject, Player}
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.inventory.Container
|
||||
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}
|
||||
import net.psforever.types.ExoSuitType
|
||||
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
object AvatarAction {
|
||||
trait Action
|
||||
|
|
@ -17,21 +20,19 @@ object AvatarAction {
|
|||
final case class ChangeFireState_Start(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||
final case class ChangeFireState_Stop(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||
final case class ConcealPlayer(player_guid : PlanetSideGUID) extends Action
|
||||
final case class DropItem(player_guid : PlanetSideGUID, item : Equipment, zone : Zone) extends Action
|
||||
final case class EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
||||
final case class EquipmentOnGround(player_guid : PlanetSideGUID, pos : Vector3, orient : Vector3, item_id : Int, item_guid : PlanetSideGUID, item_data : ConstructorData) extends Action
|
||||
final case class LoadPlayer(player_guid : PlanetSideGUID, pdata : ConstructorData) extends Action
|
||||
// final case class LoadMap(msg : PlanetSideGUID) extends Action
|
||||
// final case class unLoadMap(msg : PlanetSideGUID) extends Action
|
||||
final case class ObjectDelete(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, unk : Int = 0) extends Action
|
||||
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, time : Option[Long] = None) extends Action
|
||||
final case class PickupItem(player_guid : PlanetSideGUID, zone : Zone, target : PlanetSideGameObject with Container, slot : Int, item : Equipment, unk : Int = 0) extends Action
|
||||
final case class Release(player : Player, zone : Zone, time : Option[FiniteDuration] = None) extends Action
|
||||
final case class Reload(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||
final case class StowEquipment(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
||||
final case class WeaponDryFire(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||
// final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
||||
// final case class DestroyDisplay(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
||||
// final case class HitHintReturn(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
||||
// final case class ChangeWeapon(unk1 : Int, sessionId : Long) extends Action
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ 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.{ObjectCreateMessage, PlanetSideGUID, PlayerStateMessageUpstream}
|
||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||
import net.psforever.types.{ExoSuitType, Vector3}
|
||||
import net.psforever.types.ExoSuitType
|
||||
|
||||
object AvatarResponse {
|
||||
trait Response
|
||||
|
|
@ -16,11 +16,9 @@ object AvatarResponse {
|
|||
final case class ChangeFireState_Start(weapon_guid : PlanetSideGUID) extends Response
|
||||
final case class ChangeFireState_Stop(weapon_guid : PlanetSideGUID) extends Response
|
||||
final case class ConcealPlayer() extends Response
|
||||
final case class EquipmentInHand(target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Response
|
||||
final case class EquipmentOnGround(pos : Vector3, orient : Vector3, item_id : Int, item_guid : PlanetSideGUID, item_data : ConstructorData) extends Response
|
||||
final case class EquipmentInHand(pkt : ObjectCreateMessage) extends Response
|
||||
final case class DropItem(pkt : ObjectCreateMessage) extends Response
|
||||
final case class LoadPlayer(pdata : ConstructorData) extends Response
|
||||
// final case class unLoadMap() extends Response
|
||||
// final case class LoadMap() extends Response
|
||||
final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response
|
||||
final case class ObjectHeld(slot : Int) extends Response
|
||||
final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response
|
||||
|
|
@ -32,5 +30,4 @@ object AvatarResponse {
|
|||
// final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response
|
||||
// final case class DestroyDisplay(itemID : PlanetSideGUID) extends Response
|
||||
// final case class HitHintReturn(itemID : PlanetSideGUID) extends Response
|
||||
// final case class ChangeWeapon(facingYaw : Int) extends Response
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,15 @@
|
|||
package services.avatar
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import services.avatar.support.CorpseRemovalActor
|
||||
import services.{GenericEventBus, Service}
|
||||
import net.psforever.packet.game.ObjectCreateMessage
|
||||
import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectCreateMessageParent, PlacementData}
|
||||
import services.avatar.support.{CorpseRemovalActor, DroppedItemRemover}
|
||||
import services.{GenericEventBus, RemoverActor, Service}
|
||||
|
||||
class AvatarService extends Actor {
|
||||
private val undertaker : ActorRef = context.actorOf(Props[CorpseRemovalActor], "corpse-removal-agent")
|
||||
undertaker ! "startup"
|
||||
private val janitor = context.actorOf(Props[DroppedItemRemover], "item-remover-agent")
|
||||
//undertaker ! "startup"
|
||||
|
||||
private [this] val log = org.log4s.getLogger
|
||||
|
||||
|
|
@ -62,13 +65,26 @@ class AvatarService extends Actor {
|
|||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ConcealPlayer())
|
||||
)
|
||||
case AvatarAction.EquipmentInHand(player_guid, target_guid, slot, obj) =>
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.EquipmentInHand(target_guid, slot, obj))
|
||||
case AvatarAction.DropItem(player_guid, item, zone) =>
|
||||
val definition = item.Definition
|
||||
val objectData = DroppedItemData(
|
||||
PlacementData(item.Position, item.Orientation),
|
||||
definition.Packet.ConstructorData(item).get
|
||||
)
|
||||
case AvatarAction.EquipmentOnGround(player_guid, pos, orient, item_id, item_guid, item_data) =>
|
||||
janitor forward RemoverActor.AddTask(item, zone)
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.EquipmentOnGround(pos, orient, item_id, item_guid, item_data))
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid,
|
||||
AvatarResponse.DropItem(ObjectCreateMessage(definition.ObjectId, item.GUID, objectData))
|
||||
)
|
||||
)
|
||||
case AvatarAction.EquipmentInHand(player_guid, target_guid, slot, item) =>
|
||||
val definition = item.Definition
|
||||
val containerData = ObjectCreateMessageParent(target_guid, slot)
|
||||
val objectData = definition.Packet.ConstructorData(item).get
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid,
|
||||
AvatarResponse.EquipmentInHand(ObjectCreateMessage(definition.ObjectId, item.GUID, containerData, objectData))
|
||||
)
|
||||
)
|
||||
case AvatarAction.LoadPlayer(player_guid, pdata) =>
|
||||
AvatarEvents.publish(
|
||||
|
|
@ -90,11 +106,24 @@ class AvatarService extends Actor {
|
|||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(msg, spectator, weapon))
|
||||
)
|
||||
case AvatarAction.PickupItem(player_guid, zone, target, slot, item, unk) =>
|
||||
janitor forward RemoverActor.ClearSpecific(List(item), zone)
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, {
|
||||
val itemGUID = item.GUID
|
||||
if(target.VisibleSlots.contains(slot)) {
|
||||
val definition = item.Definition
|
||||
val containerData = ObjectCreateMessageParent(target.GUID, slot)
|
||||
val objectData = definition.Packet.ConstructorData(item).get
|
||||
AvatarResponse.EquipmentInHand(ObjectCreateMessage(definition.ObjectId, itemGUID, containerData, objectData))
|
||||
}
|
||||
else {
|
||||
AvatarResponse.ObjectDelete(itemGUID, unk)
|
||||
}
|
||||
})
|
||||
)
|
||||
case AvatarAction.Release(player, zone, time) =>
|
||||
undertaker ! (time match {
|
||||
case Some(t) => CorpseRemovalActor.AddCorpse(player, zone, t)
|
||||
case None => CorpseRemovalActor.AddCorpse(player, zone)
|
||||
})
|
||||
undertaker forward RemoverActor.AddTask(player, zone, time)
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player.GUID, AvatarResponse.Release(player))
|
||||
)
|
||||
|
|
@ -115,52 +144,13 @@ class AvatarService extends Actor {
|
|||
}
|
||||
|
||||
//message to Undertaker
|
||||
case AvatarServiceMessage.RemoveSpecificCorpse(corpses) =>
|
||||
undertaker ! AvatarServiceMessage.RemoveSpecificCorpse( corpses.filter(corpse => {corpse.HasGUID && corpse.isBackpack}) )
|
||||
case AvatarServiceMessage.Corpse(msg) =>
|
||||
undertaker forward msg
|
||||
|
||||
case AvatarServiceMessage.Ground(msg) =>
|
||||
janitor forward msg
|
||||
|
||||
/*
|
||||
case AvatarService.PlayerStateMessage(msg) =>
|
||||
// log.info(s"NEW: ${m}")
|
||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(msg.avatar_guid)
|
||||
if (playerOpt.isDefined) {
|
||||
val player: PlayerAvatar = playerOpt.get
|
||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, msg.avatar_guid,
|
||||
AvatarServiceReply.PlayerStateMessage(msg.pos, msg.vel, msg.facingYaw, msg.facingPitch, msg.facingYawUpper, msg.is_crouching, msg.is_jumping, msg.jump_thrust, msg.is_cloaked)
|
||||
))
|
||||
|
||||
}
|
||||
case AvatarService.LoadMap(msg) =>
|
||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(msg.guid)
|
||||
if (playerOpt.isDefined) {
|
||||
val player: PlayerAvatar = playerOpt.get
|
||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, PlanetSideGUID(msg.guid),
|
||||
AvatarServiceReply.LoadMap()
|
||||
))
|
||||
}
|
||||
case AvatarService.unLoadMap(msg) =>
|
||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(msg.guid)
|
||||
if (playerOpt.isDefined) {
|
||||
val player: PlayerAvatar = playerOpt.get
|
||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, PlanetSideGUID(msg.guid),
|
||||
AvatarServiceReply.unLoadMap()
|
||||
))
|
||||
}
|
||||
case AvatarService.ObjectHeld(msg) =>
|
||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(msg.guid)
|
||||
if (playerOpt.isDefined) {
|
||||
val player: PlayerAvatar = playerOpt.get
|
||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, PlanetSideGUID(msg.guid),
|
||||
AvatarServiceReply.ObjectHeld()
|
||||
))
|
||||
}
|
||||
case AvatarService.PlanetsideAttribute(guid, attribute_type, attribute_value) =>
|
||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(guid)
|
||||
if (playerOpt.isDefined) {
|
||||
val player: PlayerAvatar = playerOpt.get
|
||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, guid,
|
||||
AvatarServiceReply.PlanetSideAttribute(attribute_type, attribute_value)
|
||||
))
|
||||
}
|
||||
case AvatarService.PlayerStateShift(killer, guid) =>
|
||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(guid)
|
||||
if (playerOpt.isDefined) {
|
||||
|
|
@ -185,16 +175,8 @@ class AvatarService extends Actor {
|
|||
AvatarServiceReply.DestroyDisplay(source_guid)
|
||||
))
|
||||
}
|
||||
case AvatarService.ChangeWeapon(unk1, sessionId) =>
|
||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(sessionId)
|
||||
if (playerOpt.isDefined) {
|
||||
val player: PlayerAvatar = playerOpt.get
|
||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, PlanetSideGUID(player.guid),
|
||||
AvatarServiceReply.ChangeWeapon(unk1)
|
||||
))
|
||||
}
|
||||
*/
|
||||
case msg =>
|
||||
log.info(s"Unhandled message $msg from $sender")
|
||||
log.warn(s"Unhandled message $msg from $sender")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.avatar
|
||||
|
||||
import net.psforever.objects.Player
|
||||
|
||||
final case class AvatarServiceMessage(forChannel : String, actionMessage : AvatarAction.Action)
|
||||
|
||||
object AvatarServiceMessage {
|
||||
final case class RemoveSpecificCorpse(corpse : List[Player])
|
||||
final case class Corpse(msg : Any)
|
||||
final case class Ground(msg : Any)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,245 +1,35 @@
|
|||
// 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 net.psforever.types.Vector3
|
||||
import services.{Service, ServiceManager}
|
||||
import services.ServiceManager.Lookup
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import net.psforever.objects.Player
|
||||
import services.{RemoverActor, Service}
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class CorpseRemovalActor extends Actor {
|
||||
private var burial : Cancellable = DefaultCancellable.obj
|
||||
private var corpses : List[CorpseRemovalActor.Entry] = List()
|
||||
class CorpseRemovalActor extends RemoverActor {
|
||||
final val FirstStandardDuration : FiniteDuration = 3 minutes
|
||||
|
||||
private var decomposition : Cancellable = DefaultCancellable.obj
|
||||
private var buriedCorpses : List[CorpseRemovalActor.Entry] = List()
|
||||
final val SecondStandardDuration : FiniteDuration = 500 milliseconds
|
||||
|
||||
private var taskResolver : ActorRef = Actor.noSender
|
||||
|
||||
private[this] val log = org.log4s.getLogger
|
||||
|
||||
override def postStop() = {
|
||||
//Cart Master: See you on Thursday.
|
||||
super.postStop()
|
||||
burial.cancel
|
||||
decomposition.cancel
|
||||
|
||||
corpses.foreach(corpse => {
|
||||
BurialTask(corpse)
|
||||
LastRitesTask(corpse)
|
||||
})
|
||||
buriedCorpses.foreach { LastRitesTask }
|
||||
def InclusionTest(entry : RemoverActor.Entry) : Boolean = {
|
||||
entry.obj.isInstanceOf[Player] && entry.obj.asInstanceOf[Player].isBackpack
|
||||
}
|
||||
|
||||
def receive : Receive = {
|
||||
case "startup" =>
|
||||
ServiceManager.serviceManager ! Lookup("taskResolver") //ask for a resolver to deal with the GUID system
|
||||
def InitialJob(entry : RemoverActor.Entry) : Unit = { }
|
||||
|
||||
case ServiceManager.LookupResult("taskResolver", endpoint) =>
|
||||
//Cart Master: Bring out your dead!
|
||||
taskResolver = endpoint
|
||||
context.become(Processing)
|
||||
|
||||
case _ => ;
|
||||
def FirstJob(entry : RemoverActor.Entry) : Unit = {
|
||||
import net.psforever.objects.zones.Zone
|
||||
entry.zone.Population ! Zone.Corpse.Remove(entry.obj.asInstanceOf[Player])
|
||||
context.parent ! AvatarServiceMessage(entry.zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, entry.obj.GUID))
|
||||
}
|
||||
|
||||
def Processing : Receive = {
|
||||
case CorpseRemovalActor.AddCorpse(corpse, zone, time) =>
|
||||
import CorpseRemovalActor.SimilarCorpses
|
||||
if(corpse.isBackpack && !buriedCorpses.exists(entry => SimilarCorpses(entry.corpse, corpse) )) {
|
||||
if(corpses.isEmpty) {
|
||||
//we were the only entry so the event must be started from scratch
|
||||
corpses = List(CorpseRemovalActor.Entry(corpse, zone, time))
|
||||
RetimeFirstTask()
|
||||
}
|
||||
else {
|
||||
//unknown number of entries; append, sort, then re-time tasking
|
||||
val oldHead = corpses.head
|
||||
if(!corpses.exists(entry => SimilarCorpses(entry.corpse, corpse))) {
|
||||
corpses = (corpses :+ CorpseRemovalActor.Entry(corpse, zone, time)).sortBy(_.timeAlive)
|
||||
if(oldHead != corpses.head) {
|
||||
RetimeFirstTask()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Cart Master: 'Ere. He says he's not dead!
|
||||
log.warn(s"$corpse does not qualify as a corpse; ignored queueing request")
|
||||
}
|
||||
|
||||
case AvatarServiceMessage.RemoveSpecificCorpse(targets) =>
|
||||
if(targets.nonEmpty) {
|
||||
//Cart Master: No, I've got to go to the Robinsons'. They've lost nine today.
|
||||
burial.cancel
|
||||
if(targets.size == 1) {
|
||||
log.debug(s"a target corpse submitted for early cleanup: ${targets.head}")
|
||||
//simple selection
|
||||
CorpseRemovalActor.recursiveFindCorpse(corpses.iterator, targets.head) match {
|
||||
case None => ;
|
||||
case Some(index) =>
|
||||
decomposition.cancel
|
||||
BurialTask(corpses(index))
|
||||
buriedCorpses = buriedCorpses :+ corpses(index)
|
||||
corpses = (corpses.take(index) ++ corpses.drop(index + 1)).sortBy(_.timeAlive)
|
||||
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")
|
||||
import CorpseRemovalActor.SimilarCorpses
|
||||
decomposition.cancel
|
||||
//cumbersome partition
|
||||
//a - find targets from corpses
|
||||
val locatedTargets = for {
|
||||
a <- targets
|
||||
b <- corpses
|
||||
if b.corpse.HasGUID && a.HasGUID && SimilarCorpses(b.corpse, a)
|
||||
} yield b
|
||||
if(locatedTargets.nonEmpty) {
|
||||
decomposition.cancel
|
||||
locatedTargets.foreach { BurialTask }
|
||||
buriedCorpses = locatedTargets ++ buriedCorpses
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
decomposition = context.system.scheduler.scheduleOnce(500 milliseconds, self, CorpseRemovalActor.TryDelete())
|
||||
//b - corpses, after the found targets are removed (cull any non-GUID entries while at it)
|
||||
corpses = (for {
|
||||
a <- locatedTargets
|
||||
b <- corpses
|
||||
if b.corpse.HasGUID && a.corpse.HasGUID && !SimilarCorpses(b.corpse, a.corpse)
|
||||
} yield b).sortBy(_.timeAlive)
|
||||
}
|
||||
}
|
||||
RetimeFirstTask()
|
||||
}
|
||||
|
||||
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.
|
||||
log.error(s"corpse $target from $zone not properly unregistered - $ex")
|
||||
|
||||
case _ => ;
|
||||
def ClearanceTest(entry : RemoverActor.Entry) : Boolean = {
|
||||
!entry.zone.Corpses.contains(entry.obj.asInstanceOf[Player])
|
||||
}
|
||||
|
||||
def RetimeFirstTask(now : Long = System.nanoTime) : Unit = {
|
||||
//Cart Master: Thursday.
|
||||
burial.cancel
|
||||
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.StartDelete())
|
||||
}
|
||||
}
|
||||
|
||||
def BurialTask(entry : CorpseRemovalActor.Entry) : Unit = {
|
||||
val target = entry.corpse
|
||||
entry.zone.Population ! Zone.Corpse.Remove(target)
|
||||
context.parent ! AvatarServiceMessage(entry.zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, target.GUID))
|
||||
}
|
||||
|
||||
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() {
|
||||
private val localCorpse = corpse
|
||||
private val localZone = zone
|
||||
private val localAnnounce = self
|
||||
|
||||
override def isComplete : Task.Resolution.Value = if(!localCorpse.HasGUID) {
|
||||
Task.Resolution.Success
|
||||
}
|
||||
else {
|
||||
Task.Resolution.Incomplete
|
||||
}
|
||||
|
||||
def Execute(resolver : ActorRef) : Unit = {
|
||||
resolver ! scala.util.Success(this)
|
||||
}
|
||||
|
||||
override def onFailure(ex : Throwable): Unit = {
|
||||
localAnnounce ! CorpseRemovalActor.FailureToWork(localCorpse, localZone, ex)
|
||||
}
|
||||
}, List(GUIDTask.UnregisterPlayer(corpse)(zone.GUID))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object CorpseRemovalActor {
|
||||
final val time : Long = 180000000000L //3 min (180s)
|
||||
|
||||
final case class AddCorpse(corpse : Player, zone : Zone, time : Long = CorpseRemovalActor.time)
|
||||
|
||||
final case class Entry(corpse : Player, zone : Zone, timeAlive : Long = CorpseRemovalActor.time, time : Long = System.nanoTime())
|
||||
|
||||
private final case class FailureToWork(corpse : Player, zone : Zone, ex : Throwable)
|
||||
|
||||
private final case class StartDelete()
|
||||
|
||||
private final case class TryDelete()
|
||||
|
||||
private def SimilarCorpses(obj1 : Player, obj2 : Player) : Boolean = {
|
||||
obj1 == obj2 && obj1.Continent.equals(obj2.Continent) && obj1.GUID == obj2.GUID
|
||||
}
|
||||
|
||||
/**
|
||||
* A recursive function that finds and removes a specific player from a list of players.
|
||||
* @param iter an `Iterator` of `CorpseRemovalActor.Entry` objects
|
||||
* @param player the target `Player`
|
||||
* @param index the index of the discovered `Player` object
|
||||
* @return the index of the `Player` object in the list to be removed;
|
||||
* `None`, otherwise
|
||||
*/
|
||||
@tailrec final def recursiveFindCorpse(iter : Iterator[CorpseRemovalActor.Entry], player : Player, index : Int = 0) : Option[Int] = {
|
||||
if(!iter.hasNext) {
|
||||
None
|
||||
}
|
||||
else {
|
||||
val corpse = iter.next.corpse
|
||||
if(corpse.HasGUID && player.HasGUID && SimilarCorpses(corpse, player)) {
|
||||
Some(index)
|
||||
}
|
||||
else {
|
||||
recursiveFindCorpse(iter, player, index + 1)
|
||||
}
|
||||
}
|
||||
def DeletionTask(entry : RemoverActor.Entry) : TaskResolver.GiveTask = {
|
||||
GUIDTask.UnregisterPlayer(entry.obj.asInstanceOf[Player])(entry.zone.GUID)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.local.support
|
||||
package services.avatar.support
|
||||
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import services.{RemoverActor, Service}
|
||||
import services.local.{LocalAction, LocalServiceMessage}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ class DroppedItemRemover extends RemoverActor {
|
|||
def FirstJob(entry : RemoverActor.Entry) : Unit = {
|
||||
import net.psforever.objects.zones.Zone
|
||||
entry.zone.Ground ! Zone.Ground.PickupItem(entry.obj.GUID)
|
||||
context.parent ! LocalServiceMessage(entry.zone.Id, LocalAction.ObjectDelete(Service.defaultPlayerGUID, entry.obj.GUID))
|
||||
context.parent ! AvatarServiceMessage(entry.zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, entry.obj.GUID))
|
||||
}
|
||||
|
||||
def ClearanceTest(entry : RemoverActor.Entry) : Boolean = true
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.local
|
||||
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
import net.psforever.objects.zones.Zone
|
||||
|
|
@ -13,10 +12,8 @@ object LocalAction {
|
|||
|
||||
final case class DoorOpens(player_guid : PlanetSideGUID, continent : Zone, door : Door) extends Action
|
||||
final case class DoorCloses(player_guid : PlanetSideGUID, door_guid : PlanetSideGUID) extends Action
|
||||
final case class DropItem(player_guid : PlanetSideGUID, item : Equipment) extends Action
|
||||
final case class HackClear(player_guid : PlanetSideGUID, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
|
||||
final case class HackTemporarily(player_guid : PlanetSideGUID, continent : Zone, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
|
||||
final case class ObjectDelete(player_guid : PlanetSideGUID, item_guid : PlanetSideGUID, unk : Int = 0) extends Action
|
||||
final case class ProximityTerminalEffect(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, effectState : Boolean) extends Action
|
||||
final case class TriggerSound(player_guid : PlanetSideGUID, sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Action
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.local
|
||||
|
||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||
import net.psforever.packet.game.{PlanetSideGUID, TriggeredSound}
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
|
|
@ -10,10 +9,8 @@ object LocalResponse {
|
|||
|
||||
final case class DoorOpens(door_guid : PlanetSideGUID) extends Response
|
||||
final case class DoorCloses(door_guid : PlanetSideGUID) extends Response
|
||||
final case class DropItem(item_id : Int, item_guid : PlanetSideGUID, item_data : ConstructorData) extends Response
|
||||
final case class HackClear(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
|
||||
final case class HackObject(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
|
||||
final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response
|
||||
final case class ProximityTerminalEffect(object_guid : PlanetSideGUID, effectState : Boolean) extends Response
|
||||
final case class TriggerSound(sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Response
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,12 @@
|
|||
package services.local
|
||||
|
||||
import akka.actor.{Actor, Props}
|
||||
import net.psforever.packet.game.objectcreate.{DroppedItemData, PlacementData}
|
||||
import services.local.support.{DoorCloseActor, DroppedItemRemover, HackClearActor}
|
||||
import services.{GenericEventBus, RemoverActor, Service}
|
||||
import services.local.support.{DoorCloseActor, HackClearActor}
|
||||
import services.{GenericEventBus, Service}
|
||||
|
||||
class LocalService extends Actor {
|
||||
private val doorCloser = context.actorOf(Props[DoorCloseActor], "local-door-closer")
|
||||
private val hackClearer = context.actorOf(Props[HackClearActor], "local-hack-clearer")
|
||||
private val janitor = context.actorOf(Props[DroppedItemRemover], "local-item-remover")
|
||||
private [this] val log = org.log4s.getLogger
|
||||
|
||||
override def preStart = {
|
||||
|
|
@ -48,21 +46,6 @@ class LocalService extends Actor {
|
|||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.DoorCloses(door_guid))
|
||||
)
|
||||
case LocalAction.DropItem(player_guid, item) =>
|
||||
val definition = item.Definition
|
||||
val objectData = DroppedItemData(
|
||||
PlacementData(item.Position, item.Orientation),
|
||||
definition.Packet.ConstructorData(item).get
|
||||
)
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid,
|
||||
LocalResponse.DropItem(definition.ObjectId, item.GUID, objectData)
|
||||
)
|
||||
)
|
||||
case LocalAction.ObjectDelete(player_guid, item_guid, unk) =>
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.ObjectDelete(item_guid, unk))
|
||||
)
|
||||
case LocalAction.HackClear(player_guid, target, unk1, unk2) =>
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackClear(target.GUID, unk1, unk2))
|
||||
|
|
@ -83,12 +66,6 @@ class LocalService extends Actor {
|
|||
case _ => ;
|
||||
}
|
||||
|
||||
//messages to DroppedItemRemover
|
||||
case msg @ (RemoverActor.AddTask |
|
||||
RemoverActor.HurrySpecific | RemoverActor.HurryAll |
|
||||
RemoverActor.ClearSpecific | RemoverActor.ClearAll) =>
|
||||
janitor ! msg
|
||||
|
||||
//response from DoorCloseActor
|
||||
case DoorCloseActor.CloseTheDoor(door_guid, zone_id) =>
|
||||
LocalEvents.publish(
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ import akka.routing.RandomPool
|
|||
import net.psforever.objects._
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
|
||||
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
|
||||
import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectCreateMessageParent, PlacementData}
|
||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID, PlayerStateMessageUpstream}
|
||||
import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire, Vector3}
|
||||
import services.{Service, ServiceManager}
|
||||
import services.{RemoverActor, Service, ServiceManager}
|
||||
import services.avatar._
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -93,32 +94,59 @@ class ConcealPlayerTest extends ActorTest {
|
|||
}
|
||||
|
||||
class EquipmentInHandTest extends ActorTest {
|
||||
val tool = Tool(GlobalDefinitions.beamer)
|
||||
ServiceManager.boot(system) ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
|
||||
val service = system.actorOf(Props[AvatarService], "release-test-service")
|
||||
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||
val taskResolver = system.actorOf(Props[TaskResolver], "release-test-resolver")
|
||||
|
||||
val toolDef = GlobalDefinitions.beamer
|
||||
val tool = Tool(toolDef)
|
||||
tool.GUID = PlanetSideGUID(40)
|
||||
tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(41)
|
||||
val pkt = ObjectCreateMessage(
|
||||
toolDef.ObjectId,
|
||||
tool.GUID,
|
||||
ObjectCreateMessageParent(PlanetSideGUID(11), 2),
|
||||
toolDef.Packet.ConstructorData(tool).get
|
||||
)
|
||||
|
||||
"AvatarService" should {
|
||||
"pass EquipmentInHand" in {
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
service ! AvatarServiceMessage("test", AvatarAction.EquipmentInHand(PlanetSideGUID(10), PlanetSideGUID(11), 2, tool))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentInHand(PlanetSideGUID(11), 2, tool)))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentInHand(pkt)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EquipmentOnGroundTest extends ActorTest {
|
||||
class DroptItemTest extends ActorTest {
|
||||
ServiceManager.boot(system) ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
|
||||
val service = system.actorOf(Props[AvatarService], "release-test-service")
|
||||
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||
val taskResolver = system.actorOf(Props[TaskResolver], "drop-item-test-resolver")
|
||||
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "drop-item-test-zone")
|
||||
zone.Actor ! Zone.Init()
|
||||
|
||||
val toolDef = GlobalDefinitions.beamer
|
||||
val tool = Tool(toolDef)
|
||||
tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(1)
|
||||
val cdata = toolDef.Packet.ConstructorData(tool).get
|
||||
tool.Position = Vector3(1,2,3)
|
||||
tool.Orientation = Vector3(4,5,6)
|
||||
tool.GUID = PlanetSideGUID(40)
|
||||
tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(41)
|
||||
val pkt = ObjectCreateMessage(
|
||||
toolDef.ObjectId,
|
||||
tool.GUID,
|
||||
DroppedItemData(
|
||||
PlacementData(tool.Position, tool.Orientation),
|
||||
toolDef.Packet.ConstructorData(tool).get
|
||||
)
|
||||
)
|
||||
|
||||
"AvatarService" should {
|
||||
"pass EquipmentOnGround" in {
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
"pass DropItem" in {
|
||||
service ! Service.Join("test")
|
||||
service ! AvatarServiceMessage("test", AvatarAction.EquipmentOnGround(PlanetSideGUID(10), Vector3(300f, 200f, 100f), Vector3(450f, 300f, 150f), toolDef.ObjectId, PlanetSideGUID(11), cdata))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentOnGround(Vector3(300f, 200f, 100f), Vector3(450f, 300f, 150f), toolDef.ObjectId, PlanetSideGUID(11), cdata)))
|
||||
service ! AvatarServiceMessage("test", AvatarAction.DropItem(PlanetSideGUID(10), tool, zone))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.DropItem(pkt)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -193,6 +221,45 @@ class PlayerStateTest extends ActorTest {
|
|||
}
|
||||
}
|
||||
|
||||
class PickupItemATest extends ActorTest {
|
||||
val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
|
||||
obj.GUID = PlanetSideGUID(10)
|
||||
obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(11)
|
||||
|
||||
val toolDef = GlobalDefinitions.beamer
|
||||
val tool = Tool(toolDef)
|
||||
tool.GUID = PlanetSideGUID(40)
|
||||
tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(41)
|
||||
val pkt = ObjectCreateMessage(
|
||||
toolDef.ObjectId,
|
||||
tool.GUID,
|
||||
ObjectCreateMessageParent(PlanetSideGUID(10), 0),
|
||||
toolDef.Packet.ConstructorData(tool).get
|
||||
)
|
||||
|
||||
"pass PickUpItem as EquipmentInHand (visible pistol slot)" in {
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
service ! AvatarServiceMessage("test", AvatarAction.PickupItem(PlanetSideGUID(10), Zone.Nowhere, obj, 0, tool))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentInHand(pkt)))
|
||||
}
|
||||
}
|
||||
|
||||
class PickupItemBTest extends ActorTest {
|
||||
val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
|
||||
val tool = Tool(GlobalDefinitions.beamer)
|
||||
tool.GUID = PlanetSideGUID(40)
|
||||
|
||||
"pass PickUpItem as ObjectDelete (not visible inventory space)" in {
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
service ! AvatarServiceMessage("test", AvatarAction.PickupItem(PlanetSideGUID(10), Zone.Nowhere, obj, 6, tool))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ObjectDelete(tool.GUID, 0)))
|
||||
}
|
||||
}
|
||||
|
||||
class ReloadTest extends ActorTest {
|
||||
"AvatarService" should {
|
||||
"pass Reload" in {
|
||||
|
|
@ -320,14 +387,14 @@ class AvatarReleaseTest extends ActorTest {
|
|||
taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
zone.Population ! Zone.Corpse.Add(obj)
|
||||
expectNoMsg(100 milliseconds) //spacer
|
||||
expectNoMsg(200 milliseconds) //spacer
|
||||
|
||||
assert(zone.Corpses.size == 1)
|
||||
assert(obj.HasGUID)
|
||||
val guid = obj.GUID
|
||||
service ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone, Some(1000000000))) //alive for one second
|
||||
service ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone, Some(1 second))) //alive for one second
|
||||
|
||||
val reply1 = receiveOne(100 milliseconds)
|
||||
val reply1 = receiveOne(200 milliseconds)
|
||||
assert(reply1.isInstanceOf[AvatarServiceResponse])
|
||||
val reply1msg = reply1.asInstanceOf[AvatarServiceResponse]
|
||||
assert(reply1msg.toChannel == "/test/Avatar")
|
||||
|
|
@ -343,7 +410,7 @@ class AvatarReleaseTest extends ActorTest {
|
|||
assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete])
|
||||
assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid)
|
||||
|
||||
expectNoMsg(1000 milliseconds)
|
||||
expectNoMsg(1 seconds)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
assert(!obj.HasGUID)
|
||||
}
|
||||
|
|
@ -369,14 +436,14 @@ class AvatarReleaseEarly1Test extends ActorTest {
|
|||
taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
zone.Population ! Zone.Corpse.Add(obj)
|
||||
expectNoMsg(100 milliseconds) //spacer
|
||||
expectNoMsg(200 milliseconds) //spacer
|
||||
|
||||
assert(zone.Corpses.size == 1)
|
||||
assert(obj.HasGUID)
|
||||
val guid = obj.GUID
|
||||
service ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone)) //3+ minutes!
|
||||
|
||||
val reply1 = receiveOne(100 milliseconds)
|
||||
val reply1 = receiveOne(200 milliseconds)
|
||||
assert(reply1.isInstanceOf[AvatarServiceResponse])
|
||||
val reply1msg = reply1.asInstanceOf[AvatarServiceResponse]
|
||||
assert(reply1msg.toChannel == "/test/Avatar")
|
||||
|
|
@ -384,8 +451,8 @@ class AvatarReleaseEarly1Test extends ActorTest {
|
|||
assert(reply1msg.replyMessage.isInstanceOf[AvatarResponse.Release])
|
||||
assert(reply1msg.replyMessage.asInstanceOf[AvatarResponse.Release].player == obj)
|
||||
|
||||
service ! AvatarServiceMessage.RemoveSpecificCorpse(List(obj)) //IMPORTANT: ONE ENTRY
|
||||
val reply2 = receiveOne(100 milliseconds)
|
||||
service ! AvatarServiceMessage.Corpse(RemoverActor.HurrySpecific(List(obj), zone)) //IMPORTANT: ONE ENTRY
|
||||
val reply2 = receiveOne(200 milliseconds)
|
||||
assert(reply2.isInstanceOf[AvatarServiceResponse])
|
||||
val reply2msg = reply2.asInstanceOf[AvatarServiceResponse]
|
||||
assert(reply2msg.toChannel.equals("/test/Avatar"))
|
||||
|
|
@ -393,7 +460,7 @@ class AvatarReleaseEarly1Test extends ActorTest {
|
|||
assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete])
|
||||
assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid)
|
||||
|
||||
expectNoMsg(600 milliseconds)
|
||||
expectNoMsg(1 seconds)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
assert(!obj.HasGUID)
|
||||
}
|
||||
|
|
@ -420,14 +487,14 @@ class AvatarReleaseEarly2Test extends ActorTest {
|
|||
taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
zone.Population ! Zone.Corpse.Add(obj)
|
||||
expectNoMsg(100 milliseconds) //spacer
|
||||
expectNoMsg(200 milliseconds) //spacer
|
||||
|
||||
assert(zone.Corpses.size == 1)
|
||||
assert(obj.HasGUID)
|
||||
val guid = obj.GUID
|
||||
service ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone)) //3+ minutes!
|
||||
|
||||
val reply1 = receiveOne(100 milliseconds)
|
||||
val reply1 = receiveOne(200 milliseconds)
|
||||
assert(reply1.isInstanceOf[AvatarServiceResponse])
|
||||
val reply1msg = reply1.asInstanceOf[AvatarServiceResponse]
|
||||
assert(reply1msg.toChannel == "/test/Avatar")
|
||||
|
|
@ -435,7 +502,7 @@ class AvatarReleaseEarly2Test extends ActorTest {
|
|||
assert(reply1msg.replyMessage.isInstanceOf[AvatarResponse.Release])
|
||||
assert(reply1msg.replyMessage.asInstanceOf[AvatarResponse.Release].player == obj)
|
||||
|
||||
service ! AvatarServiceMessage.RemoveSpecificCorpse(List(objAlt, obj)) //IMPORTANT: TWO ENTRIES
|
||||
service ! AvatarServiceMessage.Corpse(RemoverActor.HurrySpecific(List(objAlt, obj), zone)) //IMPORTANT: TWO ENTRIES
|
||||
val reply2 = receiveOne(100 milliseconds)
|
||||
assert(reply2.isInstanceOf[AvatarServiceResponse])
|
||||
val reply2msg = reply2.asInstanceOf[AvatarServiceResponse]
|
||||
|
|
@ -444,7 +511,7 @@ class AvatarReleaseEarly2Test extends ActorTest {
|
|||
assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete])
|
||||
assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid)
|
||||
|
||||
expectNoMsg(600 milliseconds)
|
||||
expectNoMsg(1 seconds)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
assert(!obj.HasGUID)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue