mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-04-29 16:25:30 +00:00
commit
ab56a9f11b
4 changed files with 213 additions and 114 deletions
|
|
@ -13,34 +13,31 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
class TaskResolver() extends Actor {
|
class TaskResolver() extends Actor {
|
||||||
/** list of all work currently managed by this TaskResolver */
|
/** list of all work currently managed by this resolver */
|
||||||
private val tasks : ListBuffer[TaskResolver.TaskEntry] = new ListBuffer[TaskResolver.TaskEntry]
|
private val tasks : ListBuffer[TaskResolver.TaskEntry] = new ListBuffer[TaskResolver.TaskEntry]
|
||||||
/** scheduled examination of all managed work */
|
/** scheduled termination of tardy managed work */
|
||||||
private var timeoutCleanup : Cancellable = TaskResolver.DefaultCancellable
|
private var timeoutCleanup : Cancellable = TaskResolver.DefaultCancellable
|
||||||
//private[this] val log = org.log4s.getLogger
|
/** logging utilities; default to tracing */
|
||||||
|
private[this] val log = org.log4s.getLogger
|
||||||
|
private def trace(msg : String) = log.trace(msg)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deal with any tasks that are still enqueued with this expiring `TaskResolver`.<br>
|
* Deal with any tasks that are still enqueued with this expiring `TaskResolver`.
|
||||||
* <br>
|
|
||||||
* First, eliminate all timed-out tasks.
|
|
||||||
* Secondly, deal with all tasks that have reported "success" but have not yet been handled.
|
|
||||||
* Finally, all other remaining tasks should be treated as if they had failed.
|
|
||||||
*/
|
*/
|
||||||
override def aroundPostStop() = {
|
override def aroundPostStop() = {
|
||||||
super.aroundPostStop()
|
/*
|
||||||
|
First, eliminate all timed-out tasks.
|
||||||
|
Secondly, deal with all tasks that have reported "success" but have not yet been handled.
|
||||||
|
Finally, all other remaining tasks should be treated as if they had failed.
|
||||||
|
*/
|
||||||
timeoutCleanup.cancel()
|
timeoutCleanup.cancel()
|
||||||
TimeoutCleanup()
|
TimeoutCleanup()
|
||||||
OnSuccess()
|
tasks.filter(entry => entry.task.isComplete == Task.Resolution.Success).foreach(entry => OnSuccess(entry.task) )
|
||||||
val ex : Throwable = new Exception(s"a task is being stopped")
|
val ex : Throwable = new Exception(s"a task is being stopped")
|
||||||
OnFailure(ex)
|
tasks.foreach(entry => {
|
||||||
tasks.indices.foreach({index =>
|
OnFailure(entry.task, ex)
|
||||||
val entry = tasks(index)
|
|
||||||
PropagateAbort(index, ex)
|
|
||||||
if(entry.isASubtask) {
|
|
||||||
entry.supertaskRef ! Failure(ex) //alert our superior task's resolver we have completed
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
super.aroundPostStop()
|
||||||
}
|
}
|
||||||
|
|
||||||
def receive : Receive = {
|
def receive : Receive = {
|
||||||
|
|
@ -51,15 +48,21 @@ class TaskResolver() extends Actor {
|
||||||
QueueSubtasks(aTask, subtasks)
|
QueueSubtasks(aTask, subtasks)
|
||||||
|
|
||||||
case TaskResolver.GiveSubtask(aTask, subtasks, resolver) =>
|
case TaskResolver.GiveSubtask(aTask, subtasks, resolver) =>
|
||||||
QueueSubtasks(aTask, subtasks, true, resolver)
|
QueueSubtasks(aTask, subtasks, resolver)
|
||||||
|
|
||||||
case TaskResolver.CompletedSubtask() =>
|
case TaskResolver.CompletedSubtask(obj) => //inter-resolver calls
|
||||||
ExecuteNewTasks()
|
ExecuteNewTasks(obj)
|
||||||
|
|
||||||
case Success(_) => //ignore the contents as unreliable
|
case Success(obj : Task) => //inter-resolver calls
|
||||||
|
OnSuccess(obj)
|
||||||
|
|
||||||
|
case Success | Success(_) => //success redirected from called event
|
||||||
OnSuccess()
|
OnSuccess()
|
||||||
|
|
||||||
case Failure(ex) =>
|
case TaskResolver.Failure(obj, ex) => //inter-resolver calls
|
||||||
|
OnFailure(obj, ex)
|
||||||
|
|
||||||
|
case Failure(ex) => //failure redirected from called event
|
||||||
OnFailure(ex)
|
OnFailure(ex)
|
||||||
|
|
||||||
case TaskResolver.AbortTask(task, ex) =>
|
case TaskResolver.AbortTask(task, ex) =>
|
||||||
|
|
@ -68,7 +71,8 @@ class TaskResolver() extends Actor {
|
||||||
case TaskResolver.TimeoutCleanup() =>
|
case TaskResolver.TimeoutCleanup() =>
|
||||||
TimeoutCleanup()
|
TimeoutCleanup()
|
||||||
|
|
||||||
case _ => ;
|
case msg =>
|
||||||
|
log.warn(s"$self received an unexpected message $msg from $sender")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -78,7 +82,8 @@ class TaskResolver() extends Actor {
|
||||||
private def GiveTask(aTask : Task) : Unit = {
|
private def GiveTask(aTask : Task) : Unit = {
|
||||||
val entry : TaskResolver.TaskEntry = TaskResolver.TaskEntry(aTask)
|
val entry : TaskResolver.TaskEntry = TaskResolver.TaskEntry(aTask)
|
||||||
tasks += entry
|
tasks += entry
|
||||||
entry.Execute(self) //send this Actor; aesthetically pleasant expression
|
trace(s"enqueue and start task $aTask")
|
||||||
|
entry.Execute(self)
|
||||||
StartTimeoutCheck()
|
StartTimeoutCheck()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,16 +106,15 @@ class TaskResolver() extends Actor {
|
||||||
* The parent of this `TaskResolver` is the router logic for all brethren `TaskResolver` `Actors`.
|
* The parent of this `TaskResolver` is the router logic for all brethren `TaskResolver` `Actors`.
|
||||||
* @param task the work to be completed
|
* @param task the work to be completed
|
||||||
* @param subtasks other work that needs to be completed first
|
* @param subtasks other work that needs to be completed first
|
||||||
* @param isSubTask `true`, if this task counts as internal or as a leaf in the chain of `Task` dependency;
|
|
||||||
* `false`, by default, if we are the top of the chain fo dependency
|
|
||||||
* @param resolver the `TaskResolver` that distributed this work, thus determining that this work is a sub-task;
|
* @param resolver the `TaskResolver` that distributed this work, thus determining that this work is a sub-task;
|
||||||
* by default, no one, as the work is identified as a main task
|
* by default, no one, as the work is identified as a main task
|
||||||
*/
|
*/
|
||||||
private def QueueSubtasks(task : Task, subtasks : List[TaskResolver.GiveTask], isSubTask : Boolean = false, resolver : ActorRef = Actor.noSender) : Unit = {
|
private def QueueSubtasks(task : Task, subtasks : List[TaskResolver.GiveTask], resolver : ActorRef = Actor.noSender) : Unit = {
|
||||||
val sublist : List[Task] = subtasks.map(task => task.task)
|
val entry : TaskResolver.TaskEntry = TaskResolver.TaskEntry(task, subtasks.map(task => task.task), resolver)
|
||||||
val entry : TaskResolver.TaskEntry = TaskResolver.TaskEntry(task, sublist, isSubTask, resolver)
|
|
||||||
tasks += entry
|
tasks += entry
|
||||||
if(sublist.isEmpty) { //a leaf in terms of task dependency; so, not dependent on any other work
|
trace(s"enqueue task $task")
|
||||||
|
if(subtasks.isEmpty) { //a leaf in terms of task dependency; so, not dependent on any other work
|
||||||
|
trace(s"start task $task")
|
||||||
entry.Execute(self)
|
entry.Execute(self)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -123,65 +127,121 @@ class TaskResolver() extends Actor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform these checks when a task has reported successful completion to this TaskResolver.
|
* Perform these checks when a task has reported successful completion to this TaskResolver.
|
||||||
* Since the `Success(_)` can not be associated with a specific task, every task and subtask will be checked.
|
* Every task and subtask will be checked, starting from the end of the list of queued entries
|
||||||
|
* and only the first discovered one will be used.
|
||||||
*/
|
*/
|
||||||
private def OnSuccess(): Unit = {
|
private def OnSuccess(): Unit = {
|
||||||
//by reversing the List, we can remove TaskEntries without disrupting the order
|
//by reversing the List, we find the most outstanding Task with the completion state
|
||||||
TaskResolver.filterCompletion(tasks.indices.reverseIterator, tasks.toList, Task.Resolution.Success).foreach({index =>
|
TaskResolver.filterCompletion(tasks.indices.reverseIterator, tasks.toList, Task.Resolution.Success) match {
|
||||||
val entry = tasks(index)
|
case Some(index) =>
|
||||||
entry.task.onSuccess()
|
GeneralOnSuccess(index)
|
||||||
if(entry.isASubtask) {
|
case None => ;
|
||||||
entry.supertaskRef ! TaskResolver.CompletedSubtask() //alert our dependent task's resolver that we have completed
|
}
|
||||||
}
|
}
|
||||||
TaskCleanup(index)
|
|
||||||
})
|
/**
|
||||||
|
* Perform these checks when a task has reported successful completion to this TaskResolver.
|
||||||
|
* @param task a `Task` object
|
||||||
|
*/
|
||||||
|
private def OnSuccess(task : Task): Unit = {
|
||||||
|
//find specific task and dequeue
|
||||||
|
TaskResolver.findTask(tasks.iterator, task) match {
|
||||||
|
case Some(index) =>
|
||||||
|
GeneralOnSuccess(index)
|
||||||
|
case None => ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform these checks when a task has reported successful completion to this TaskResolver.
|
||||||
|
* This is what actually happens upon completion.
|
||||||
|
* @param index the `TaskEntry` index
|
||||||
|
*/
|
||||||
|
private def GeneralOnSuccess(index : Int) : Unit = {
|
||||||
|
val entry = tasks(index)
|
||||||
|
entry.task.onSuccess()
|
||||||
|
trace(s"success with this task ${entry.task}")
|
||||||
|
if(entry.supertaskRef != ActorRef.noSender) {
|
||||||
|
entry.supertaskRef ! TaskResolver.CompletedSubtask(entry.task) //alert our dependent task's resolver that we have completed
|
||||||
|
}
|
||||||
|
TaskCleanup(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scan across a group of sub-tasks and determine if the associated main `Task` may execute.
|
* Scan across a group of sub-tasks and determine if the associated main `Task` may execute.
|
||||||
* All of the sub-tasks must report a `Success` completion status before the main work can begin.
|
* All of the sub-tasks must report a `Success` completion status before the main work can begin.
|
||||||
|
* @param subtask a `Task` that is a subtask of some parent task in this resolver's group
|
||||||
*/
|
*/
|
||||||
private def ExecuteNewTasks() : Unit = {
|
private def ExecuteNewTasks(subtask : Task) : Unit = {
|
||||||
tasks.filter({taskEntry => taskEntry.subtasks.nonEmpty}).foreach(entry => {
|
TaskResolver.findTaskWithSubtask(tasks.iterator, subtask) match {
|
||||||
if(TaskResolver.filterCompletionMatch(entry.subtasks.iterator, Task.Resolution.Success)) {
|
case Some(index) =>
|
||||||
entry.Execute(self)
|
val entry = tasks(index)
|
||||||
StartTimeoutCheck()
|
if(TaskResolver.filterCompletionMatch(entry.subtasks.iterator, Task.Resolution.Success)) {
|
||||||
}
|
trace(s"start new task ${entry.task}")
|
||||||
})
|
entry.Execute(self)
|
||||||
|
StartTimeoutCheck()
|
||||||
|
}
|
||||||
|
case None => ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform these checks when a task has reported failure to this TaskResolver.
|
* Perform these checks when a task has reported failure to this `TaskResolver`.
|
||||||
* Since the `Failure(Throwable)` can not be associated with a specific task, every task and subtask will be checked.
|
* Since the `Failure(Throwable)` can not be associated with a specific task,
|
||||||
|
* every task and subtask will be checked, starting from the end of the list of queued entries,
|
||||||
|
* and only the first discovered one will be used.
|
||||||
* Consequently, the specific `Throwable` that contains the error message may have nothing to do with the failed task.
|
* Consequently, the specific `Throwable` that contains the error message may have nothing to do with the failed task.
|
||||||
* @param ex a `Throwable` that reports what happened to the task
|
* @param ex a `Throwable` that reports what happened to the task
|
||||||
*/
|
*/
|
||||||
private def OnFailure(ex : Throwable) : Unit = {
|
private def OnFailure(ex : Throwable) : Unit = {
|
||||||
TaskResolver.filterCompletion(tasks.indices.reverseIterator, tasks.toList, Task.Resolution.Failure).foreach({index =>
|
//by reversing the List, we find the most outstanding Task with the completion state
|
||||||
val entry = tasks(index)
|
TaskResolver.filterCompletion(tasks.indices.reverseIterator, tasks.toList, Task.Resolution.Failure) match {
|
||||||
PropagateAbort(index, ex)
|
case Some(index) =>
|
||||||
entry.task.onFailure(ex) //TODO let the error be disjoint?
|
GeneralOnFailure(index, ex)
|
||||||
if(entry.isASubtask) {
|
case None => ;
|
||||||
entry.supertaskRef ! Failure(ex) //alert our superior task's resolver we have completed
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
FaultSubtasks()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scan across a group of sub-tasks and, if any have reported `Failure`, report to the main `Task` that it should fail as well.
|
* Perform these checks when a task has reported failure to this `TaskResolver`.
|
||||||
|
* @param subtask the task that had reported failure from some other resolver
|
||||||
|
* @param ex a `Throwable` that reports what happened to the task
|
||||||
*/
|
*/
|
||||||
private def FaultSubtasks() : Unit = {
|
private def OnFailure(subtask : Task, ex : Throwable) : Unit = {
|
||||||
tasks.indices.filter({index => tasks(index).subtasks.nonEmpty}).reverse.foreach(index => {
|
TaskResolver.findTaskWithSubtask(tasks.iterator, subtask) match {
|
||||||
val entry = tasks(index)
|
case Some(index) =>
|
||||||
if(TaskResolver.filterCompletionMatch(entry.subtasks.iterator, Task.Resolution.Failure)) {
|
GeneralOnFailure(index, ex)
|
||||||
val ex : Throwable = new Exception(s"a task ${entry.task} had a subtask that failed")
|
case None => ;
|
||||||
entry.task.onFailure(ex)
|
}
|
||||||
if(entry.isASubtask) {
|
}
|
||||||
entry.supertaskRef ! Failure(ex) //alert our superior task's resolver we have completed
|
|
||||||
}
|
/**
|
||||||
TaskCleanup(index)
|
* Perform these checks when a task has reported failure to this `TaskResolver`.
|
||||||
}
|
* This is what actually happens upon completion.
|
||||||
|
* @param index the `TaskEntry` index
|
||||||
|
* @param ex a `Throwable` that reports what happened to the task
|
||||||
|
*/
|
||||||
|
private def GeneralOnFailure(index : Int, ex : Throwable) : Unit = {
|
||||||
|
val entry = tasks(index)
|
||||||
|
val task = entry.task
|
||||||
|
trace(s"failure with this task $task")
|
||||||
|
task.onAbort(ex)
|
||||||
|
task.onFailure(ex)
|
||||||
|
if(entry.supertaskRef != ActorRef.noSender) {
|
||||||
|
entry.supertaskRef ! TaskResolver.Failure(task, ex) //alert our superior task's resolver we have completed
|
||||||
|
}
|
||||||
|
FaultSubtasks(entry)
|
||||||
|
TaskCleanup(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instruct all subtasks of a given `Task` to fail.
|
||||||
|
* @param entry the target parent entry (that has failed)
|
||||||
|
*/
|
||||||
|
private def FaultSubtasks(entry : TaskResolver.TaskEntry) : Unit = {
|
||||||
|
val ex : Throwable = new Exception(s"a task ${entry.task} had a subtask that failed")
|
||||||
|
entry.subtasks.foreach(subtask => {
|
||||||
|
context.parent ! Broadcast(TaskResolver.Failure(subtask, ex)) //we have no clue where this subtask was hosted
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -191,9 +251,10 @@ class TaskResolver() extends Actor {
|
||||||
* @param ex a `Throwable` that reports what happened to the work
|
* @param ex a `Throwable` that reports what happened to the work
|
||||||
*/
|
*/
|
||||||
private def OnAbort(task : Task, ex : Throwable) : Unit = {
|
private def OnAbort(task : Task, ex : Throwable) : Unit = {
|
||||||
TaskResolver.findTaskIndex(tasks.iterator, task) match {
|
TaskResolver.findTask(tasks.iterator, task) match {
|
||||||
case Some(index) =>
|
case Some(index) =>
|
||||||
PropagateAbort(index, ex)
|
PropagateAbort(index, ex)
|
||||||
|
TaskCleanup(index)
|
||||||
case None => ;
|
case None => ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -206,11 +267,11 @@ class TaskResolver() extends Actor {
|
||||||
private def PropagateAbort(index : Int, ex : Throwable) : Unit = {
|
private def PropagateAbort(index : Int, ex : Throwable) : Unit = {
|
||||||
tasks(index).subtasks.foreach({subtask =>
|
tasks(index).subtasks.foreach({subtask =>
|
||||||
if(subtask.isComplete == Task.Resolution.Success) {
|
if(subtask.isComplete == Task.Resolution.Success) {
|
||||||
|
trace(s"aborting task $subtask")
|
||||||
subtask.onAbort(ex)
|
subtask.onAbort(ex)
|
||||||
}
|
}
|
||||||
context.parent ! Broadcast(TaskResolver.AbortTask(subtask, ex))
|
context.parent ! Broadcast(TaskResolver.AbortTask(subtask, ex))
|
||||||
})
|
})
|
||||||
TaskCleanup(index)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -260,9 +321,17 @@ object TaskResolver {
|
||||||
private final case class TimeoutCleanup()
|
private final case class TimeoutCleanup()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* A specific kind of `Failure` that reports on which specific `Task` has reported failure.
|
||||||
|
* @param obj a task object
|
||||||
|
* @param ex information about what went wrong
|
||||||
*/
|
*/
|
||||||
private final case class CompletedSubtask()
|
private final case class Failure(obj : Task, ex : Throwable)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A specific kind of `Success` that reports on which specific `Task` has reported Success where that `Task` was some other `Task`'s subtask.
|
||||||
|
* @param obj a task object
|
||||||
|
*/
|
||||||
|
private final case class CompletedSubtask(obj : Task)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `Broadcast` message designed to find and remove a particular task from this series of routed `Actors`.
|
* A `Broadcast` message designed to find and remove a particular task from this series of routed `Actors`.
|
||||||
|
|
@ -275,10 +344,10 @@ object TaskResolver {
|
||||||
* Storage unit for a specific unit of work, plus extra information.
|
* Storage unit for a specific unit of work, plus extra information.
|
||||||
* @param task the work to be completed
|
* @param task the work to be completed
|
||||||
* @param subtasks other work that needs to be completed first
|
* @param subtasks other work that needs to be completed first
|
||||||
* @param isASubtask whether this work is intermediary or the last in a dependency chain
|
* //@param isASubtask whether this work is intermediary or the last in a dependency chain
|
||||||
* @param supertaskRef the `TaskResolver` that will handle work that depends on the outcome of this work
|
* @param supertaskRef the `TaskResolver` that will handle work that depends on the outcome of this work
|
||||||
*/
|
*/
|
||||||
private final case class TaskEntry(task : Task, subtasks : List[Task] = Nil, isASubtask : Boolean = false, supertaskRef : ActorRef = Actor.noSender) {
|
private final case class TaskEntry(task : Task, subtasks : List[Task] = Nil, supertaskRef : ActorRef = Actor.noSender) {
|
||||||
private var start : Long = 0L
|
private var start : Long = 0L
|
||||||
private var isExecuting : Boolean = false
|
private var isExecuting : Boolean = false
|
||||||
|
|
||||||
|
|
@ -286,10 +355,14 @@ object TaskResolver {
|
||||||
|
|
||||||
def Executing : Boolean = isExecuting
|
def Executing : Boolean = isExecuting
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only execute each task once.
|
||||||
|
* @param ref the `TaskResolver` currently handling this `Task`/`TaskEntry`
|
||||||
|
*/
|
||||||
def Execute(ref : ActorRef) : Unit = {
|
def Execute(ref : ActorRef) : Unit = {
|
||||||
if(!isExecuting) {
|
if(!isExecuting) {
|
||||||
start = Task.TimeNow
|
|
||||||
isExecuting = true
|
isExecuting = true
|
||||||
|
start = Task.TimeNow
|
||||||
task.Execute(ref)
|
task.Execute(ref)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -304,45 +377,22 @@ object TaskResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the index of the targeted `Task`, if it is enqueued here.
|
* Scan across a group of tasks to determine which ones match the target completion status.
|
||||||
* @param iter an `Iterator` of
|
* @param iter an `Iterator` of enqueued `TaskEntry` indices
|
||||||
* @param task a target `Task`
|
* @param resolution the target completion status
|
||||||
* @param index the current index in the aforementioned `List`;
|
* @return the first valid index when `TaskEntry` has its primary `Task` matching the completion status
|
||||||
* defaults to 0
|
|
||||||
* @return the index of the discovered task, or `None`
|
|
||||||
*/
|
*/
|
||||||
@tailrec private def findTaskIndex(iter : Iterator[TaskResolver.TaskEntry], task : Task, index : Int = 0) : Option[Int] = {
|
@tailrec private def filterCompletion(iter : Iterator[Int], tasks : List[TaskEntry], resolution : Task.Resolution.Value) : Option[Int] = {
|
||||||
if(!iter.hasNext) {
|
if(!iter.hasNext) {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(iter.next.task == task) {
|
val index : Int = iter.next
|
||||||
|
if(tasks(index).task.isComplete == resolution) {
|
||||||
Some(index)
|
Some(index)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
findTaskIndex(iter, task, index + 1)
|
filterCompletion(iter, tasks, resolution)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scan across a group of tasks to determine which ones match the target completion status.
|
|
||||||
* @param iter an `Iterator` of enqueued `TaskEntry` indices
|
|
||||||
* @param resolution the target completion status
|
|
||||||
* @param indexList a persistent `List` of indices
|
|
||||||
* @return the `List` of all valid `Task` indices
|
|
||||||
*/
|
|
||||||
@tailrec private def filterCompletion(iter : Iterator[Int], tasks : List[TaskEntry], resolution : Task.Resolution.Value, indexList : List[Int] = Nil) : List[Int] = {
|
|
||||||
if(!iter.hasNext) {
|
|
||||||
indexList
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val index : Int = iter.next
|
|
||||||
if(tasks(index).task.isComplete == resolution) {
|
|
||||||
filterCompletion(iter, tasks, resolution, indexList :+ index)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
filterCompletion(iter, tasks, resolution, indexList)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -390,4 +440,49 @@ object TaskResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the index of the targeted `Task`, if it is enqueued here.
|
||||||
|
* @param iter an `Iterator` of entries
|
||||||
|
* @param target a target `Task`
|
||||||
|
* @param index the current index in the aforementioned `List`;
|
||||||
|
* defaults to 0
|
||||||
|
* @return the index of the discovered task, or `None`
|
||||||
|
*/
|
||||||
|
@tailrec private def findTask(iter : Iterator[TaskEntry], target : Task, index : Int = 0) : Option[Int] = {
|
||||||
|
if(!iter.hasNext) {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(iter.next.task == target) {
|
||||||
|
Some(index)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
findTask(iter, target, index + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the index of the targeted `Task`, if it is enqueued here, given a specific "subtask" of that `Task`.
|
||||||
|
* @param iter an `Iterator` of entries
|
||||||
|
* @param target a target subtask
|
||||||
|
* @param index the current index in the aforementioned `List`;
|
||||||
|
* defaults to 0
|
||||||
|
* @return the index of the discovered task, or `None`
|
||||||
|
*/
|
||||||
|
@tailrec private def findTaskWithSubtask(iter : Iterator[TaskEntry], target : Task, index : Int = 0) : Option[Int] = {
|
||||||
|
if(!iter.hasNext) {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val tEntry = iter.next
|
||||||
|
if(tEntry.subtasks.contains(target)) {
|
||||||
|
Some(index)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
findTaskWithSubtask(iter, target, index + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
package net.psforever.objects.zones
|
package net.psforever.objects.zones
|
||||||
|
|
||||||
import akka.actor.{ActorContext, ActorRef, Props}
|
import akka.actor.{ActorContext, ActorRef, Props}
|
||||||
|
import akka.routing.RandomPool
|
||||||
import net.psforever.objects.serverobject.doors.Base
|
import net.psforever.objects.serverobject.doors.Base
|
||||||
import net.psforever.objects.{PlanetSideGameObject, Player}
|
import net.psforever.objects.{PlanetSideGameObject, Player}
|
||||||
import net.psforever.objects.equipment.Equipment
|
import net.psforever.objects.equipment.Equipment
|
||||||
|
|
@ -66,7 +67,7 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
||||||
def Init(implicit context : ActorContext) : Unit = {
|
def Init(implicit context : ActorContext) : Unit = {
|
||||||
if(accessor == ActorRef.noSender) {
|
if(accessor == ActorRef.noSender) {
|
||||||
implicit val guid : NumberPoolHub = this.guid //passed into builderObject.Build implicitly
|
implicit val guid : NumberPoolHub = this.guid //passed into builderObject.Build implicitly
|
||||||
accessor = context.actorOf(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystem.AllocateNumberPoolActors(guid)), s"$Id-uns")
|
accessor = context.actorOf(RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystem.AllocateNumberPoolActors(guid))), s"$Id-uns")
|
||||||
ground = context.actorOf(Props(classOf[ZoneGroundActor], equipmentOnGround), s"$Id-ground")
|
ground = context.actorOf(Props(classOf[ZoneGroundActor], equipmentOnGround), s"$Id-ground")
|
||||||
|
|
||||||
Map.LocalObjects.foreach({ builderObject =>
|
Map.LocalObjects.foreach({ builderObject =>
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,8 @@ object PsLogin {
|
||||||
|
|
||||||
LocalObject(DoorObjectBuilder(door, 330))
|
LocalObject(DoorObjectBuilder(door, 330))
|
||||||
LocalObject(DoorObjectBuilder(door, 332))
|
LocalObject(DoorObjectBuilder(door, 332))
|
||||||
|
LocalObject(DoorObjectBuilder(door, 370))
|
||||||
|
LocalObject(DoorObjectBuilder(door, 371))
|
||||||
LocalObject(DoorObjectBuilder(door, 372))
|
LocalObject(DoorObjectBuilder(door, 372))
|
||||||
LocalObject(DoorObjectBuilder(door, 373))
|
LocalObject(DoorObjectBuilder(door, 373))
|
||||||
LocalObject(IFFLockObjectBuilder(lock_external, 556))
|
LocalObject(IFFLockObjectBuilder(lock_external, 556))
|
||||||
|
|
|
||||||
|
|
@ -586,7 +586,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentInHand(player_guid, slot, item))
|
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentInHand(player_guid, slot, item))
|
||||||
}
|
}
|
||||||
case None =>
|
case None =>
|
||||||
continent.Actor ! Zone.DropItemOnGround(item, item.Position, item.Orientation) //restore
|
continent.Ground ! Zone.DropItemOnGround(item, item.Position, item.Orientation) //restore
|
||||||
}
|
}
|
||||||
|
|
||||||
case ItemHacking(tplayer, target, tool_guid, delta, completeAction, tickAction) =>
|
case ItemHacking(tplayer, target, tool_guid, delta, completeAction, tickAction) =>
|
||||||
|
|
@ -1390,7 +1390,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
|
|
||||||
def Execute(resolver : ActorRef) : Unit = {
|
def Execute(resolver : ActorRef) : Unit = {
|
||||||
localTarget.Slot(localIndex).Equipment = localObject
|
localTarget.Slot(localIndex).Equipment = localObject
|
||||||
resolver ! scala.util.Success(localObject)
|
resolver ! scala.util.Success(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def onSuccess() : Unit = {
|
override def onSuccess() : Unit = {
|
||||||
|
|
@ -1442,8 +1442,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
}
|
}
|
||||||
|
|
||||||
def Execute(resolver : ActorRef) : Unit = {
|
def Execute(resolver : ActorRef) : Unit = {
|
||||||
|
log.info(s"Player $localPlayer is registered")
|
||||||
|
resolver ! scala.util.Success(this)
|
||||||
localAnnounce ! PlayerLoaded(localPlayer) //alerts WSA
|
localAnnounce ! PlayerLoaded(localPlayer) //alerts WSA
|
||||||
resolver ! scala.util.Success(localPlayer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override def onFailure(ex : Throwable) : Unit = {
|
override def onFailure(ex : Throwable) : Unit = {
|
||||||
|
|
@ -1536,7 +1537,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
|
|
||||||
def Execute(resolver : ActorRef) : Unit = {
|
def Execute(resolver : ActorRef) : Unit = {
|
||||||
localTarget.Slot(localIndex).Equipment = None
|
localTarget.Slot(localIndex).Equipment = None
|
||||||
resolver ! scala.util.Success(localObject)
|
resolver ! scala.util.Success(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def onSuccess() : Unit = {
|
override def onSuccess() : Unit = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue