diff --git a/server/src/test/scala/actor/service/AvatarServiceTest.scala b/server/src/test/scala/actor/service/AvatarServiceTest.scala
index ec324027..448d4a27 100644
--- a/server/src/test/scala/actor/service/AvatarServiceTest.scala
+++ b/server/src/test/scala/actor/service/AvatarServiceTest.scala
@@ -491,11 +491,7 @@ class AvatarStowEquipmentTest extends ActorTest {
/*
Preparation for these three Release tests is involved.
-The ServiceManager must not only be set up correctly, but must be given a TaskResolver.
-The AvatarService is started and that starts CorpseRemovalActor, an essential part of this test.
-The CorpseRemovalActor needs that TaskResolver created by the ServiceManager;
-but, another independent TaskResolver will be needed for manual parts of the test.
-(The ServiceManager's TaskResolver can be "borrowed" but that requires writing code to intercept it.)
+The ServiceManager must be set up correctly.
The Zone needs to be set up and initialized properly with a ZoneActor.
The ZoneActor builds the GUID Actor and the ZonePopulationActor.
diff --git a/src/main/resources/akka.conf b/src/main/resources/akka.conf
index 01953b2e..b05cd94e 100644
--- a/src/main/resources/akka.conf
+++ b/src/main/resources/akka.conf
@@ -46,11 +46,6 @@ akka.actor.deployment {
dispatcher = galaxy-service
}
- # Isolate tasks
- "/service/taskResolver*" {
- dispatcher = task-dispatcher
- }
-
# Bottleneck (dedicated thread)
"/service/cluster" {
dispatcher = interstellar-cluster-service
diff --git a/src/main/resources/guid-pools/c1.json b/src/main/resources/guid-pools/c1.json
new file mode 100644
index 00000000..cb02ee06
--- /dev/null
+++ b/src/main/resources/guid-pools/c1.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 937,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/c2.json b/src/main/resources/guid-pools/c2.json
new file mode 100644
index 00000000..8c547436
--- /dev/null
+++ b/src/main/resources/guid-pools/c2.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 1615,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/c3.json b/src/main/resources/guid-pools/c3.json
new file mode 100644
index 00000000..5a231211
--- /dev/null
+++ b/src/main/resources/guid-pools/c3.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 1128,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/c4.json b/src/main/resources/guid-pools/c4.json
new file mode 100644
index 00000000..2f3ba16d
--- /dev/null
+++ b/src/main/resources/guid-pools/c4.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 904,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/c5.json b/src/main/resources/guid-pools/c5.json
new file mode 100644
index 00000000..903a5213
--- /dev/null
+++ b/src/main/resources/guid-pools/c5.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 635,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/c6.json b/src/main/resources/guid-pools/c6.json
new file mode 100644
index 00000000..6a64fcbd
--- /dev/null
+++ b/src/main/resources/guid-pools/c6.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 908,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/default.json b/src/main/resources/guid-pools/default.json
new file mode 100644
index 00000000..7d6ec482
--- /dev/null
+++ b/src/main/resources/guid-pools/default.json
@@ -0,0 +1,74 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 3000,
+ "Selector": "specific"
+ },
+ {
+ "Name": "players",
+ "Start": 3001,
+ "Max": 4500,
+ "Selector": "random"
+ },
+ {
+ "Name": "lockers",
+ "Start": 4501,
+ "Max": 5000,
+ "Selector": "random"
+ },
+ {
+ "Name": "tools",
+ "Start": 5001,
+ "Max": 9500,
+ "Selector": "random"
+ },
+ {
+ "Name": "ammo",
+ "Start": 9501,
+ "Max": 23000,
+ "Selector": "random"
+ },
+ {
+ "Name": "kits",
+ "Start": 23001,
+ "Max": 36500,
+ "Selector": "random"
+ },
+ {
+ "Name": "items",
+ "Start": 36501,
+ "Max": 39500,
+ "Selector": "random"
+ },
+ {
+ "Name": "projectiles",
+ "Start": 40100,
+ "Max": 40149,
+ "Selector": "specific"
+ },
+ {
+ "Name": "locker-contents",
+ "Start": 40150,
+ "Max": 40450,
+ "Selector": "specific"
+ },
+ {
+ "Name": "vehicles",
+ "Start": 45001,
+ "Max": 47000,
+ "Selector": "random"
+ },
+ {
+ "Name": "terminals",
+ "Start": 47001,
+ "Max": 48000,
+ "Selector": "random"
+ },
+ {
+ "Name": "deployables",
+ "Start": 48001,
+ "Max": 64000,
+ "Selector": "random"
+ }
+]
diff --git a/src/main/resources/guid-pools/home1.json b/src/main/resources/guid-pools/home1.json
new file mode 100644
index 00000000..cfa7d5c8
--- /dev/null
+++ b/src/main/resources/guid-pools/home1.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 1161,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/home2.json b/src/main/resources/guid-pools/home2.json
new file mode 100644
index 00000000..56527608
--- /dev/null
+++ b/src/main/resources/guid-pools/home2.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 1096,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/home3.json b/src/main/resources/guid-pools/home3.json
new file mode 100644
index 00000000..4cfd0cf0
--- /dev/null
+++ b/src/main/resources/guid-pools/home3.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 1074,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/i1.json b/src/main/resources/guid-pools/i1.json
new file mode 100644
index 00000000..e2dc15bf
--- /dev/null
+++ b/src/main/resources/guid-pools/i1.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 778,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/i2.json b/src/main/resources/guid-pools/i2.json
new file mode 100644
index 00000000..7669a98c
--- /dev/null
+++ b/src/main/resources/guid-pools/i2.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 918,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/i3.json b/src/main/resources/guid-pools/i3.json
new file mode 100644
index 00000000..3650c32a
--- /dev/null
+++ b/src/main/resources/guid-pools/i3.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 699,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/i4.json b/src/main/resources/guid-pools/i4.json
new file mode 100644
index 00000000..fe78825b
--- /dev/null
+++ b/src/main/resources/guid-pools/i4.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 318,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/z1.json b/src/main/resources/guid-pools/z1.json
new file mode 100644
index 00000000..affb1102
--- /dev/null
+++ b/src/main/resources/guid-pools/z1.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 2088,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/z10.json b/src/main/resources/guid-pools/z10.json
new file mode 100644
index 00000000..34dc0424
--- /dev/null
+++ b/src/main/resources/guid-pools/z10.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 2657,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/z2.json b/src/main/resources/guid-pools/z2.json
new file mode 100644
index 00000000..df1f8cf3
--- /dev/null
+++ b/src/main/resources/guid-pools/z2.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 2515,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/z3.json b/src/main/resources/guid-pools/z3.json
new file mode 100644
index 00000000..6a4c3bf5
--- /dev/null
+++ b/src/main/resources/guid-pools/z3.json
@@ -0,0 +1,44 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 3732,
+ "Selector": "specific"
+ },
+ {
+ "Name": "players",
+ "Start": 4001,
+ "Max": 5500,
+ "Selector": "random"
+ },
+ {
+ "Name": "lockers",
+ "Start": 5501,
+ "Max": 6000,
+ "Selector": "random"
+ },
+ {
+ "Name": "tools",
+ "Start": 6001,
+ "Max": 10500,
+ "Selector": "random"
+ },
+ {
+ "Name": "ammo",
+ "Start": 10501,
+ "Max": 24000,
+ "Selector": "random"
+ },
+ {
+ "Name": "kits",
+ "Start": 24001,
+ "Max": 37500,
+ "Selector": "random"
+ },
+ {
+ "Name": "items",
+ "Start": 37501,
+ "Max": 40099,
+ "Selector": "random"
+ }
+]
diff --git a/src/main/resources/guid-pools/z4.json b/src/main/resources/guid-pools/z4.json
new file mode 100644
index 00000000..bf027122
--- /dev/null
+++ b/src/main/resources/guid-pools/z4.json
@@ -0,0 +1,44 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 3076,
+ "Selector": "specific"
+ },
+ {
+ "Name": "players",
+ "Start": 3101,
+ "Max": 4600,
+ "Selector": "random"
+ },
+ {
+ "Name": "lockers",
+ "Start": 4601,
+ "Max": 5100,
+ "Selector": "random"
+ },
+ {
+ "Name": "tools",
+ "Start": 5101,
+ "Max": 9600,
+ "Selector": "random"
+ },
+ {
+ "Name": "ammo",
+ "Start": 9601,
+ "Max": 23100,
+ "Selector": "random"
+ },
+ {
+ "Name": "kits",
+ "Start": 23101,
+ "Max": 36600,
+ "Selector": "random"
+ },
+ {
+ "Name": "items",
+ "Start": 36601,
+ "Max": 39600,
+ "Selector": "random"
+ }
+]
diff --git a/src/main/resources/guid-pools/z5.json b/src/main/resources/guid-pools/z5.json
new file mode 100644
index 00000000..819821d6
--- /dev/null
+++ b/src/main/resources/guid-pools/z5.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 2112,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/z6.json b/src/main/resources/guid-pools/z6.json
new file mode 100644
index 00000000..89266080
--- /dev/null
+++ b/src/main/resources/guid-pools/z6.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 2422,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/z7.json b/src/main/resources/guid-pools/z7.json
new file mode 100644
index 00000000..5d8e90cd
--- /dev/null
+++ b/src/main/resources/guid-pools/z7.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 2816,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/z8.json b/src/main/resources/guid-pools/z8.json
new file mode 100644
index 00000000..2e66c514
--- /dev/null
+++ b/src/main/resources/guid-pools/z8.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 2139,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/resources/guid-pools/z9.json b/src/main/resources/guid-pools/z9.json
new file mode 100644
index 00000000..76f52c09
--- /dev/null
+++ b/src/main/resources/guid-pools/z9.json
@@ -0,0 +1,8 @@
+[
+ {
+ "Name": "environment",
+ "Start": 0,
+ "Max": 2921,
+ "Selector": "specific"
+ }
+]
diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala
index c1f0f817..1bba1e7d 100644
--- a/src/main/scala/net/psforever/actors/session/SessionActor.scala
+++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala
@@ -16,7 +16,7 @@ import net.psforever.objects.definition._
import net.psforever.objects.definition.converter.{CorpseConverter, DestroyedVehicleConverter}
import net.psforever.objects.entity.{SimpleWorldEntity, WorldEntity}
import net.psforever.objects.equipment._
-import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
+import net.psforever.objects.guid._
import net.psforever.objects.inventory.{Container, InventoryItem}
import net.psforever.objects.locker.LockerContainer
import net.psforever.objects.serverobject.affinity.FactionAffinity
@@ -1049,11 +1049,11 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
//!!only dispatched to SessionActor as cleanup if the target deployable was never fully introduced
case Zone.Deployable.IsDismissed(obj: TurretDeployable) =>
- continent.tasks ! GUIDTask.UnregisterDeployableTurret(obj)(continent.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(continent.GUID, obj))
//!!only dispatched to SessionActor as cleanup if the target deployable was never fully introduced
case Zone.Deployable.IsDismissed(obj) =>
- continent.tasks ! GUIDTask.UnregisterObjectTask(obj)(continent.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterObject(continent.GUID, obj))
case ICS.ZonesResponse(zones) =>
zones.foreach { zone =>
@@ -1156,9 +1156,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
player.avatar = avatar
interstellarFerry match {
case Some(vehicle) if vehicle.PassengerInSeat(player).contains(0) =>
- continent.tasks ! RegisterDrivenVehicle(vehicle, player)
+ TaskWorkflow.execute(registerDrivenVehicle(vehicle, player))
case _ =>
- continent.tasks ! RegisterNewAvatar(player)
+ TaskWorkflow.execute(registerNewAvatar(player))
}
case NewPlayerLoaded(tplayer) =>
@@ -1997,13 +1997,13 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case (_, guid) => sendResponse(ObjectDeleteMessage(guid, 0))
}
//functionally delete
- delete.foreach { case (obj, _) => continent.tasks ! GUIDTask.UnregisterEquipment(obj)(continent.GUID) }
+ delete.foreach { case (obj, _) => TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) }
//redraw
if (maxhand) {
- continent.tasks ! HoldNewEquipmentUp(player)(
+ TaskWorkflow.execute(HoldNewEquipmentUp(player)(
Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)),
0
- )
+ ))
}
//draw free hand
player.FreeHand.Equipment match {
@@ -2075,14 +2075,14 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
(old_holsters ++ old_inventory).foreach {
case (obj, guid) =>
sendResponse(ObjectDeleteMessage(guid, 0))
- continent.tasks ! GUIDTask.UnregisterEquipment(obj)(continent.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj))
}
//redraw
if (maxhand) {
- continent.tasks ! HoldNewEquipmentUp(player)(
+ TaskWorkflow.execute(HoldNewEquipmentUp(player)(
Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)),
0
- )
+ ))
}
ApplyPurchaseTimersBeforePackingLoadout(player, player, holsters ++ inventory)
DropLeftovers(player)(drops)
@@ -2166,7 +2166,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
if (Avatar.purchaseCooldowns.contains(item.obj.Definition)) {
avatarActor ! AvatarActor.UpdatePurchaseTime(item.obj.Definition)
}
- continent.tasks ! PutLoadoutEquipmentInInventory(target)(item.obj, item.start)
+ TaskWorkflow.execute(PutLoadoutEquipmentInInventory(target)(item.obj, item.start))
}
}
}
@@ -2574,11 +2574,11 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, false))
case None =>
avatarActor ! AvatarActor.UpdatePurchaseTime(item.Definition)
- continent.tasks ! BuyNewEquipmentPutInInventory(
+ TaskWorkflow.execute(BuyNewEquipmentPutInInventory(
continent.GUID(tplayer.VehicleSeated) match { case Some(v : Vehicle) => v; case _ => player },
tplayer,
msg.terminal_guid
- )(item)
+ )(item))
}
case Terminal.SellEquipment() =>
@@ -2637,7 +2637,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
entry.obj.Faction = tplayer.Faction
vTrunk.InsertQuickly(entry.start, entry.obj)
})
- continent.tasks ! RegisterVehicleFromSpawnPad(vehicle, pad, term)
+ TaskWorkflow.execute(registerVehicleFromSpawnPad(vehicle, pad, term))
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, true))
case _ =>
log.error(
@@ -2921,7 +2921,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
(old_weapons ++ old_inventory).foreach {
case (obj, eguid) =>
sendResponse(ObjectDeleteMessage(eguid, 0))
- continent.tasks ! GUIDTask.UnregisterEquipment(obj)(continent.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj))
}
ApplyPurchaseTimersBeforePackingLoadout(player, vehicle, added_weapons ++ new_inventory)
} else if (accessedContainer.contains(target)) {
@@ -4406,7 +4406,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
continent.id,
AvatarAction.ProjectileExplodes(player.GUID, obj.GUID, obj)
)
- continent.tasks ! UnregisterProjectile(obj)
+ TaskWorkflow.execute(unregisterProjectile(obj))
}
}
@@ -4965,13 +4965,13 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
dObj.Orientation = orient
dObj.Faction = player.Faction
dObj.AssignOwnership(player)
- val tasking: TaskResolver.GiveTask = dObj match {
+ val tasking: TaskBundle = dObj match {
case turret: TurretDeployable =>
- GUIDTask.RegisterDeployableTurret(turret)(continent.GUID)
+ GUIDTask.registerDeployableTurret(continent.GUID, turret)
case _ =>
- GUIDTask.RegisterObjectTask(dObj)(continent.GUID)
+ GUIDTask.registerObject(continent.GUID, dObj)
}
- continent.tasks ! CallBackForTask(tasking, continent.Deployables, Zone.Deployable.BuildByOwner(dObj, player, obj))
+ TaskWorkflow.execute(CallBackForTask(tasking, continent.Deployables, Zone.Deployable.BuildByOwner(dObj, player, obj)))
case Some(obj) =>
log.warn(s"DeployObject: what is $obj, ${player.Name}? It's not a construction tool!")
@@ -5724,35 +5724,22 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* as if that player is only just being introduced.
* `Players` are complex objects that contain a variety of other register-able objects and each of these objects much be handled.
* @param tplayer the avatar `Player`
- * @return a `TaskResolver.GiveTask` message
+ * @return a `TaskBundle` message
*/
- private def RegisterNewAvatar(tplayer: Player): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- new Task() {
+ private def registerNewAvatar(tplayer: Player): TaskBundle = {
+ TaskBundle(
+ new StraightforwardTask() {
private val localPlayer = tplayer
private val localAnnounce = self
- override def Description: String = s"register new player avatar ${localPlayer.Name}"
+ override def description(): String = s"register new player avatar ${localPlayer.Name}"
- override def isComplete: Task.Resolution.Value = {
- if (localPlayer.HasGUID) {
- Task.Resolution.Success
- } else {
- Task.Resolution.Incomplete
- }
- }
-
- def Execute(resolver: ActorRef): Unit = {
- log.trace(s"Player ${localPlayer.Name} is newly registered")
- resolver ! Success(this)
- localAnnounce ! NewPlayerLoaded(localPlayer) //alerts WorldSessionActor
- }
-
- override def onFailure(ex: Throwable): Unit = {
- localAnnounce ! PlayerFailedToLoad(localPlayer) //alerts SessionActor
+ def action(): Future[Any] = {
+ localAnnounce ! NewPlayerLoaded(localPlayer)
+ Future(true)
}
},
- List(GUIDTask.RegisterAvatar(tplayer)(continent.GUID))
+ List(GUIDTask.registerAvatar(continent.GUID, tplayer))
)
}
@@ -5761,35 +5748,22 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* as if that player was already introduced and is just being renewed.
* `Players` are complex objects that contain a variety of other register-able objects and each of these objects much be handled.
* @param tplayer the avatar `Player`
- * @return a `TaskResolver.GiveTask` message
+ * @return a `TaskBundle` message
*/
- private def RegisterAvatar(tplayer: Player): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- new Task() {
+ private def registerAvatar(tplayer: Player): TaskBundle = {
+ TaskBundle(
+ new StraightforwardTask() {
private val localPlayer = tplayer
private val localAnnounce = self
- override def Description: String = s"register player avatar ${localPlayer.Name}"
+ override def description(): String = s"register player avatar ${localPlayer.Name}"
- override def isComplete: Task.Resolution.Value = {
- if (localPlayer.HasGUID) {
- Task.Resolution.Success
- } else {
- Task.Resolution.Incomplete
- }
- }
-
- def Execute(resolver: ActorRef): Unit = {
- log.trace(s"Player $localPlayer is registered")
- resolver ! Success(this)
- localAnnounce ! PlayerLoaded(localPlayer) //alerts WorldSessionActor
- }
-
- override def onFailure(ex: Throwable): Unit = {
- localAnnounce ! PlayerFailedToLoad(localPlayer) //alerts WorldSessionActor
+ def action(): Future[Any] = {
+ localAnnounce ! PlayerLoaded(localPlayer)
+ Future(true)
}
},
- List(GUIDTask.RegisterPlayer(tplayer)(continent.GUID))
+ List(GUIDTask.registerPlayer(continent.GUID, tplayer))
)
}
@@ -5798,29 +5772,21 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* Use this function to renew the globally unique identifiers on a vehicle that has already been added to the scene once.
* @param vehicle the `Vehicle` object
* @see `RegisterVehicleFromSpawnPad`
- * @return a `TaskResolver.GiveTask` message
+ * @return a `TaskBundle` message
*/
- def RegisterVehicle(vehicle: Vehicle): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- new Task() {
+ private def registerVehicle(vehicle: Vehicle): TaskBundle = {
+ TaskBundle(
+ new StraightforwardTask() {
private val localVehicle = vehicle
+ private val localAnnounce = self
- override def Description: String = s"register a ${localVehicle.Definition.Name}"
+ override def description(): String = s"register a ${localVehicle.Definition.Name}"
- override def isComplete: Task.Resolution.Value = {
- if (localVehicle.HasGUID) {
- Task.Resolution.Success
- } else {
- Task.Resolution.Incomplete
- }
- }
-
- def Execute(resolver: ActorRef): Unit = {
- log.trace(s"Vehicle $localVehicle is registered")
- resolver ! Success(this)
+ def action(): Future[Any] = {
+ Future(true)
}
},
- List(GUIDTask.RegisterVehicle(vehicle)(continent.GUID))
+ List(GUIDTask.registerVehicle(continent.GUID, vehicle))
)
}
@@ -5839,43 +5805,34 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* and the user who is the driver (second param) is properly seated
* but the said driver does not know about the vehicle through his usual convention - `VehicleSeated` - yet.
* @see `GlobalDefinitions.droppod`
- * @see `GUIDTask.RegisterObjectTask`
+ * @see `GUIDTask.registerObject`
* @see `interstellarFerry`
* @see `Player.VehicleSeated`
* @see `PlayerLoaded`
- * @see `TaskResolver.GiveTask`
+ * @see `TaskBundle`
* @see `Vehicles.Own`
* @param vehicle the unregistered droppod
* @param tplayer the player using the droppod for instant action;
* should already be the driver of the droppod
- * @return a `TaskResolver.GiveTask` message
+ * @return a `TaskBundle` message
*/
- def RegisterDroppod(vehicle: Vehicle, tplayer: Player): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- new Task() {
+ private def registerDroppod(vehicle: Vehicle, tplayer: Player): TaskBundle = {
+ TaskBundle(
+ new StraightforwardTask() {
private val localDriver = tplayer
private val localVehicle = vehicle
private val localAnnounce = self
- override def Description: String = s"register a ${localVehicle.Definition.Name} manned by ${localDriver.Name}"
+ override def description(): String = s"register a ${localVehicle.Definition.Name} manned by ${localDriver.Name}"
- override def isComplete: Task.Resolution.Value = {
- if (localVehicle.HasGUID) {
- Task.Resolution.Success
- } else {
- Task.Resolution.Incomplete
- }
- }
-
- def Execute(resolver: ActorRef): Unit = {
- log.trace(s"Vehicle $localVehicle is registered")
+ def action(): Future[Any] = {
localDriver.VehicleSeated = localVehicle.GUID
Vehicles.Own(localVehicle, localDriver)
localAnnounce ! PlayerLoaded(localDriver)
- resolver ! Success(this)
+ Future(true)
}
},
- List(GUIDTask.RegisterObjectTask(vehicle)(continent.GUID))
+ List(GUIDTask.registerObject(continent.GUID, vehicle))
)
}
@@ -5887,65 +5844,44 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* the vehicle is being brought into existence from scratch and was never a member of any `Zone`.
* @param obj the `Vehicle` object
* @see `RegisterVehicle`
- * @return a `TaskResolver.GiveTask` message
+ * @return a `TaskBundle` message
*/
- def RegisterVehicleFromSpawnPad(obj: Vehicle, pad: VehicleSpawnPad, terminal: Terminal): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- new Task() {
- private val localVehicle = obj
+ private def registerVehicleFromSpawnPad(vehicle: Vehicle, pad: VehicleSpawnPad, terminal: Terminal): TaskBundle = {
+ TaskBundle(
+ new StraightforwardTask() {
+ private val localVehicle = vehicle
private val localPad = pad.Actor
private val localTerminal = terminal
private val localPlayer = player
- override def Description: String = s"register a ${localVehicle.Definition.Name} for spawn pad"
+ override def description(): String = s"register a ${localVehicle.Definition.Name} for spawn pad"
- override def isComplete: Task.Resolution.Value = {
- if (localVehicle.HasGUID) {
- Task.Resolution.Success
- } else {
- Task.Resolution.Incomplete
- }
- }
-
- def Execute(resolver: ActorRef): Unit = {
+ def action(): Future[Any] = {
localPad ! VehicleSpawnPad.VehicleOrder(localPlayer, localVehicle, localTerminal)
- resolver ! Success(this)
+ Future(true)
}
},
- List(RegisterVehicle(obj))
+ List(registerVehicle(vehicle))
)
}
- def RegisterDrivenVehicle(obj: Vehicle, driver: Player): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- new Task() {
- private val localVehicle = obj
+ private def registerDrivenVehicle(vehicle: Vehicle, driver: Player): TaskBundle = {
+ TaskBundle(
+ new StraightforwardTask() {
+ private val localVehicle = vehicle
private val localDriver = driver
private val localAnnounce = self
- override def Description: String = s"register a ${localVehicle.Definition.Name} driven by ${localDriver.Name}"
+ override def description(): String = s"register a ${localVehicle.Definition.Name} driven by ${localDriver.Name}"
- override def isComplete: Task.Resolution.Value = {
- if (localVehicle.HasGUID && localDriver.HasGUID) {
- Task.Resolution.Success
- } else {
- Task.Resolution.Incomplete
- }
- }
-
- def Execute(resolver: ActorRef): Unit = {
- log.trace(s"${localDriver.Name} 's vehicle ${localVehicle.Definition.Name} is registered")
+ def action(): Future[Any] = {
localDriver.VehicleSeated = localVehicle.GUID
Vehicles.Own(localVehicle, localDriver)
- localAnnounce ! NewPlayerLoaded(localDriver) //alerts WorldSessionActor
- resolver ! Success(this)
- }
-
- override def onFailure(ex: Throwable): Unit = {
- localAnnounce ! PlayerFailedToLoad(localDriver) //alerts SessionActor
+ localAnnounce ! NewPlayerLoaded(localDriver)
+ Future(true)
}
},
- List(GUIDTask.RegisterAvatar(driver)(continent.GUID), GUIDTask.RegisterVehicle(obj)(continent.GUID))
+ List(GUIDTask.registerAvatar(continent.GUID, driver), GUIDTask.registerVehicle(continent.GUID, vehicle))
)
}
@@ -5954,55 +5890,39 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* After the projectile is registered to the curent zone's global unique identifier system,
* all connected clients save for the one that registered it will be informed about the projectile's "creation."
* @param obj the projectile to be registered
- * @return a `TaskResolver.GiveTask` message
+ * @return a `TaskBundle` message
*/
- def RegisterProjectile(obj: Projectile): TaskResolver.GiveTask = {
- val definition = obj.Definition
- TaskResolver.GiveTask(
- new Task() {
+ private def registerProjectile(obj: Projectile): TaskBundle = {
+ TaskBundle(
+ new StraightforwardTask() {
private val globalProjectile = obj
private val localAnnounce = self
- override def Description: String = s"register a ${globalProjectile.profile.Name}"
+ override def description(): String = s"register a ${globalProjectile.profile.Name}"
- override def isComplete: Task.Resolution.Value = {
- if (globalProjectile.HasGUID) {
- Task.Resolution.Success
- } else {
- Task.Resolution.Incomplete
- }
- }
-
- def Execute(resolver: ActorRef): Unit = {
+ def action(): Future[Any] = {
localAnnounce ! LoadedRemoteProjectile(globalProjectile.GUID, Some(globalProjectile))
- resolver ! Success(this)
+ Future(true)
}
},
- List(GUIDTask.RegisterObjectTask(obj)(continent.GUID))
+ List(GUIDTask.registerObject(continent.GUID, obj))
)
}
- def UnregisterDrivenVehicle(obj: Vehicle, driver: Player): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- new Task() {
- private val localVehicle = obj
- private val localDriver = driver
+ private def unregisterDrivenVehicle(vehicle: Vehicle, driver: Player): TaskBundle = {
+ TaskBundle(
+ new StraightforwardTask() {
+ private val localVehicle = vehicle
+ private val localDriver = driver
+ private val localAnnounce = self
- override def Description: String = s"unregister a ${localVehicle.Definition.Name} driven by ${localDriver.Name}"
+ override def description(): String = s"unregister a ${localVehicle.Definition.Name} driven by ${localDriver.Name}"
- override def isComplete: Task.Resolution.Value = {
- if (!localVehicle.HasGUID && !localDriver.HasGUID) {
- Task.Resolution.Success
- } else {
- Task.Resolution.Incomplete
- }
- }
-
- def Execute(resolver: ActorRef): Unit = {
- resolver ! Success(this)
+ def action(): Future[Any] = {
+ Future(true)
}
},
- List(GUIDTask.UnregisterAvatar(driver)(continent.GUID), GUIDTask.UnregisterVehicle(obj)(continent.GUID))
+ List(GUIDTask.unregisterAvatar(continent.GUID, driver), GUIDTask.unregisterVehicle(continent.GUID, vehicle))
)
}
@@ -6011,52 +5931,42 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* After the projectile is unregistered from the curent zone's global unique identifier system,
* all connected clients save for the one that registered it will be informed about the projectile's "destruction."
* @param obj the projectile to be unregistered
- * @return a `TaskResolver.GiveTask` message
+ * @return a `TaskBundle` message
*/
- def UnregisterProjectile(obj: Projectile): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- new Task() {
+ private def unregisterProjectile(obj: Projectile): TaskBundle = {
+ TaskBundle(
+ new StraightforwardTask() {
private val globalProjectile = obj
- private val localAnnounce = continent.AvatarEvents
+ private val localAnnounce = self
private val localMsg = AvatarServiceMessage(continent.id, AvatarAction.ObjectDelete(player.GUID, obj.GUID, 2))
- override def Description: String = s"unregister a ${globalProjectile.profile.Name}"
+ override def description(): String = s"unregister a ${globalProjectile.profile.Name}"
- override def isComplete: Task.Resolution.Value = {
- if (!globalProjectile.HasGUID) {
- Task.Resolution.Success
- } else {
- Task.Resolution.Incomplete
- }
- }
-
- def Execute(resolver: ActorRef): Unit = {
+ def action(): Future[Any] = {
localAnnounce ! localMsg
- resolver ! Success(this)
+ Future(true)
}
},
- List(GUIDTask.UnregisterObjectTask(obj)(continent.GUID))
+ List(GUIDTask.unregisterObject(continent.GUID, obj))
)
}
/**
* If the projectile object is unregistered, register it.
* If the projectile object is already registered, unregister it and then register it again.
- * @see `RegisterProjectile(Projectile)`
- * @see `UnregisterProjectile(Projectile)`
+ * @see `registerProjectile(Projectile)`
+ * @see `unregisterProjectile(Projectile)`
* @param obj the projectile to be registered (a second time?)
- * @return a `TaskResolver.GiveTask` message
+ * @return a `TaskBundle` message
*/
- def ReregisterProjectile(obj: Projectile): TaskResolver.GiveTask = {
- val reg = RegisterProjectile(obj)
+ def reregisterProjectile(obj: Projectile): TaskBundle = {
+ val reg = registerProjectile(obj)
if (obj.HasGUID) {
- TaskResolver.GiveTask(
- reg.task,
- List(
- TaskResolver.GiveTask(
- reg.subs(0).task,
- List(UnregisterProjectile(obj))
- )
+ TaskBundle(
+ reg.mainTask,
+ TaskBundle(
+ reg.subTasks(0).mainTask,
+ unregisterProjectile(obj)
)
)
} else {
@@ -6371,13 +6281,13 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case veh: Vehicle => ModifyAmmunitionInVehicle(veh)
case _ => ModifyAmmunition(obj)
}
- val stowNewFunc: Equipment => TaskResolver.GiveTask = PutNewEquipmentInInventoryOrDrop(obj)
- val stowFunc: Equipment => Future[Any] = PutEquipmentInInventoryOrDrop(obj)
-
+ val stowNewFunc: Equipment => TaskBundle = PutNewEquipmentInInventoryOrDrop(obj)
+ val stowFunc: Equipment => Future[Any] = PutEquipmentInInventoryOrDrop(obj)
+
xs.foreach(item => {
obj.Inventory -= item.start
sendResponse(ObjectDeleteMessage(item.obj.GUID, 0))
- continent.tasks ! GUIDTask.UnregisterObjectTask(item.obj)(continent.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterObject(continent.GUID, item.obj))
})
//box will be the replacement ammo; give it the discovered magazine and load it into the weapon
@@ -6426,7 +6336,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
s"PerformToolAmmoChange: ${player.Name} takes ${originalBoxCapacity - splitReloadAmmo} from a box of $originalBoxCapacity $requestedAmmoType ammo"
)
val boxForInventory = AmmoBox(box.Definition, splitReloadAmmo)
- continent.tasks ! stowNewFunc(boxForInventory)
+ TaskWorkflow.execute(stowNewFunc(boxForInventory))
fullMagazine
}
sendResponse(
@@ -6471,10 +6381,10 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
case Nil | List(_) => ; //done (the former case is technically not possible)
case _ :: toUpdate =>
modifyFunc(previousBox, 0) //update to changed capacity value
- toUpdate.foreach(box => { continent.tasks ! stowNewFunc(box) })
+ toUpdate.foreach(box => { TaskWorkflow.execute(stowNewFunc(box)) })
}
} else {
- continent.tasks ! GUIDTask.UnregisterObjectTask(previousBox)(continent.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterObject(continent.GUID, previousBox))
}
}
}
@@ -6496,32 +6406,6 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
zone.Ground.tell(Zone.Ground.DropItem(item, obj.Position, Vector3.z(obj.Orientation.z)), obj.Actor)
}
- /**
- * Register an `Equipment` item and then drop it on the ground.
- * @see `NormalItemDrop`
- * @param obj a `Container` object that represents where the item will be dropped;
- * curried for callback
- * @param zone the continent in which the item is being dropped;
- * curried for callback
- * @param item the item
- */
- def NewItemDrop(obj: PlanetSideServerObject with Container, zone: Zone)(item: Equipment): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- new Task() {
- private val localItem = item
- private val localFunc: Equipment => Unit = NormalItemDrop(obj, zone)
-
- override def Description: String = s"dropping a new ${localItem.Definition.Name} on the ground"
-
- def Execute(resolver: ActorRef): Unit = {
- localFunc(localItem)
- resolver ! Success(this)
- }
- },
- List(GUIDTask.RegisterEquipment(item)(zone.GUID))
- )
- }
-
/**
* After a weapon has finished shooting, determine if it needs to be sorted in a special way.
* @param tool a weapon
@@ -7274,7 +7158,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* @see `AvatarAction.Release`
* @see `AvatarServiceMessage`
* @see `FriskDeadBody`
- * @see `GUIDTask.UnregisterPlayer`
+ * @see `GUIDTask.unregisterPlayer`
* @see `ObjectDeleteMessage`
* @see `WellLootedDeadBody`
* @see `Zone.Corpse.Add`
@@ -7291,7 +7175,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
zone.Population ! Zone.Population.Release(avatar)
sendResponse(ObjectDeleteMessage(pguid, 0))
zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.ObjectDelete(pguid, pguid, 0))
- zone.tasks ! GUIDTask.UnregisterPlayer(tplayer)(zone.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterPlayer(zone.GUID, tplayer))
}
}
@@ -7788,7 +7672,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
projectile.quality(quality)
} else if (projectile.tool_def.Size == EquipmentSize.Melee) {
//melee
- val quality = player.avatar.implants.flatten.find { entry => entry.definition == GlobalDefinitions.melee_booster } match {
+ val quality = player.avatar.implants.flatten.find { entry => entry.definition.implantType == ImplantType.MeleeBooster } match {
case Some(booster) if booster.active && player.avatar.stamina > 9 =>
avatarActor ! AvatarActor.ConsumeStamina(10)
ProjectileQuality.Modified(25f)
@@ -8210,7 +8094,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
if (!zoneReload && zoneId == continent.id) {
if (player.isBackpack) { // important! test the actor-wide player ref, not the parameter
// respawning from unregistered player
- continent.tasks ! RegisterAvatar(targetPlayer)
+ TaskWorkflow.execute(registerAvatar(targetPlayer))
} else {
// move existing player; this is the one case where the original GUID is retained by the player
self ! PlayerLoaded(targetPlayer)
@@ -8220,15 +8104,15 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
val original = player
if (player.isBackpack) {
session = session.copy(player = targetPlayer)
- taskThenZoneChange(
- GUIDTask.UnregisterObjectTask(original.avatar.locker)(continent.GUID),
+ TaskWorkflow.execute(taskThenZoneChange(
+ GUIDTask.unregisterObject(continent.GUID, original.avatar.locker),
ICS.FindZone(_.id == zoneId, context.self)
- )
+ ))
} else if (player.HasGUID) {
- taskThenZoneChange(
- GUIDTask.UnregisterAvatar(original)(continent.GUID),
+ TaskWorkflow.execute(taskThenZoneChange(
+ GUIDTask.unregisterAvatar(continent.GUID, original),
ICS.FindZone(_.id == zoneId, context.self)
- )
+ ))
} else {
cluster ! ICS.FindZone(_.id == zoneId, context.self)
}
@@ -8317,7 +8201,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
if (!zoneReload && zoneId == continent.id) {
if (vehicle.Definition == GlobalDefinitions.droppod) {
//instant action droppod in the same zone
- continent.tasks ! RegisterDroppod(vehicle, player)
+ TaskWorkflow.execute(registerDroppod(vehicle, player))
} else {
//transferring a vehicle between spawn points (warp gates) in the same zone
self ! PlayerLoaded(player)
@@ -8325,10 +8209,10 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
} else if (vehicle.Definition == GlobalDefinitions.droppod) {
LoadZoneCommonTransferActivity()
player.Continent = zoneId //forward-set the continent id to perform a test
- taskThenZoneChange(
- GUIDTask.UnregisterAvatar(player)(continent.GUID),
+ TaskWorkflow.execute(taskThenZoneChange(
+ GUIDTask.unregisterAvatar(continent.GUID, player),
ICS.FindZone(_.id == zoneId, context.self)
- )
+ ))
} else {
UnaccessContainer(vehicle)
LoadZoneCommonTransferActivity()
@@ -8347,10 +8231,10 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
}
//unregister vehicle and driver whole + GiveWorld
continent.Transport ! Zone.Vehicle.Despawn(vehicle)
- taskThenZoneChange(
- UnregisterDrivenVehicle(vehicle, player),
+ TaskWorkflow.execute(taskThenZoneChange(
+ unregisterDrivenVehicle(vehicle, player),
ICS.FindZone(_.id == zoneId, context.self)
- )
+ ))
}
}
@@ -8369,7 +8253,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
* A reference to the top-level ferrying vehicle's former globally unique identifier has been retained for this purpose.
* This vehicle can be deleted for everyone if no more work can be detected.
*
- * @see `GUIDTask.UnregisterPlayer`
+ * @see `GUIDTask.unregisterPlayer`
* @see `LoadZoneCommonTransferActivity`
* @see `Vehicles.AllGatedOccupantsInSameZone`
* @see `PlayerLoaded`
@@ -8393,10 +8277,10 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
val continentId = continent.id
interstellarFerryTopLevelGUID = None
- taskThenZoneChange(
- GUIDTask.UnregisterAvatar(player)(continent.GUID),
+ TaskWorkflow.execute(taskThenZoneChange(
+ GUIDTask.unregisterAvatar(continent.GUID, player),
ICS.FindZone(_.id == zoneId, context.self)
- )
+ ))
}
}
@@ -8439,20 +8323,24 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
/** Before changing zones, perform the following task (which can be a nesting of subtasks). */
def taskThenZoneChange(
- task: TaskResolver.GiveTask,
- zoneMessage: ICS.FindZone
- ): Unit = {
- continent.tasks ! TaskResolver.GiveTask(
- new Task() {
- override def isComplete: Task.Resolution.Value = task.task.isComplete
+ task: TaskBundle,
+ zoneMessage: ICS.FindZone
+ ): TaskBundle = {
+ TaskBundle(
+ new StraightforwardTask() {
+ val localAvatar = avatar
+ val localZone = continent
+ val localCluster = cluster
- def Execute(resolver: ActorRef): Unit = {
- continent.Population ! Zone.Population.Leave(avatar)
+ override def description() : String = s"doing ${task.description()} before transferring zones"
+
+ def action(): Future[Any] = {
+ continent.Population ! Zone.Population.Leave(localAvatar)
cluster ! zoneMessage
- resolver ! Success(this)
+ Future(true)
}
},
- List(task)
+ task
)
}
@@ -8474,7 +8362,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
avatarActor ! AvatarActor.SetVehicle(None)
}
RemoveBoomerTriggersFromInventory().foreach(obj => {
- continent.tasks ! GUIDTask.UnregisterObjectTask(obj)(continent.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterObject(continent.GUID, obj))
})
Deployables.Disown(continent, avatar, self)
drawDeloyableIcon = RedrawDeployableIcons //important for when SetCurrentAvatar initializes the UI next zone
@@ -8896,7 +8784,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
continent.id,
AvatarAction.ProjectileExplodes(player.GUID, projectile_guid, projectile)
)
- continent.tasks ! UnregisterProjectile(projectile)
+ TaskWorkflow.execute(unregisterProjectile(projectile))
projectiles(local_index) match {
case Some(obj) if !obj.isResolved => obj.Miss()
case _ => ;
@@ -8933,7 +8821,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
tplayer.VehicleSeated = None
zone.Population ! Zone.Population.Release(avatar)
sendResponse(ObjectDeleteMessage(tplayer.GUID, 0))
- zone.tasks ! GUIDTask.UnregisterPlayer(tplayer)(zone.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterPlayer(zone.GUID, tplayer))
}
}
@@ -9144,15 +9032,15 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
log.trace(
s"WeaponFireMessage: ${player.Name}'s ${projectile_info.Name} is a remote projectile"
)
- continent.tasks ! (if (projectile.HasGUID) {
+ if (projectile.HasGUID) {
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.ProjectileExplodes(player.GUID, projectile.GUID, projectile)
)
- ReregisterProjectile(projectile)
+ TaskWorkflow.execute(reregisterProjectile(projectile))
} else {
- RegisterProjectile(projectile)
- })
+ TaskWorkflow.execute(registerProjectile(projectile))
+ }
}
projectilesToCleanUp(projectileIndex) = false
diff --git a/src/main/scala/net/psforever/login/WorldSession.scala b/src/main/scala/net/psforever/login/WorldSession.scala
index a7bd88b2..d8af343e 100644
--- a/src/main/scala/net/psforever/login/WorldSession.scala
+++ b/src/main/scala/net/psforever/login/WorldSession.scala
@@ -4,7 +4,7 @@ import akka.actor.ActorRef
import akka.pattern.{AskTimeoutException, ask}
import akka.util.Timeout
import net.psforever.objects.equipment.{Ammo, Equipment}
-import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
+import net.psforever.objects.guid._
import net.psforever.objects.inventory.{Container, InventoryItem}
import net.psforever.objects.locker.LockerContainer
import net.psforever.objects.serverobject.PlanetSideServerObject
@@ -73,32 +73,29 @@ object WorldSession {
* Equipment will go wherever it fits in containing object, or be dropped if it fits nowhere.
* Item swapping during the placement is not allowed.
* @see `ChangeAmmoMessage`
- * @see `GUIDTask.RegisterEquipment`
+ * @see `GUIDTask.registerEquipment`
* @see `PutEquipmentInInventoryOrDrop`
* @see `Task`
- * @see `TaskResolver.GiveTask`
+ * @see `TaskBundle`
* @param obj the container
* @param item the item being manipulated
- * @return a `TaskResolver` object
+ * @return a `TaskBundle` object
*/
def PutNewEquipmentInInventorySlot(
obj: PlanetSideServerObject with Container
- )(item: Equipment, slot: Int): TaskResolver.GiveTask = {
+ )(item: Equipment, slot: Int): TaskBundle = {
val localZone = obj.Zone
- TaskResolver.GiveTask(
- new Task() {
+ TaskBundle(
+ new StraightforwardTask() {
private val localContainer = obj
private val localItem = item
private val localSlot = slot
- override def isComplete: Task.Resolution.Value = Task.Resolution.Success
-
- def Execute(resolver: ActorRef): Unit = {
+ def action(): Future[Any] = {
PutEquipmentInInventorySlot(localContainer)(localItem, localSlot)
- resolver ! Success(this)
}
},
- List(GUIDTask.RegisterEquipment(item)(localZone.GUID))
+ GUIDTask.registerEquipment(localZone.GUID, item)
)
}
@@ -108,31 +105,28 @@ object WorldSession {
* Equipment will go wherever it fits in containing object, or be dropped if it fits nowhere.
* Item swapping during the placement is not allowed.
* @see `ChangeAmmoMessage`
- * @see `GUIDTask.RegisterEquipment`
+ * @see `GUIDTask.registerEquipment`
* @see `PutEquipmentInInventoryOrDrop`
* @see `Task`
- * @see `TaskResolver.GiveTask`
+ * @see `TaskBundle`
* @param obj the container
* @param item the item being manipulated
- * @return a `TaskResolver` object
+ * @return a `TaskBundle` object
*/
def PutNewEquipmentInInventoryOrDrop(
obj: PlanetSideServerObject with Container
- )(item: Equipment): TaskResolver.GiveTask = {
+ )(item: Equipment): TaskBundle = {
val localZone = obj.Zone
- TaskResolver.GiveTask(
- new Task() {
+ TaskBundle(
+ new StraightforwardTask() {
private val localContainer = obj
private val localItem = item
- override def isComplete: Task.Resolution.Value = Task.Resolution.Success
-
- def Execute(resolver: ActorRef): Unit = {
+ def action(): Future[Any] = {
PutEquipmentInInventoryOrDrop(localContainer)(localItem)
- resolver ! Success(this)
}
},
- List(GUIDTask.RegisterEquipment(item)(localZone.GUID))
+ GUIDTask.registerEquipment(localZone.GUID, item)
)
}
@@ -149,7 +143,7 @@ object WorldSession {
* @see `Containable.PutItemAway`
* @see `Future.onComplete`
* @see `Future.recover`
- * @see `GUIDTask.UnregisterEquipment`
+ * @see `GUIDTask.unregisterEquipment`
* @see `tell`
* @see `Zone.AvatarEvents`
* @param obj the container
@@ -165,7 +159,7 @@ object WorldSession {
val result = ask(localContainer.Actor, Containable.PutItemInSlotOnly(localItem, slot))
result.onComplete {
case Failure(_) | Success(_: Containable.CanNotPutItemInSlot) =>
- localContainer.Zone.tasks ! GUIDTask.UnregisterEquipment(localItem)(localContainer.Zone.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterEquipment(localContainer.Zone.GUID, localItem))
case _ => ;
}
result
@@ -177,43 +171,33 @@ object WorldSession {
* This request will (probably) be coincidental with a number of other such requests based on that loadout
* so items must be rigidly placed else cascade into a chaostic order.
* Item swapping during the placement is not allowed.
- * @see `GUIDTask.RegisterEquipment`
+ * @see `GUIDTask.registerEquipment`
* @see `PutEquipmentInInventorySlot`
* @see `Task`
- * @see `TaskResolver.GiveTask`
+ * @see `TaskBundle`
* @param obj the container
* @param item the item being manipulated
* @param slot where the item will be placed in the container
- * @return a `TaskResolver` object
+ * @return a `TaskBundle` object
*/
def PutLoadoutEquipmentInInventory(
obj: PlanetSideServerObject with Container
- )(item: Equipment, slot: Int): TaskResolver.GiveTask = {
+ )(item: Equipment, slot: Int): TaskBundle = {
val localZone = obj.Zone
- TaskResolver.GiveTask(
- new Task() {
+ TaskBundle(
+ new StraightforwardTask() {
private val localContainer = obj
private val localItem = item
private val localSlot = slot
private val localFunc: (Equipment, Int) => Future[Any] = PutEquipmentInInventorySlot(obj)
- override def Timeout: Long = 1000
+ override def description(): String = s"PutEquipmentInInventorySlot - ${localItem.Definition.Name}"
- override def isComplete: Task.Resolution.Value = {
- if (localItem.HasGUID && localContainer.Find(localItem).nonEmpty)
- Task.Resolution.Success
- else
- Task.Resolution.Incomplete
- }
-
- override def Description: String = s"PutEquipmentInInventorySlot - ${localItem.Definition.Name}"
-
- def Execute(resolver: ActorRef): Unit = {
+ def action(): Future[Any] = {
localFunc(localItem, localSlot)
- resolver ! Success(this)
}
},
- List(GUIDTask.RegisterEquipment(item)(localZone.GUID))
+ GUIDTask.registerEquipment(localZone.GUID, item)
)
}
@@ -227,8 +211,8 @@ object WorldSession {
* @see `ask`
* @see `Containable.CanNotPutItemInSlot`
* @see `Containable.PutItemInSlotOnly`
- * @see `GUIDTask.RegisterEquipment`
- * @see `GUIDTask.UnregisterEquipment`
+ * @see `GUIDTask.registerEquipment`
+ * @see `GUIDTask.unregisterEquipment`
* @see `Future.onComplete`
* @see `PutEquipmentInInventorySlot`
* @see `TerminalMessageOnTimeout`
@@ -236,31 +220,22 @@ object WorldSession {
* @param player na
* @param term na
* @param item the item being manipulated
- * @return a `TaskResolver` object
+ * @return a `TaskBundle` object
*/
def BuyNewEquipmentPutInInventory(
obj: PlanetSideServerObject with Container,
player: Player,
term: PlanetSideGUID
- )(item: Equipment): TaskResolver.GiveTask = {
+ )(item: Equipment): TaskBundle = {
val localZone = obj.Zone
- TaskResolver.GiveTask(
- new Task() {
+ TaskBundle(
+ new StraightforwardTask() {
private val localContainer = obj
private val localItem = item
private val localPlayer = player
private val localTermMsg: Boolean => Unit = TerminalResult(term, localPlayer, TransactionType.Buy)
- override def Timeout: Long = 1000
-
- override def isComplete: Task.Resolution.Value = {
- if (localItem.HasGUID && localContainer.Find(localItem).nonEmpty)
- Task.Resolution.Success
- else
- Task.Resolution.Incomplete
- }
-
- def Execute(resolver: ActorRef): Unit = {
+ def action(): Future[Any] = {
TerminalMessageOnTimeout(
ask(localContainer.Actor, Containable.PutItemAway(localItem)),
localTermMsg
@@ -279,16 +254,16 @@ object WorldSession {
localTermMsg(true)
}
} else {
- localContainer.Zone.tasks ! GUIDTask.UnregisterEquipment(localItem)(localContainer.Zone.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterEquipment(localContainer.Zone.GUID, localItem))
localTermMsg(false)
}
case _ =>
localTermMsg(true)
}
- resolver ! Success(this)
+ Future(true)
}
},
- List(GUIDTask.RegisterEquipment(item)(localZone.GUID))
+ GUIDTask.registerEquipment(localZone.GUID, item)
)
}
@@ -306,44 +281,35 @@ object WorldSession {
* @see `AvatarAction.SendResponse`
* @see `Containable.CanNotPutItemInSlot`
* @see `Containable.PutItemInSlotOnly`
- * @see `GUIDTask.RegisterEquipment`
- * @see `GUIDTask.UnregisterEquipment`
+ * @see `GUIDTask.registerEquipment`
+ * @see `GUIDTask.unregisterEquipment`
* @see `Future.onComplete`
* @see `ObjectHeldMessage`
* @see `Player.DrawnSlot`
* @see `Player.LastDrawnSlot`
* @see `Service.defaultPlayerGUID`
- * @see `TaskResolver.GiveTask`
+ * @see `TaskBundle`
* @see `Zone.AvatarEvents`
* @param player the player whose visible slot will be equipped and drawn
* @param item the item to equip
* @param slot the slot in which the item will be equipped
- * @return a `TaskResolver` object
+ * @return a `TaskBundle` object
*/
- def HoldNewEquipmentUp(player: Player)(item: Equipment, slot: Int): TaskResolver.GiveTask = {
+ def HoldNewEquipmentUp(player: Player)(item: Equipment, slot: Int): TaskBundle = {
if (player.VisibleSlots.contains(slot)) {
val localZone = player.Zone
- TaskResolver.GiveTask(
- new Task() {
+ TaskBundle(
+ new StraightforwardTask() {
private val localPlayer = player
private val localGUID = player.GUID
private val localItem = item
private val localSlot = slot
- override def Timeout: Long = 1000
-
- override def isComplete: Task.Resolution.Value = {
- if (localPlayer.DrawnSlot == localSlot)
- Task.Resolution.Success
- else
- Task.Resolution.Incomplete
- }
-
- def Execute(resolver: ActorRef): Unit = {
+ def action(): Future[Any] = {
ask(localPlayer.Actor, Containable.PutItemInSlotOnly(localItem, localSlot))
.onComplete {
case Failure(_) | Success(_: Containable.CanNotPutItemInSlot) =>
- localPlayer.Zone.tasks ! GUIDTask.UnregisterEquipment(localItem)(localZone.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterEquipment(localZone.GUID, localItem))
case _ =>
if (localPlayer.DrawnSlot != Player.HandsDownSlot) {
localPlayer.DrawnSlot = Player.HandsDownSlot
@@ -365,10 +331,10 @@ object WorldSession {
AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectHeldMessage(localGUID, localSlot, false))
)
}
- resolver ! Success(this)
+ Future(this)
}
},
- List(GUIDTask.RegisterEquipment(item)(localZone.GUID))
+ GUIDTask.registerEquipment(localZone.GUID, item)
)
} else {
//TODO log.error
@@ -460,7 +426,7 @@ object WorldSession {
* @see `Containable.RemoveItemFromSlot`
* @see `Future.onComplete`
* @see `Future.recover`
- * @see `GUIDTask.UnregisterEquipment`
+ * @see `GUIDTask.unregisterEquipment`
* @see `Zone.AvatarEvents`
* @param obj the container to search
* @param item the item to find and remove from the container
@@ -474,7 +440,7 @@ object WorldSession {
val result = ask(localContainer.Actor, Containable.RemoveItemFromSlot(localItem))
result.onComplete {
case Success(Containable.ItemFromSlot(_, Some(_), Some(_))) =>
- localContainer.Zone.tasks ! GUIDTask.UnregisterEquipment(localItem)(localContainer.Zone.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterEquipment(localContainer.Zone.GUID, localItem))
case _ =>
}
result
@@ -492,7 +458,7 @@ object WorldSession {
* @see `Containable.RemoveItemFromSlot`
* @see `Future.onComplete`
* @see `Future.recover`
- * @see `GUIDTask.UnregisterEquipment`
+ * @see `GUIDTask.unregisterEquipment`
* @see `RemoveOldEquipmentFromInventory`
* @see `TerminalMessageOnTimeout`
* @see `TerminalResult`
@@ -517,7 +483,7 @@ object WorldSession {
)
result.onComplete {
case Success(Containable.ItemFromSlot(_, Some(item), Some(_))) =>
- localContainer.Zone.tasks ! GUIDTask.UnregisterEquipment(item)(localContainer.Zone.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterEquipment(localContainer.Zone.GUID, item))
localTermMsg(true)
case _ =>
localTermMsg(false)
@@ -538,7 +504,7 @@ object WorldSession {
* @see `LockerContainer`
* @see `RemoveEquipmentFromLockerContainer`
* @see `StowEquipmentInLockerContainer`
- * @see `TaskResolver`
+ * @see `TaskBundle`
* @param toChannel broadcast channel name for a manual packet callback
* @param source the container in which the item is to be removed
* @param destination the container into which the item is to be placed
@@ -573,14 +539,14 @@ object WorldSession {
* @see `Container`
* @see `Equipment`
* @see `GridInventory.CheckCollisionsVar`
- * @see `GUIDTask.RegisterEquipment`
- * @see `GUIDTask.UnregisterEquipment`
+ * @see `GUIDTask.registerEquipment`
+ * @see `GUIDTask.unregisterEquipment`
* @see `IdentifiableEntity.Invalidate`
* @see `LockerContainer`
* @see `Service`
* @see `Task`
- * @see `TaskResolver`
- * @see `TaskResolver.GiveTask`
+ * @see `TaskBundle`
+ * @see `TaskBundle`
* @see `Zone.AvatarEvents`
* @param toChannel broadcast channel name for a manual packet callback
* @param source the container in which the item is to be removed
@@ -610,7 +576,7 @@ object WorldSession {
(false, None)
}
if (performSwap) {
- def moveItemTaskFunc(toSlot: Int): Task = new Task() {
+ def moveItemTaskFunc(toSlot: Int): Task = new StraightforwardTask() {
val localGUID = swapItemGUID //the swap item's original GUID, if any swap item
val localChannel = toChannel
val localSource = source
@@ -621,21 +587,13 @@ object WorldSession {
val localMoveOnComplete: Try[Any] => Unit = {
case Success(Containable.ItemPutInSlot(_, _, _, Some(swapItem))) =>
//swapItem is not registered right now, we can not drop the item without re-registering it
- localSource.Zone.tasks ! PutNewEquipmentInInventorySlot(localSource)(swapItem, localSrcSlot)
+ TaskWorkflow.execute(PutNewEquipmentInInventorySlot(localSource)(swapItem, localSrcSlot))
case _ => ;
}
- override def Description: String = s"unregistering $localItem before stowing in $localDestination"
+ override def description(): String = s"unregistering $localItem before stowing in $localDestination"
- override def isComplete: Task.Resolution.Value = {
- if (localItem.HasGUID && localDestination.Find(localItem).contains(localDestSlot)) {
- Task.Resolution.Success
- } else {
- Task.Resolution.Incomplete
- }
- }
-
- def Execute(resolver: ActorRef): Unit = {
+ def action(): Future[Any] = {
localGUID match {
case Some(guid) =>
//see LockerContainerControl.RemoveItemFromSlotCallback
@@ -647,15 +605,15 @@ object WorldSession {
}
val moveResult = ask(localDestination.Actor, Containable.PutItemInSlotOrAway(localItem, Some(localDestSlot)))
moveResult.onComplete(localMoveOnComplete)
- resolver ! Success(this)
+ moveResult
}
}
val resultOnComplete: Try[Any] => Unit = {
case Success(Containable.ItemFromSlot(fromSource, Some(itemToMove), Some(fromSlot))) =>
- destination.Zone.tasks ! TaskResolver.GiveTask(
+ TaskWorkflow.execute(TaskBundle(
moveItemTaskFunc(fromSlot),
- List(GUIDTask.UnregisterEquipment(itemToMove)(fromSource.Zone.GUID))
- )
+ GUIDTask.unregisterEquipment(fromSource.Zone.GUID, itemToMove)
+ ))
case _ => ;
}
val result = ask(source.Actor, Containable.RemoveItemFromSlot(item))
@@ -673,14 +631,14 @@ object WorldSession {
* @see `Container`
* @see `Equipment`
* @see `GridInventory.CheckCollisionsVar`
- * @see `GUIDTask.RegisterEquipment`
- * @see `GUIDTask.UnregisterEquipment`
+ * @see `GUIDTask.registerEquipment`
+ * @see `GUIDTask.unregisterEquipment`
* @see `IdentifiableEntity.Invalidate`
* @see `LockerContainer`
* @see `Service`
* @see `Task`
- * @see `TaskResolver`
- * @see `TaskResolver.GiveTask`
+ * @see `TaskBundle`
+ * @see `TaskBundle`
* @see `Zone.AvatarEvents`
* @param toChannel broadcast channel name for a manual packet callback
* @param source the container in which the item is to be removed
@@ -695,8 +653,8 @@ object WorldSession {
item: Equipment,
dest: Int
): Unit = {
- destination.Zone.tasks ! TaskResolver.GiveTask(
- new Task() {
+ TaskWorkflow.execute(TaskBundle(
+ new StraightforwardTask() {
val localGUID = item.GUID //original GUID
val localChannel = toChannel
val localSource = source
@@ -717,25 +675,16 @@ object WorldSession {
case _ => ;
}
- override def Description: String = s"registering $localItem in ${localDestination.Zone.id} before removing from $localSource"
+ override def description(): String = s"registering $localItem in ${localDestination.Zone.id} before removing from $localSource"
- override def isComplete: Task.Resolution.Value = {
- if (localItem.HasGUID && localDestination.Find(localItem).isEmpty) {
- Task.Resolution.Success
- } else {
- Task.Resolution.Incomplete
- }
- }
-
- def Execute(resolver: ActorRef): Unit = {
+ def action(): Future[Any] = {
val zone = localSource.Zone
//see LockerContainerControl.RemoveItemFromSlotCallback
zone.AvatarEvents ! AvatarServiceMessage(localChannel, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, localGUID))
- localSource.Actor ! Containable.MoveItem(localDestination, localItem, localSlot)
- resolver ! Success(this)
+ ask(localSource.Actor, Containable.MoveItem(localDestination, localItem, localSlot))
}
},
- List(GUIDTask.RegisterEquipment(item)(destination.Zone.GUID))
+ GUIDTask.registerEquipment(destination.Zone.GUID, item))
)
}
@@ -884,21 +833,21 @@ object WorldSession {
}
}
- def CallBackForTask(task: TaskResolver.GiveTask, sendTo: ActorRef, pass: Any): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- new Task() {
- private val localDesc = task.task.Description
+ def CallBackForTask(task: TaskBundle, sendTo: ActorRef, pass: Any): TaskBundle = {
+ TaskBundle(
+ new StraightforwardTask() {
+ private val localDesc = task.description()
private val destination = sendTo
private val passMsg = pass
- override def Description: String = s"callback for tasking $localDesc"
+ override def description(): String = s"callback for tasking $localDesc"
- def Execute(resolver: ActorRef): Unit = {
+ def action() : Future[Any] = {
destination ! passMsg
- resolver ! Success(this)
+ Future(this)
}
},
- List(task)
+ task
)
}
}
diff --git a/src/main/scala/net/psforever/objects/BoomerDeployable.scala b/src/main/scala/net/psforever/objects/BoomerDeployable.scala
index e40a1993..e18d5c24 100644
--- a/src/main/scala/net/psforever/objects/BoomerDeployable.scala
+++ b/src/main/scala/net/psforever/objects/BoomerDeployable.scala
@@ -4,7 +4,7 @@ package net.psforever.objects
import akka.actor.{ActorContext, Props}
import net.psforever.objects.ballistics.{PlayerSource, SourceEntry}
import net.psforever.objects.ce.{Deployable, DeployedItem}
-import net.psforever.objects.guid.GUIDTask
+import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.vital.etc.TriggerUsedReason
import net.psforever.objects.vital.interaction.DamageInteraction
@@ -99,7 +99,7 @@ class BoomerDeployableControl(mine: BoomerDeployable)
zone.id,
AvatarAction.ObjectDelete(Service.defaultPlayerGUID, trigger.GUID)
)
- zone.tasks ! GUIDTask.UnregisterObjectTask(trigger)(zone.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterObject(zone.GUID, trigger))
case None => ;
}
}
diff --git a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index c67beb24..f98bef47 100644
--- a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -9,6 +9,7 @@ import net.psforever.objects.definition.converter._
import net.psforever.objects.equipment._
import net.psforever.objects.geometry.GeometryForm
import net.psforever.objects.inventory.InventoryTile
+import net.psforever.objects.locker.LockerContainerDefinition
import net.psforever.objects.serverobject.aura.Aura
import net.psforever.objects.serverobject.doors.DoorDefinition
import net.psforever.objects.serverobject.generator.GeneratorDefinition
@@ -426,11 +427,7 @@ object GlobalDefinitions {
Equipment (locker_container, kits, ammunition, weapons)
*/
import net.psforever.packet.game.objectcreate.ObjectClass
- val locker_container = new EquipmentDefinition(456) {
- Name = "locker_container"
- Size = EquipmentSize.Inventory
- Packet = new LockerContainerConverter()
- }
+ val locker_container = new LockerContainerDefinition()
val medkit = KitDefinition(Kits.medkit)
diff --git a/src/main/scala/net/psforever/objects/Players.scala b/src/main/scala/net/psforever/objects/Players.scala
index 9317937a..c577822e 100644
--- a/src/main/scala/net/psforever/objects/Players.scala
+++ b/src/main/scala/net/psforever/objects/Players.scala
@@ -7,7 +7,7 @@ import net.psforever.objects.avatar.PlayerControl
import net.psforever.objects.ce.Deployable
import net.psforever.objects.definition.ExoSuitDefinition
import net.psforever.objects.equipment.EquipmentSlot
-import net.psforever.objects.guid.GUIDTask
+import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects.inventory.InventoryItem
import net.psforever.objects.loadouts.InfantryLoadout
import net.psforever.objects.zones.Zone
@@ -341,7 +341,7 @@ object Players {
def commonDestroyConstructionItem(player: Player, tool: ConstructionItem, index: Int): Unit = {
val zone = player.Zone
if (safelyRemoveConstructionItemFromSlot(player, tool, index, "CommonDestroyConstructionItem")) {
- zone.tasks ! GUIDTask.UnregisterEquipment(tool)(zone.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterEquipment(zone.GUID, tool))
}
}
diff --git a/src/main/scala/net/psforever/objects/TurretDeployable.scala b/src/main/scala/net/psforever/objects/TurretDeployable.scala
index b2616885..c363e625 100644
--- a/src/main/scala/net/psforever/objects/TurretDeployable.scala
+++ b/src/main/scala/net/psforever/objects/TurretDeployable.scala
@@ -6,7 +6,7 @@ import net.psforever.objects.ce.{Deployable, DeployableBehavior, DeployedItem}
import net.psforever.objects.definition.DeployableDefinition
import net.psforever.objects.definition.converter.SmallTurretConverter
import net.psforever.objects.equipment.{JammableMountedWeapons, JammableUnit}
-import net.psforever.objects.guid.GUIDTask
+import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
import net.psforever.objects.serverobject.damage.Damageable.Target
@@ -132,6 +132,6 @@ class TurretControl(turret: TurretDeployable)
override def unregisterDeployable(obj: Deployable): Unit = {
val zone = obj.Zone
- zone.tasks ! GUIDTask.UnregisterDeployableTurret(turret)(zone.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(zone.GUID, turret))
}
}
diff --git a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
index 24f420cd..4f653bf7 100644
--- a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
+++ b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala
@@ -9,7 +9,7 @@ import net.psforever.objects.ballistics.PlayerSource
import net.psforever.objects.ce.Deployable
import net.psforever.objects.definition.DeployAnimation
import net.psforever.objects.equipment._
-import net.psforever.objects.guid.GUIDTask
+import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
import net.psforever.objects.loadouts.Loadout
import net.psforever.objects.serverobject.aura.{Aura, AuraEffectBehavior}
@@ -284,7 +284,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
val zone = player.Zone
avatarActor ! AvatarActor.UpdateUseTime(kdef)
player.Slot(slot).Equipment = None //remove from slot immediately; must exist on client for now
- zone.tasks ! GUIDTask.UnregisterEquipment(kit)(zone.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterEquipment(zone.GUID, kit))
zone.AvatarEvents ! AvatarServiceMessage(
zone.id,
AvatarAction.PlanetsideAttributeToAll(player.GUID, attribute, value)
@@ -482,17 +482,17 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
obj.Trigger = trigger
//TODO sufficiently delete the tool
zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.ObjectDelete(player.GUID, tool.GUID))
- zone.tasks ! GUIDTask.UnregisterEquipment(tool)(zone.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterEquipment(zone.GUID, tool))
player.Find(tool) match {
case Some(index) if player.VisibleSlots.contains(index) =>
player.Slot(index).Equipment = None
- zone.tasks ! HoldNewEquipmentUp(player)(trigger, index)
+ TaskWorkflow.execute(HoldNewEquipmentUp(player)(trigger, index))
case Some(index) =>
player.Slot(index).Equipment = None
- zone.tasks ! PutNewEquipmentInInventoryOrDrop(player)(trigger)
+ TaskWorkflow.execute(PutNewEquipmentInInventoryOrDrop(player)(trigger))
case None =>
//don't know where boomer trigger "should" go
- zone.tasks ! PutNewEquipmentInInventoryOrDrop(player)(trigger)
+ TaskWorkflow.execute(PutNewEquipmentInInventoryOrDrop(player)(trigger))
}
Players.buildCooldownReset(zone, player.Name, obj)
case _ => ;
diff --git a/src/main/scala/net/psforever/objects/ce/DeployableBehavior.scala b/src/main/scala/net/psforever/objects/ce/DeployableBehavior.scala
index 4d6e512d..dba3e149 100644
--- a/src/main/scala/net/psforever/objects/ce/DeployableBehavior.scala
+++ b/src/main/scala/net/psforever/objects/ce/DeployableBehavior.scala
@@ -2,7 +2,7 @@
package net.psforever.objects.ce
import akka.actor.{Actor, ActorRef, Cancellable}
-import net.psforever.objects.guid.GUIDTask
+import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects._
import net.psforever.objects.definition.DeployAnimation
import net.psforever.objects.zones.Zone
@@ -280,7 +280,7 @@ trait DeployableBehavior {
*/
def unregisterDeployable(obj: Deployable): Unit = {
val zone = obj.Zone
- zone.tasks ! GUIDTask.UnregisterObjectTask(obj)(zone.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterObject(zone.GUID, obj))
}
/**
diff --git a/src/main/scala/net/psforever/objects/definition/AmmoBoxDefinition.scala b/src/main/scala/net/psforever/objects/definition/AmmoBoxDefinition.scala
index 9be5296c..76ba00ba 100644
--- a/src/main/scala/net/psforever/objects/definition/AmmoBoxDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/AmmoBoxDefinition.scala
@@ -2,17 +2,17 @@
package net.psforever.objects.definition
import net.psforever.objects.definition.converter.AmmoBoxConverter
-import net.psforever.objects.equipment.Ammo
+import net.psforever.objects.equipment.{Ammo, EquipmentSize}
class AmmoBoxDefinition(objectId: Int) extends EquipmentDefinition(objectId) {
- import net.psforever.objects.equipment.EquipmentSize
+ Name = "ammo_box"
+ Size = EquipmentSize.Inventory
+ Packet = AmmoBoxDefinition.converter
private val ammoType: Ammo.Value = Ammo(objectId) //let throw NoSuchElementException
private var capacity: Int = 1
var repairAmount: Float = 0
+ registerAs = "ammo"
- Name = "ammo box"
- Size = EquipmentSize.Inventory
- Packet = AmmoBoxDefinition.converter
def AmmoType: Ammo.Value = ammoType
diff --git a/src/main/scala/net/psforever/objects/definition/AvatarDefinition.scala b/src/main/scala/net/psforever/objects/definition/AvatarDefinition.scala
index f99de884..91b6ae93 100644
--- a/src/main/scala/net/psforever/objects/definition/AvatarDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/AvatarDefinition.scala
@@ -14,6 +14,7 @@ class AvatarDefinition(objectId: Int) extends ObjectDefinition(objectId) with Vi
Avatars(objectId) //let throw NoSuchElementException
Packet = AvatarDefinition.converter
Geometry = GeometryForm.representPlayerByCylinder(radius = 1.6f)
+ registerAs = "players"
}
object AvatarDefinition {
diff --git a/src/main/scala/net/psforever/objects/definition/ConstructionItemDefinition.scala b/src/main/scala/net/psforever/objects/definition/ConstructionItemDefinition.scala
index 77ba0f78..e46208ce 100644
--- a/src/main/scala/net/psforever/objects/definition/ConstructionItemDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/ConstructionItemDefinition.scala
@@ -12,6 +12,7 @@ class ConstructionItemDefinition(objectId: Int) extends EquipmentDefinition(obje
CItem(objectId) //let throw NoSuchElementException
private val modes: ListBuffer[ConstructionFireMode] = ListBuffer()
Packet = new ACEConverter
+ registerAs = "items"
def Modes: ListBuffer[ConstructionFireMode] = modes
}
diff --git a/src/main/scala/net/psforever/objects/definition/DeployableDefinition.scala b/src/main/scala/net/psforever/objects/definition/DeployableDefinition.scala
index 78ffb641..8600bdff 100644
--- a/src/main/scala/net/psforever/objects/definition/DeployableDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/DeployableDefinition.scala
@@ -59,6 +59,7 @@ abstract class DeployableDefinition(objectId: Int)
DamageUsing = DamageCalculations.AgainstVehicle
ResistUsing = NoResistanceSelection
Packet = new SmallDeployableConverter
+ registerAs = "deployables"
def Item: DeployedItem.Value = item
}
diff --git a/src/main/scala/net/psforever/objects/definition/KitDefinition.scala b/src/main/scala/net/psforever/objects/definition/KitDefinition.scala
index 5653fadf..3aa4ae07 100644
--- a/src/main/scala/net/psforever/objects/definition/KitDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/KitDefinition.scala
@@ -16,6 +16,7 @@ class KitDefinition(objectId: Int) extends EquipmentDefinition(objectId) {
Tile = InventoryTile.Tile42
Name = "kit"
Packet = KitDefinition.converter
+ registerAs = "kits"
}
object KitDefinition {
diff --git a/src/main/scala/net/psforever/objects/definition/ObjectDefinition.scala b/src/main/scala/net/psforever/objects/definition/ObjectDefinition.scala
index b4cc26d3..c42e53ab 100644
--- a/src/main/scala/net/psforever/objects/definition/ObjectDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/ObjectDefinition.scala
@@ -21,10 +21,11 @@ import net.psforever.types.OxygenState
* @param objectId the object's identifier number
*/
abstract class ObjectDefinition(private val objectId: Int) extends BasicDefinition {
+ var registerAs: String = "generic"
/** a data converter for this type of object */
protected var packet: PacketConverter = new ObjectCreateConverter[PlanetSideGameObject]() {}
- Name = "object definition"
+ Name = "object_definition"
/**
* Get the conversion object.
diff --git a/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala b/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
index 75f916ca..cb72bf8d 100644
--- a/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/ProjectileDefinition.scala
@@ -60,6 +60,7 @@ class ProjectileDefinition(objectId: Int)
private var finalVelocity: Float = 0f
Name = "projectile"
Modifiers = DistanceDegrade
+ registerAs = "projectiles"
def ProjectileType: Projectiles.Value = projectileType
diff --git a/src/main/scala/net/psforever/objects/definition/SimpleItemDefinition.scala b/src/main/scala/net/psforever/objects/definition/SimpleItemDefinition.scala
index 6633469c..173dda92 100644
--- a/src/main/scala/net/psforever/objects/definition/SimpleItemDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/SimpleItemDefinition.scala
@@ -6,8 +6,9 @@ import net.psforever.objects.equipment.SItem
class SimpleItemDefinition(objectId: Int) extends EquipmentDefinition(objectId) {
import net.psforever.objects.equipment.EquipmentSize
SItem(objectId) //let throw NoSuchElementException
- Name = "tool"
+ Name = "simple_item"
Size = EquipmentSize.Pistol //all items
+ registerAs = "items"
}
object SimpleItemDefinition {
diff --git a/src/main/scala/net/psforever/objects/definition/ToolDefinition.scala b/src/main/scala/net/psforever/objects/definition/ToolDefinition.scala
index 08e4205e..b5513bd3 100644
--- a/src/main/scala/net/psforever/objects/definition/ToolDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/ToolDefinition.scala
@@ -18,6 +18,7 @@ class ToolDefinition(objectId: Int) extends EquipmentDefinition(objectId) {
private var defaultFireModeIndex: Option[Int] = None
Name = "tool"
Packet = ToolDefinition.converter
+ registerAs = "tools"
def AmmoTypes: mutable.ListBuffer[AmmoBoxDefinition] = ammoTypes
diff --git a/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala b/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
index 790b2307..feddf851 100644
--- a/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
+++ b/src/main/scala/net/psforever/objects/definition/VehicleDefinition.scala
@@ -61,6 +61,7 @@ class VehicleDefinition(objectId: Int)
Model = VehicleResolutions.calculate
RepairDistance = 10
RepairRestoresAt = 1
+ registerAs = "vehicles"
def MaxShields: Int = maxShields
diff --git a/src/main/scala/net/psforever/objects/guid/AvailabilityPolicy.scala b/src/main/scala/net/psforever/objects/guid/AvailabilityPolicy.scala
deleted file mode 100644
index 6b50a026..00000000
--- a/src/main/scala/net/psforever/objects/guid/AvailabilityPolicy.scala
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.guid
-
-/**
- * The availability of individual GUIDs is maintained by the given policy.
- */
-object AvailabilityPolicy extends Enumeration {
- type Type = Value
-
- /**
- * An `AVAILABLE` GUID is ready and waiting to be `LEASED` for use.
- * A `LEASED` GUID has been issued and is currently being used.
- * A `RESTRICTED` GUID can never be freed. It is allowed, however, to be assigned once as if it were `LEASED`.
- */
- val Available, Leased, Restricted = Value
-}
diff --git a/src/main/scala/net/psforever/objects/guid/GUIDTask.scala b/src/main/scala/net/psforever/objects/guid/GUIDTask.scala
index b8143a91..cd86cfaf 100644
--- a/src/main/scala/net/psforever/objects/guid/GUIDTask.scala
+++ b/src/main/scala/net/psforever/objects/guid/GUIDTask.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.guid
-import akka.actor.ActorRef
+import akka.util.Timeout
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
import net.psforever.objects._
@@ -10,6 +10,8 @@ import net.psforever.objects.locker.{LockerContainer, LockerEquipment}
import net.psforever.objects.serverobject.turret.WeaponTurret
import scala.annotation.tailrec
+import scala.concurrent.duration._
+import scala.concurrent.Future
/**
* The basic compiled tasks for assigning (registering) and revoking (unregistering) globally unique identifiers.
@@ -22,43 +24,63 @@ import scala.annotation.tailrec
* It will get passed from the more complicated functions down into the less complicated functions,
* until it has found the basic number assignment functionality.
*
- * All functions produce a `TaskResolver.GiveTask` container object
- * or a list of `TaskResolver.GiveTask` container objects that is expected to be used by a `TaskResolver` `Actor`.
+ * All functions produce a `TaskBundle` container object
+ * or a list of `TaskBundle` container objects that is expected to be used by a `TaskBundle` container.
* These "task containers" can also be unpackaged into their component tasks, sorted into other containers,
* and combined with other tasks to enact more complicated sequences of operations.
* Almost all tasks have an explicit registering and an unregistering activity defined for it.
*/
+
object GUIDTask {
+ private implicit val timeout = Timeout(2.seconds)
+
+ //registration tasking
+ protected case class RegisterObjectTask(
+ guid: UniqueNumberOps,
+ obj: IdentifiableEntity,
+ pool: String
+ ) extends Task {
+ def action(): Future[Any] = {
+ guid.Register(obj, pool)
+ }
+
+ def undo(): Unit = {
+ guid.Unregister(obj)
+ }
+
+ def isSuccessful() : Boolean = obj.HasGUID
+
+ override def description(): String = s"register $obj to $pool"
+ }
+
+ def RegisterObjectTask(guid: UniqueNumberOps, obj: IdentifiableEntity): RegisterObjectTask = obj match {
+ case o: PlanetSideGameObject => RegisterObjectTask(guid, o)
+ case _ => RegisterObjectTask(guid, obj, "generic")
+ }
+
+ def RegisterObjectTask(guid: UniqueNumberOps, obj: PlanetSideGameObject): RegisterObjectTask =
+ RegisterObjectTask(guid, obj, obj.Definition.registerAs)
/**
- * Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers.
- *
+ * Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers.
* Regardless of the complexity of the object provided to this function, only the current depth will be assigned a GUID.
* This is the most basic operation that all objects that can be assigned a GUID must perform.
* @param obj the object being registered
* @param guid implicit reference to a unique number system
- * @return a `TaskResolver.GiveTask` message
+ * @return a `TaskBundle` message
*/
- def RegisterObjectTask(obj: IdentifiableEntity)(implicit guid: ActorRef): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(new Task() {
- private val localObject = obj
- private val localAccessor = guid
+ def registerObject(guid: UniqueNumberOps, obj: IdentifiableEntity): TaskBundle =
+ TaskBundle(RegisterObjectTask(guid, obj, "generic"))
- override def Description: String = s"register $localObject"
-
- override def isComplete: Task.Resolution.Value =
- if (localObject.HasGUID) {
- Task.Resolution.Success
- } else {
- Task.Resolution.Incomplete
- }
-
- def Execute(resolver: ActorRef): Unit = {
- import net.psforever.objects.guid.actor.Register
- localAccessor ! Register(localObject, "dynamic", resolver) //TODO pool should not be hardcoded
- }
- })
- }
+ /**
+ * Construct tasking that registers an object with a globally unique identifier selected from a specific pool of numbers.
+ * Regardless of the complexity of the object provided to this function, only the current depth will be assigned a GUID.
+ * @param obj the object being registered
+ * @param guid implicit reference to a unique number system
+ * @return a `TaskBundle` message
+ */
+ def registerObject(guid: UniqueNumberOps, obj: PlanetSideGameObject): TaskBundle =
+ TaskBundle(RegisterObjectTask(guid, obj, obj.Definition.registerAs))
/**
* Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers, as a `Tool`.
@@ -74,41 +96,14 @@ object GUIDTask {
* else use a more general function to differentiate between simple and complex objects.
* @param obj the `Tool` object being registered
* @param guid implicit reference to a unique number system
- * @see `GUIDTask.RegisterEquipment`
- * @return a `TaskResolver.GiveTask` message
+ * @see `GUIDTask.registerEquipment`
+ * @return a `TaskBundle` message
*/
- def RegisterTool(obj: Tool)(implicit guid: ActorRef): TaskResolver.GiveTask = {
- val ammoTasks: List[TaskResolver.GiveTask] =
- (0 until obj.MaxAmmoSlot).map(ammoIndex => RegisterObjectTask(obj.AmmoSlots(ammoIndex).Box)).toList
- TaskResolver.GiveTask(RegisterObjectTask(obj).task, ammoTasks)
- }
-
- /**
- * Construct tasking that registers a `LockerContainer` object
- * with a globally unique identifier selected from a pool of numbers.
- * @param obj the object being registered
- * @param guid implicit reference to a unique number system
- * @see `GUIDTask.UnregisterLocker`
- * @return a `TaskResolver.GiveTask` message
- */
- def RegisterLocker(obj: LockerContainer)(implicit guid: ActorRef): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(RegisterObjectTask(obj).task, RegisterInventory(obj))
- }
- def RegisterLocker(obj: LockerEquipment)(implicit guid: ActorRef): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(RegisterObjectTask(obj).task, RegisterInventory(obj))
- }
-
- /**
- * Construct tasking that registers the objects that are within the given container's inventory
- * with a globally unique identifier selected from a pool of numbers for each object.
- * @param container the storage unit in which objects can be found
- * @param guid implicit reference to a unique number system
- * @see `GUID.UnregisterInventory`
- * `Container`
- * @return a list of `TaskResolver.GiveTask` messages
- */
- def RegisterInventory(container: Container)(implicit guid: ActorRef): List[TaskResolver.GiveTask] = {
- container.Inventory.Items.map(entry => { RegisterEquipment(entry.obj) })
+ def registerTool(guid: UniqueNumberOps, obj: Tool): TaskBundle = {
+ TaskBundle(
+ RegisterObjectTask(guid, obj),
+ (0 until obj.MaxAmmoSlot).map(ammoIndex => registerObject(guid, obj.AmmoSlots(ammoIndex).Box))
+ )
}
/**
@@ -125,17 +120,52 @@ object GUIDTask {
* The type will be sorted and the object will be handled according to its complexity level.
* @param obj the `Equipment` object being registered
* @param guid implicit reference to a unique number system
- * @return a `TaskResolver.GiveTask` message
+ * @return a `TaskBundle` message
*/
- def RegisterEquipment(obj: Equipment)(implicit guid: ActorRef): TaskResolver.GiveTask = {
+ def registerEquipment(guid: UniqueNumberOps, obj: Equipment): TaskBundle = {
obj match {
- case tool: Tool =>
- RegisterTool(tool)
- case _ =>
- RegisterObjectTask(obj)
+ case tool: Tool => registerTool(guid, tool)
+ case _ => registerObject(guid, obj)
}
}
+ /**
+ * Construct tasking that registers the objects that are within the given container's inventory
+ * with a globally unique identifier selected from a pool of numbers for each object.
+ * @param container the storage unit in which objects can be found
+ * @param guid implicit reference to a unique number system
+ * @see `GUIDTask.unregisterInventory`
+ * `Container`
+ * @return a list of `TaskBundle` messages
+ */
+ def registerInventory(guid: UniqueNumberOps, container: Container): List[TaskBundle] = {
+ container.Inventory.Items.map{ entry => registerEquipment(guid, entry.obj) }
+ }
+
+ /**
+ * Construct tasking that registers a `LockerContainer` object
+ * with a globally unique identifier selected from a pool of numbers.
+ * @param obj the object being registered
+ * @param guid implicit reference to a unique number system
+ * @see `GUIDTask.unregisterLocker`
+ * @return a `TaskBundle` message
+ */
+ def registerLocker(guid: UniqueNumberOps, obj: LockerContainer): TaskBundle = {
+ TaskBundle(RegisterObjectTask(guid, obj), registerInventory(guid, obj))
+ }
+
+ /**
+ * Construct tasking that registers a `LockerContainer` object
+ * with a globally unique identifier selected from a pool of numbers.
+ * @param obj the object being registered
+ * @param guid implicit reference to a unique number system
+ * @see `GUIDTask.unregisterLocker`
+ * @return a `TaskBundle` message
+ */
+ def registerLocker(guid: UniqueNumberOps, obj: LockerEquipment): TaskBundle = {
+ TaskBundle(RegisterObjectTask(guid, obj), registerInventory(guid, obj))
+ }
+
/**
* Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers, as a `Player`.
*
@@ -150,13 +180,13 @@ object GUIDTask {
* a task built of lesser registration tasks and supporting tasks should be written instead.
* @param tplayer the `Player` object being registered
* @param guid implicit reference to a unique number system
- * @return a `TaskResolver.GiveTask` message
+ * @return a `TaskBundle` message
*/
- def RegisterAvatar(tplayer: Player)(implicit guid: ActorRef): TaskResolver.GiveTask = {
- val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), RegisterEquipment)
- val lockerTask = List(RegisterObjectTask(tplayer.avatar.locker))
- val inventoryTasks = RegisterInventory(tplayer)
- TaskResolver.GiveTask(RegisterObjectTask(tplayer).task, holsterTasks ++ lockerTask ++ inventoryTasks)
+ def registerAvatar(guid: UniqueNumberOps, tplayer: Player): TaskBundle = {
+ val holsterTasks = visibleSlotTaskBuilding(guid, tplayer.Holsters(), registerEquipment)
+ val lockerTask = List(registerObject(guid, tplayer.avatar.locker))
+ val inventoryTasks = registerInventory(guid, tplayer)
+ TaskBundle(RegisterObjectTask(guid, tplayer), holsterTasks ++ lockerTask ++ inventoryTasks)
}
/**
@@ -165,12 +195,12 @@ object GUIDTask {
* Similar to `RegisterAvatar` but the locker components are skipped.
* @param tplayer the `Player` object being registered
* @param guid implicit reference to a unique number system
- * @return a `TaskResolver.GiveTask` message
+ * @return a `TaskBundle` message
*/
- def RegisterPlayer(tplayer: Player)(implicit guid: ActorRef): TaskResolver.GiveTask = {
- val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), RegisterEquipment)
- val inventoryTasks = RegisterInventory(tplayer)
- TaskResolver.GiveTask(GUIDTask.RegisterObjectTask(tplayer)(guid).task, holsterTasks ++ inventoryTasks)
+ def registerPlayer(guid: UniqueNumberOps, tplayer: Player): TaskBundle = {
+ val holsterTasks = visibleSlotTaskBuilding(guid, tplayer.Holsters(), registerEquipment)
+ val inventoryTasks = registerInventory(guid, tplayer)
+ TaskBundle(RegisterObjectTask(guid, tplayer), holsterTasks ++ inventoryTasks)
}
/**
@@ -188,25 +218,39 @@ object GUIDTask {
* a task built of lesser registration tasks and supporting tasks should be written instead.
* @param vehicle the `Vehicle` object being registered
* @param guid implicit reference to a unique number system
- * @return a `TaskResolver.GiveTask` message
+ * @return a `TaskBundle` message
*/
- def RegisterVehicle(vehicle: Vehicle)(implicit guid: ActorRef): TaskResolver.GiveTask = {
- val weaponTasks = VisibleSlotTaskBuilding(vehicle.Weapons.values, RegisterEquipment)
+ def registerVehicle(guid: UniqueNumberOps, vehicle: Vehicle): TaskBundle = {
+ val weaponTasks = visibleSlotTaskBuilding(guid, vehicle.Weapons.values, registerEquipment)
val utilTasks =
- Vehicle.EquipmentUtilities(vehicle.Utilities).values.map(util => { RegisterObjectTask(util()) }).toList
- val inventoryTasks = RegisterInventory(vehicle)
- TaskResolver.GiveTask(RegisterObjectTask(vehicle).task, weaponTasks ++ utilTasks ++ inventoryTasks)
+ Vehicle.EquipmentUtilities(vehicle.Utilities).values.map(util => { registerObject(guid, util()) }).toList
+ val inventoryTasks = registerInventory(guid, vehicle)
+ TaskBundle(RegisterObjectTask(guid, vehicle), weaponTasks ++ utilTasks ++ inventoryTasks)
}
- def RegisterDeployableTurret(
- obj: PlanetSideGameObject with WeaponTurret
- )(implicit guid: ActorRef): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- RegisterObjectTask(obj).task,
- VisibleSlotTaskBuilding(obj.Weapons.values, GUIDTask.RegisterEquipment) ++ RegisterInventory(obj)
+ def registerDeployableTurret(guid: UniqueNumberOps, obj: PlanetSideGameObject with WeaponTurret): TaskBundle = {
+ TaskBundle(
+ RegisterObjectTask(guid, obj),
+ visibleSlotTaskBuilding(guid, obj.Weapons.values, registerEquipment) ++ registerInventory(guid, obj)
)
}
+ //unregistration tasking
+ protected case class UnregisterObjectTask(
+ guid: UniqueNumberOps,
+ obj: IdentifiableEntity
+ ) extends Task {
+ def action(): Future[Any] = {
+ guid.Unregister(obj)
+ }
+
+ def undo(): Unit = RegisterObjectTask(guid, obj)
+
+ def isSuccessful() : Boolean = !obj.HasGUID
+
+ override def description(): String = s"unregister $obj"
+ }
+
/**
* Construct tasking that unregisters an object from a globally unique identifier system.
*
@@ -214,73 +258,10 @@ object GUIDTask {
* It is the most basic operation that all objects that can have their GUIDs revoked must perform.
* @param obj the object being unregistered
* @param guid implicit reference to a unique number system
- * @see `GUIDTask.RegisterObjectTask`
- * @return a `TaskResolver.GiveTask` message
+ * @see `GUIDTask.registerObjectTask`
+ * @return a `TaskBundle` message
*/
- def UnregisterObjectTask(obj: IdentifiableEntity)(implicit guid: ActorRef): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- new Task() {
- private val localObject = obj
- private val localAccessor = guid
-
- override def Description: String = s"unregister $localObject"
-
- override def isComplete: Task.Resolution.Value =
- if (!localObject.HasGUID) {
- Task.Resolution.Success
- } else {
- Task.Resolution.Incomplete
- }
-
- def Execute(resolver: ActorRef): Unit = {
- import net.psforever.objects.guid.actor.Unregister
- localAccessor ! Unregister(localObject, resolver)
- }
- }
- )
- }
-
- /**
- * Construct tasking that unregisters a `Tool` object from a globally unique identifier system.
- *
- * This task performs an operation that reverses the effect of `RegisterTool`.
- * @param obj the `Tool` object being unregistered
- * @param guid implicit reference to a unique number system
- * @see `GUIDTask.RegisterTool`
- * @return a `TaskResolver.GiveTask` message
- */
- def UnregisterTool(obj: Tool)(implicit guid: ActorRef): TaskResolver.GiveTask = {
- val ammoTasks: List[TaskResolver.GiveTask] =
- (0 until obj.MaxAmmoSlot).map(ammoIndex => UnregisterObjectTask(obj.AmmoSlots(ammoIndex).Box)).toList
- TaskResolver.GiveTask(UnregisterObjectTask(obj).task, ammoTasks)
- }
-
- /**
- * Construct tasking that unregisters a `LockerContainer` object from a globally unique identifier system.
- * @param obj the object being unregistered
- * @param guid implicit reference to a unique number system
- * @see `GUIDTask.RegisterLocker`
- * @return a `TaskResolver.GiveTask` message
- */
- def UnregisterLocker(obj: LockerContainer)(implicit guid: ActorRef): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(UnregisterObjectTask(obj).task, UnregisterInventory(obj))
- }
- def UnregisterLocker(obj: LockerEquipment)(implicit guid: ActorRef): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(RegisterObjectTask(obj).task, RegisterInventory(obj))
- }
-
- /**
- * Construct tasking that unregisters the objects that are within the given container's inventory
- * from a globally unique identifier system.
- * @param container the storage unit in which objects can be found
- * @param guid implicit reference to a unique number system
- * @see `GUIDTask.RegisterInventory`
- * `Container`
- * @return a list of `TaskResolver.GiveTask` messages
- */
- def UnregisterInventory(container: Container)(implicit guid: ActorRef): List[TaskResolver.GiveTask] = {
- container.Inventory.Items.map(entry => { UnregisterEquipment(entry.obj) })
- }
+ def unregisterObject(guid: UniqueNumberOps, obj: IdentifiableEntity): TaskBundle = TaskBundle(UnregisterObjectTask(guid, obj))
/**
* Construct tasking that unregisters an object from a globally unique identifier system
@@ -289,32 +270,88 @@ object GUIDTask {
* This task performs an operation that reverses the effect of `RegisterEquipment`.
* @param obj the `Equipment` object being unregistered
* @param guid implicit reference to a unique number system
- * @see `GUIDTask.RegisterEquipment`
- * @return a `TaskResolver.GiveTask` message
+ * @see `GUIDTask.registerEquipment`
+ * @return a `TaskBundle` message
*/
- def UnregisterEquipment(obj: Equipment)(implicit guid: ActorRef): TaskResolver.GiveTask = {
+ def unregisterTool(guid: UniqueNumberOps, obj: Tool): TaskBundle = {
+ TaskBundle(
+ UnregisterObjectTask(guid, obj),
+ (0 until obj.MaxAmmoSlot).map(ammoIndex => unregisterObject(guid, obj.AmmoSlots(ammoIndex).Box))
+ )
+ }
+
+ /**
+ * Construct tasking that registers an object with a globally unique identifier selected from a pool of numbers,
+ * after determining whether the object is complex (`Tool` or `Locker`) or is simple.
+ *
+ * The objects in this case are specifically `Equipment`, a subclass of the basic register-able `IdentifiableEntity`.
+ * About five subclasses of `Equipment` exist, but they decompose into two groups - "complex objects" and "simple objects."
+ * "Simple objects" are most groups of `Equipment` and just their own GUID to be registered.
+ * "Complex objects" are just the `Tool` category of `Equipment`.
+ * They have internal objects that must also have their GUID's registered to function.
+ *
+ * Using this function when passing unknown `Equipment` is recommended.
+ * The type will be sorted and the object will be handled according to its complexity level.
+ * @param obj the `Equipment` object being registered
+ * @param guid implicit reference to a unique number system
+ * @return a `TaskBundle` message
+ */
+ def unregisterEquipment(guid: UniqueNumberOps, obj: Equipment): TaskBundle = {
obj match {
- case tool: Tool =>
- UnregisterTool(tool)
- case _ =>
- UnregisterObjectTask(obj)
+ case tool: Tool => unregisterTool(guid, tool)
+ case _ => unregisterObject(guid, obj)
}
}
+ /**
+ * Construct tasking that unregisters the objects that are within the given container's inventory
+ * from a globally unique identifier system.
+ * @param container the storage unit in which objects can be found
+ * @param guid implicit reference to a unique number system
+ * @see `GUIDTask.registerInventory`
+ * `Container`
+ * @return a list of `TaskBundle` messages
+ */
+ def unregisterInventory(guid: UniqueNumberOps, container: Container): List[TaskBundle] = {
+ container.Inventory.Items.map{ entry => unregisterEquipment(guid, entry.obj) }
+ }
+
+ /**
+ * Construct tasking that unregisters a `LockerContainer` object from a globally unique identifier system.
+ * @param obj the object being unregistered
+ * @param guid implicit reference to a unique number system
+ * @see `GUIDTask.registerLocker`
+ * @return a `TaskBundle` message
+ */
+ def unregisterLocker(guid: UniqueNumberOps, obj: LockerContainer): TaskBundle = {
+ TaskBundle(UnregisterObjectTask(guid, obj), unregisterInventory(guid, obj))
+ }
+
+ /**
+ * Construct tasking that unregisters a `LockerContainer` object from a globally unique identifier system.
+ * @param obj the object being unregistered
+ * @param guid implicit reference to a unique number system
+ * @see `GUIDTask.registerLocker`
+ * @return a `TaskBundle` message
+ */
+ def unregisterLocker(guid: UniqueNumberOps, obj: LockerEquipment): TaskBundle = {
+ TaskBundle(UnregisterObjectTask(guid, obj), unregisterInventory(guid, obj))
+ }
+
/**
* Construct tasking that unregisters a `Player` object from a globally unique identifier system.
*
* This task performs an operation that reverses the effect of `RegisterAvatar`.
* @param tplayer the `Player` object being unregistered
* @param guid implicit reference to a unique number system
- * @see `GUIDTask.RegisterAvatar`
- * @return a `TaskResolver.GiveTask` message
+ * @see `GUIDTask.registerAvatar`
+ * @return a `TaskBundle` message
*/
- def UnregisterAvatar(tplayer: Player)(implicit guid: ActorRef): TaskResolver.GiveTask = {
- val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), UnregisterEquipment)
- val lockerTask = List(UnregisterObjectTask(tplayer.avatar.locker))
- val inventoryTasks = UnregisterInventory(tplayer)
- TaskResolver.GiveTask(UnregisterObjectTask(tplayer).task, holsterTasks ++ lockerTask ++ inventoryTasks)
+ def unregisterAvatar(guid: UniqueNumberOps, tplayer: Player): TaskBundle = {
+ val holsterTasks = visibleSlotTaskBuilding(guid, tplayer.Holsters(), unregisterEquipment)
+ val lockerTask = List(unregisterObject(guid, tplayer.avatar.locker))
+ val inventoryTasks = unregisterInventory(guid, tplayer)
+ TaskBundle(UnregisterObjectTask(guid, tplayer), holsterTasks ++ lockerTask ++ inventoryTasks)
}
/**
@@ -324,13 +361,13 @@ object GUIDTask {
* This task performs an operation that reverses the effect of `RegisterPlayer`.
* @param tplayer the `Player` object being unregistered
* @param guid implicit reference to a unique number system
- * @see `GUIDTask.RegisterAvatar`
- * @return a `TaskResolver.GiveTask` message
+ * @see `GUIDTask.registerAvatar`
+ * @return a `TaskBundle` message
*/
- def UnregisterPlayer(tplayer: Player)(implicit guid: ActorRef): TaskResolver.GiveTask = {
- val holsterTasks = VisibleSlotTaskBuilding(tplayer.Holsters(), UnregisterEquipment)
- val inventoryTasks = UnregisterInventory(tplayer)
- TaskResolver.GiveTask(GUIDTask.UnregisterObjectTask(tplayer).task, holsterTasks ++ inventoryTasks)
+ def unregisterPlayer(guid: UniqueNumberOps, tplayer: Player): TaskBundle = {
+ val holsterTasks = visibleSlotTaskBuilding(guid, tplayer.Holsters(), unregisterEquipment)
+ val inventoryTasks = unregisterInventory(guid, tplayer)
+ TaskBundle(UnregisterObjectTask(guid, tplayer), holsterTasks ++ inventoryTasks)
}
/**
@@ -339,26 +376,25 @@ object GUIDTask {
* This task performs an operation that reverses the effect of `RegisterVehicle`.
* @param vehicle the `Vehicle` object being unregistered
* @param guid implicit reference to a unique number system
- * @see `GUIDTask.RegisterVehicle`
- * @return a `TaskResolver.GiveTask` message
+ * @see `GUIDTask.registerVehicle`
+ * @return a `TaskBundle` message
*/
- def UnregisterVehicle(vehicle: Vehicle)(implicit guid: ActorRef): TaskResolver.GiveTask = {
- val weaponTasks = VisibleSlotTaskBuilding(vehicle.Weapons.values, UnregisterEquipment)
+ def unregisterVehicle(guid: UniqueNumberOps, vehicle: Vehicle): TaskBundle = {
+ val weaponTasks = visibleSlotTaskBuilding(guid, vehicle.Weapons.values, unregisterEquipment)
val utilTasks =
- Vehicle.EquipmentUtilities(vehicle.Utilities).values.map(util => { UnregisterObjectTask(util()) }).toList
- val inventoryTasks = UnregisterInventory(vehicle)
- TaskResolver.GiveTask(UnregisterObjectTask(vehicle).task, weaponTasks ++ utilTasks ++ inventoryTasks)
+ Vehicle.EquipmentUtilities(vehicle.Utilities).values.map(util => { unregisterObject(guid, util()) }).toList
+ val inventoryTasks = unregisterInventory(guid, vehicle)
+ TaskBundle(UnregisterObjectTask(guid, vehicle), weaponTasks ++ utilTasks ++ inventoryTasks)
}
- def UnregisterDeployableTurret(
- obj: PlanetSideGameObject with WeaponTurret
- )(implicit guid: ActorRef): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- UnregisterObjectTask(obj).task,
- VisibleSlotTaskBuilding(obj.Weapons.values, GUIDTask.UnregisterEquipment) ++ UnregisterInventory(obj)
+ def unregisterDeployableTurret(guid: UniqueNumberOps, obj: PlanetSideGameObject with WeaponTurret): TaskBundle = {
+ TaskBundle(
+ UnregisterObjectTask(guid, obj),
+ visibleSlotTaskBuilding(guid, obj.Weapons.values, unregisterEquipment) ++ unregisterInventory(guid, obj)
)
}
+ //support
/**
* Construct tasking that allocates work upon encountered `Equipment` objects
* in reference to a globally unique identifier system of a pool of numbers.
@@ -367,12 +403,14 @@ object GUIDTask {
* @param func the function used to build tasking from any discovered `Equipment`;
* strictly either `RegisterEquipment` or `UnregisterEquipment`
* @param guid implicit reference to a unique number system
- * @return a list of `TaskResolver.GiveTask` messages
+ * @return a list of `TaskBundle` messages
*/
- def VisibleSlotTaskBuilding(list: Iterable[EquipmentSlot], func: Equipment => TaskResolver.GiveTask)(implicit
- guid: ActorRef
- ): List[TaskResolver.GiveTask] = {
- recursiveVisibleSlotTaskBuilding(list.iterator, func)
+ private def visibleSlotTaskBuilding(
+ guid: UniqueNumberOps,
+ list: Iterable[EquipmentSlot],
+ func: (UniqueNumberOps, Equipment) => TaskBundle
+ ): List[TaskBundle] = {
+ recursiveVisibleSlotTaskBuilding(guid, list.iterator, func)
}
/**
@@ -386,18 +424,17 @@ object GUIDTask {
* @return a `List` of `Equipment` tasking
*/
@tailrec private def recursiveVisibleSlotTaskBuilding(
- iter: Iterator[EquipmentSlot],
- func: Equipment => TaskResolver.GiveTask,
- list: List[TaskResolver.GiveTask] = Nil
- )(implicit guid: ActorRef): List[TaskResolver.GiveTask] = {
+ guid: UniqueNumberOps,
+ iter: Iterator[EquipmentSlot],
+ func: (UniqueNumberOps, Equipment) => TaskBundle,
+ list: List[TaskBundle] = Nil
+ ): List[TaskBundle] = {
if (!iter.hasNext) {
list
} else {
iter.next().Equipment match {
- case Some(item) =>
- recursiveVisibleSlotTaskBuilding(iter, func, list :+ func(item))
- case None =>
- recursiveVisibleSlotTaskBuilding(iter, func, list)
+ case Some(item) => recursiveVisibleSlotTaskBuilding(guid, iter, func, list :+ func(guid, item))
+ case None => recursiveVisibleSlotTaskBuilding(guid, iter, func, list)
}
}
}
diff --git a/src/main/scala/net/psforever/objects/guid/NumberPoolHub.scala b/src/main/scala/net/psforever/objects/guid/NumberPoolHub.scala
index fff77a2a..f4f425e2 100644
--- a/src/main/scala/net/psforever/objects/guid/NumberPoolHub.scala
+++ b/src/main/scala/net/psforever/objects/guid/NumberPoolHub.scala
@@ -2,7 +2,7 @@
package net.psforever.objects.guid
import net.psforever.objects.entity.{IdentifiableEntity, NoGUIDException}
-import net.psforever.objects.guid.key.LoanedKey
+import net.psforever.objects.guid.key.{AvailabilityPolicy, LoanedKey}
import net.psforever.objects.guid.pool.{ExclusivePool, GenericPool, NumberPool}
import net.psforever.objects.guid.source.NumberSource
import net.psforever.types.PlanetSideGUID
@@ -24,10 +24,7 @@ class NumberPoolHub(private val source: NumberSource) {
import scala.collection.mutable
private val hash: mutable.HashMap[String, NumberPool] = mutable.HashMap[String, NumberPool]()
private val bigpool: mutable.LongMap[String] = mutable.LongMap[String]()
- hash += "generic" -> new GenericPool(bigpool, source.size)
- source.finalizeRestrictions.foreach(i =>
- bigpool += i.toLong -> ""
- ) //these numbers can never be pooled; the source can no longer restrict numbers
+ hash += "generic" -> GenericPool(bigpool, source.size, poolName = "generic")
/**
* Given a globally unique identifier, return any object registered to it.
@@ -327,7 +324,7 @@ class NumberPoolHub(private val source: NumberSource) {
* @param number the number whose assignment is requested
* @return an object that has been registered
*/
- def latterPartRegister(obj: IdentifiableEntity, number: Int): Try[IdentifiableEntity] = {
+ private[guid] def latterPartRegister(obj: IdentifiableEntity, number: Int): Try[IdentifiableEntity] = {
register_GetMonitorFromSource(number) match {
case Success(monitor) =>
monitor.Object = obj
@@ -459,7 +456,7 @@ class NumberPoolHub(private val source: NumberSource) {
* @param number the number to return.
* @return any object previously using this number
*/
- def latterPartUnregister(number: Int): Option[IdentifiableEntity] = source.returnNumber(number)
+ private[guid] def latterPartUnregister(number: Int): Option[IdentifiableEntity] = source.returnNumber(number)
/**
* Determines if the object is registered.
diff --git a/src/main/scala/net/psforever/objects/guid/Task.scala b/src/main/scala/net/psforever/objects/guid/Task.scala
deleted file mode 100644
index f4c73412..00000000
--- a/src/main/scala/net/psforever/objects/guid/Task.scala
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.guid
-
-import akka.actor.ActorRef
-
-trait Task {
- def Description: String = "write_descriptive_task_message"
- def Execute(resolver: ActorRef): Unit
- def isComplete: Task.Resolution.Value = Task.Resolution.Incomplete
- def Timeout: Long = 200L //milliseconds
- def onSuccess(): Unit = {}
- def onFailure(ex: Throwable): Unit = {}
- def onTimeout(ex: Throwable): Unit = onFailure(ex)
- def onAbort(ex: Throwable): Unit = {}
- def Cleanup(): Unit = {}
-}
-
-object Task {
- def TimeNow: Long = {
- System.nanoTime()
- //java.time.Instant.now().getEpochSecond
- }
-
- object Resolution extends Enumeration {
- val Success, Incomplete, Failure = Value
- }
-}
diff --git a/src/main/scala/net/psforever/objects/guid/TaskResolver.scala b/src/main/scala/net/psforever/objects/guid/TaskResolver.scala
deleted file mode 100644
index e1b6fd91..00000000
--- a/src/main/scala/net/psforever/objects/guid/TaskResolver.scala
+++ /dev/null
@@ -1,507 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.guid
-
-import java.util.concurrent.TimeoutException
-
-import akka.actor.{Actor, ActorRef, Cancellable}
-import akka.routing.Broadcast
-import net.psforever.objects.Default
-
-import scala.annotation.tailrec
-import scala.collection.mutable.ListBuffer
-import scala.concurrent.duration._
-import scala.concurrent.ExecutionContext.Implicits.global
-import scala.util.{Failure, Success}
-
-class TaskResolver() extends Actor {
-
- /** list of all work currently managed by this resolver */
- private val tasks: ListBuffer[TaskResolver.TaskEntry] = new ListBuffer[TaskResolver.TaskEntry]
-
- /** scheduled termination of tardy managed work */
- private var timeoutCleanup: Cancellable = Default.Cancellable
-
- /** 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`.
- */
- override def 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()
- 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")
- tasks.foreach(entry => {
- OnFailure(entry.task, ex)
- })
- super.aroundPostStop()
- }
-
- def receive: Receive = {
- case TaskResolver.GiveTask(aTask, Nil) =>
- GiveTask(aTask)
-
- case TaskResolver.GiveTask(aTask, subtasks) =>
- QueueSubtasks(aTask, subtasks)
-
- case TaskResolver.GiveSubtask(aTask, subtasks, resolver) =>
- QueueSubtasks(aTask, subtasks, resolver)
-
- case TaskResolver.CompletedSubtask(obj) => //inter-resolver calls
- ExecuteNewTasks(obj)
-
- case Success(obj: Task) => //inter-resolver calls
- OnSuccess(obj)
-
- case Success | Success(_) => //success redirected from called event
- OnSuccess()
-
- case TaskResolver.Failure(obj, ex) => //inter-resolver calls
- OnFailure(obj, ex)
-
- case Failure(ex) => //failure redirected from called event
- OnFailure(ex)
-
- case TaskResolver.AbortTask(task, ex) =>
- OnAbort(task, ex)
-
- case TaskResolver.TimeoutCleanup() =>
- TimeoutCleanup()
-
- case msg =>
- log.warn(s"$self received an unexpected message $msg from ${sender()}")
- }
-
- /**
- * Accept simple work and perform it.
- * @param aTask the work to be completed
- */
- private def GiveTask(aTask: Task): Unit = {
- val entry: TaskResolver.TaskEntry = TaskResolver.TaskEntry(aTask)
- tasks += entry
- trace(s"enqueue and start task ${aTask.Description}")
- entry.Execute(self)
- StartTimeoutCheck()
- }
-
- /**
- * Start the periodic checks for a task that has run for too long (timed-out), unless those checks are already running.
- */
- private def StartTimeoutCheck(): Unit = {
- if (timeoutCleanup.isCancelled) {
- timeoutCleanup = context.system.scheduler.scheduleWithFixedDelay(
- 500 milliseconds,
- 500 milliseconds,
- self,
- TaskResolver.TimeoutCleanup()
- )
- }
- }
-
- /**
- * Accept complicated work and divide it into a main task and tasks that must be handled before the main task.
- * Do not start the main task until all of the aforementioned "sub-tasks" are completed.
- *
- * Sub-tasks can be nested many times.
- * All immediate sub-tasks count as the primary sub-tasks for the current main task.
- * Each pair of main task and sub-tasks, for every sub-task discovered, is passed on to another `TaskResolver` for completion.
- * The parent of this `TaskResolver` is the router logic for all brethren `TaskResolver` `Actors`.
- * @param task the work to be completed
- * @param subtasks other work that needs to be completed first
- * @param resolver the `TaskResolver` that distributed this work, thus determining that this work is a sub-task;
- * by default, no one, as the work is identified as a main task
- */
- private def QueueSubtasks(
- task: Task,
- subtasks: List[TaskResolver.GiveTask],
- resolver: ActorRef = ActorRef.noSender
- ): Unit = {
- val entry: TaskResolver.TaskEntry = TaskResolver.TaskEntry(task, subtasks.map(task => task.task), resolver)
- tasks += entry
- trace(s"enqueue task ${task.Description}")
- if (subtasks.isEmpty) { //a leaf in terms of task dependency; so, not dependent on any other work
- trace(s"start task ${task.Description}")
- entry.Execute(self)
- } else {
- trace(s"enqueuing ${subtasks.length} substask(s) belonging to ${task.Description}")
- subtasks.foreach({ subtask =>
- context.parent ! TaskResolver.GiveSubtask(
- subtask.task,
- subtask.subs,
- self
- ) //route back to submit subtask to pool
- })
- }
- StartTimeoutCheck()
- }
-
- /**
- * Perform these checks when a task has reported successful completion to this TaskResolver.
- * 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 = {
- //by reversing the List, we find the most outstanding Task with the completion state
- TaskResolver.filterCompletion(tasks.indices.reverseIterator, tasks.toList, Task.Resolution.Success) match {
- case Some(index) =>
- GeneralOnSuccess(index)
- case None => ;
- }
- }
-
- /**
- * 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 task ${entry.task.Description}")
- 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.
- * 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(subtask: Task): Unit = {
- TaskResolver.findTaskWithSubtask(tasks.iterator, subtask) match {
- case Some(index) =>
- val entry = tasks(index)
- if (TaskResolver.filterCompletionMatch(entry.subtasks.iterator, Task.Resolution.Success)) {
- trace(s"start new task ${entry.task.Description}")
- entry.Execute(self)
- StartTimeoutCheck()
- }
- case None => ;
- }
- }
-
- /**
- * 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, 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.
- * @param ex a `Throwable` that reports what happened to the task
- */
- private def OnFailure(ex: Throwable): Unit = {
- //by reversing the List, we find the most outstanding Task with the completion state
- TaskResolver.filterCompletion(tasks.indices.reverseIterator, tasks.toList, Task.Resolution.Failure) match {
- case Some(index) =>
- GeneralOnFailure(index, ex)
- case None => ;
- }
- }
-
- /**
- * 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 OnFailure(subtask: Task, ex: Throwable): Unit = {
- TaskResolver.findTaskWithSubtask(tasks.iterator, subtask) match {
- case Some(index) =>
- GeneralOnFailure(index, ex)
- case None => ;
- }
- }
-
- /**
- * 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 task ${task.Description}")
- 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
- })
- }
-
- /**
- * If a specific `Task` is governed by this `TaskResolver`, find its index and dispose of it and its known sub-tasks.
- * @param task the work to be found
- * @param ex a `Throwable` that reports what happened to the work
- */
- private def OnAbort(task: Task, ex: Throwable): Unit = {
- TaskResolver.findTask(tasks.iterator, task) match {
- case Some(index) =>
- PropagateAbort(index, ex)
- TaskCleanup(index)
- case None => ;
- }
- }
-
- /**
- * If a specific `Task` is governed by this `TaskResolver`, dispose of it and its known sub-tasks.
- * @param index the index of the discovered work
- * @param ex a `Throwable` that reports what happened to the work
- */
- private def PropagateAbort(index: Int, ex: Throwable): Unit = {
- tasks(index).subtasks.foreach({ subtask =>
- if (subtask.isComplete == Task.Resolution.Success) {
- trace(s"aborting task ${subtask.Description}")
- subtask.onAbort(ex)
- }
- context.parent ! Broadcast(TaskResolver.AbortTask(subtask, ex))
- })
- }
-
- /**
- * Find all tasks that have been running for too long and declare them as timed-out.
- * Run periodically, as long as work is being performed.
- */
- private def TimeoutCleanup(): Unit = {
- TaskResolver
- .filterTimeout(tasks.indices.reverseIterator, tasks.toList, Task.TimeNow)
- .foreach({ index =>
- val ex: Throwable = new TimeoutException(s"a task ${tasks(index).task} has timed out")
- tasks(index).task.onTimeout(ex)
- PropagateAbort(index, ex)
- })
- }
-
- /**
- * Remove a `Task` that has reported completion.
- * @param index an index of work in the `List` of `Task`s
- */
- private def TaskCleanup(index: Int): Unit = {
- tasks(index).task.Cleanup()
- tasks.remove(index)
- if (tasks.isEmpty) {
- timeoutCleanup.cancel()
- }
- }
-}
-
-object TaskResolver {
-
- /**
- * Give this `TaskResolver` simple work to be performed.
- * @param task the work to be completed
- * @param subs other work that needs to be completed first
- */
- final case class GiveTask(task: Task, subs: List[GiveTask] = Nil)
-
- /**
- * Pass around complex work to be performed.
- * @param task the work to be completed
- * @param subs other work that needs to be completed first
- * @param resolver the `TaskResolver` that will handle work that depends on the outcome of this work
- */
- private final case class GiveSubtask(task: Task, subs: List[GiveTask], resolver: ActorRef)
-
- /**
- * Run a scheduled timed-out `Task` check.
- */
- 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 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`.
- * @param task the work to be removed
- * @param ex an explanation why the work is being aborted
- */
- private final case class AbortTask(task: Task, ex: Throwable)
-
- /**
- * Storage unit for a specific unit of work, plus extra information.
- * @param task the work to be completed
- * @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 supertaskRef the `TaskResolver` that will handle work that depends on the outcome of this work
- */
- private final case class TaskEntry(
- task: Task,
- subtasks: List[Task] = Nil,
- supertaskRef: ActorRef = ActorRef.noSender
- ) {
- private var start: Long = 0L
- private var isExecuting: Boolean = false
-
- def Start: Long = start
-
- def Executing: Boolean = isExecuting
-
- /**
- * Only execute each task once.
- * @param ref the `TaskResolver` currently handling this `Task`/`TaskEntry`
- */
- def Execute(ref: ActorRef): Unit = {
- if (!isExecuting) {
- isExecuting = true
- start = Task.TimeNow
- task.Execute(ref)
- }
- }
- }
-
- /**
- * 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
- * @return the first valid index when `TaskEntry` has its primary `Task` matching the completion status
- */
- @tailrec private def filterCompletion(
- iter: Iterator[Int],
- tasks: List[TaskEntry],
- resolution: Task.Resolution.Value
- ): Option[Int] = {
- if (!iter.hasNext) {
- None
- } else {
- val index: Int = iter.next()
- if (tasks(index).task.isComplete == resolution) {
- Some(index)
- } else {
- filterCompletion(iter, tasks, resolution)
- }
- }
- }
-
- /**
- * Scan across a group of sub-tasks to determine if they all match the target completion status.
- * @param iter an `Iterator` of enqueued sub-tasks
- * @param resolution the target completion status
- * @return `true`, if all tasks match the complete status;
- * `false`, otherwise
- */
- @tailrec private def filterCompletionMatch(iter: Iterator[Task], resolution: Task.Resolution.Value): Boolean = {
- if (!iter.hasNext) {
- true
- } else {
- if (iter.next().isComplete == resolution) {
- filterCompletionMatch(iter, resolution)
- } else {
- false
- }
- }
- }
-
- /**
- * Find the indices of all enqueued work that has timed-out.
- * @param iter an `Iterator` of enqueued `TaskEntry` indices
- * @param now the current time in milliseconds
- * @param indexList a persistent `List` of indices
- * @return the `List` of all valid `Task` indices
- */
- @tailrec private def filterTimeout(
- iter: Iterator[Int],
- tasks: List[TaskEntry],
- now: Long,
- indexList: List[Int] = Nil
- ): List[Int] = {
- if (!iter.hasNext) {
- indexList
- } else {
- val index: Int = iter.next()
- val taskEntry = tasks(index)
- if (
- taskEntry.Executing && taskEntry.task.isComplete == Task.Resolution.Incomplete && now - taskEntry.Start > taskEntry.task.Timeout
- ) {
- filterTimeout(iter, tasks, now, indexList :+ index)
- } else {
- filterTimeout(iter, tasks, now, indexList)
- }
- }
- }
-
- /**
- * 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)
- }
- }
- }
-}
diff --git a/src/main/scala/net/psforever/objects/guid/TaskWorkflow.scala b/src/main/scala/net/psforever/objects/guid/TaskWorkflow.scala
new file mode 100644
index 00000000..85b13429
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/guid/TaskWorkflow.scala
@@ -0,0 +1,215 @@
+// Copyright (c) 2021 PSForever
+package net.psforever.objects.guid
+
+import scala.concurrent.ExecutionContext.Implicits.global
+import scala.concurrent.{Future, Promise}
+import scala.util.{Failure, Success}
+
+/**
+ * Parts of the task resolution lifecycle.
+ */
+sealed trait TaskBehaviors {
+ /** What the task is supposed to accomplish. */
+ def action(): Future[Any]
+ /** A reversal of 'what the task is supposed to accomplish'. */
+ def undo(): Unit
+ /** Has the task been successfully completed? */
+ def isSuccessful(): Boolean
+ /** Describe this task's actions. */
+ def description(): String = getClass.getSimpleName
+}
+
+/**
+ * A primary unit of work in a workflow.
+ */
+trait Task
+ extends TaskBehaviors {
+ /** A careful determination if the task can be attempted.
+ * @see `Task.action`
+ */
+ private[guid] def performAction(): Future[Any] = {
+ if (!isSuccessful()) {
+ action()
+ } else {
+ Future(Failure(new TaskNotExecutedException(task = this)))
+ }
+ }
+ /** A careful determination if the task needs to be undone.
+ * @see `Task.undo`
+ */
+ private[guid] def performUndo(): Unit = {
+ if (isSuccessful()) undo() else ()
+ }
+}
+
+/**
+ * A primary unit of work in a workflow that is set up to execute and never be taken back.
+ * Good for top-level tasking that only reports on the success of work carried out by subtasks.
+ */
+trait StraightforwardTask
+ extends Task {
+ def undo(): Unit = { /* blank */ }
+
+ def isSuccessful(): Boolean = false /* always primed to be executed */
+}
+
+/**
+ * The packaging of a more complicated unit of work in a workflow
+ * in which one task relies on the successful completion of other tasks.
+ * @param mainTask the primary task
+ * @param subTasks tasks that are necessary to complete before starting on the primary one
+ */
+final case class TaskBundle(mainTask: Task, subTasks: Seq[TaskBundle])
+ extends TaskBehaviors {
+ /** Attempt 'what the [primary] task is supposed to accomplish'. */
+ def action(): Future[Any] = mainTask.performAction()
+
+ /** Attempt a reversal of what the all the connected tasks are 'supposed to accomplish'. */
+ def undo() : Unit = {
+ mainTask.performUndo()
+ subTasks.foreach { _.undo() }
+ }
+
+ /** A hierarchical analysis of whether `the task been successfully completed`. */
+ def isSuccessful(): Boolean = mainTask.isSuccessful() && subTasks.forall { _.isSuccessful() }
+
+ override def description(): String = {
+ val subCount: String = if (subTasks.nonEmpty) s" (${subTasks.size} subtasks)" else ""
+ s"${mainTask.description()}$subCount"
+ }
+}
+
+object TaskBundle {
+ /**
+ * The packaging of a unit of work in a workflow.
+ * @param task the task
+ */
+ def apply(task: Task): TaskBundle = TaskBundle(task, List())
+ /**
+ * The packaging of a unit of work in a workflow
+ * and a single task required to be completed first.
+ * @param task the primary task
+ * @param subTask the task that must be completed before the primary task
+ */
+ def apply(task: Task, subTask: Task): TaskBundle = TaskBundle(task, TaskBundle(subTask))
+ /**
+ * The packaging of a unit of work in a workflow
+ * and the task(s) required to be completed first.
+ * @param task the primary task
+ * @param subTask the task(s) that must be completed before the primary task
+ */
+ def apply(task: Task, subTask: TaskBundle): TaskBundle = TaskBundle(task, Seq(subTask))
+}
+
+class TaskNotExecutedException(task: TaskBehaviors, msg: String) extends Exception(msg) {
+ def this(task: Task) = {
+ this(task, s"task '${task.description()}' was not successful")
+ }
+
+ def this(task: TaskBundle) = {
+ this(task, s"task ${task.description()} was not successful")
+ }
+}
+
+object TaskWorkflow {
+ /**
+ * The entry into the task workflow resolution process.
+ * @param taskTree the packaged tasks that need to be completed
+ * @return the anticipation of a task to be completed
+ */
+ def execute(taskTree: TaskBundle): Future[Any] = {
+ evaluateTaskAndSubs(taskTree)
+ }
+
+ private def evaluateTaskAndSubs(task: TaskBundle): Future[Any] = {
+ val promise = Promise[Any]()
+ val (result, subResults) = composeTaskAndSubs(task)
+ result.onComplete { _ =>
+ if (matchOnFutureFailure(result)) {
+ //every subtask that has already succeeded must be undone
+ subResults
+ .zip(task.subTasks)
+ .collect { case (a, b) if matchOnFutureSuccess(a) => b }
+ .foreach { _.undo() }
+ }
+ promise.completeWith(result)
+ }
+ promise.future
+ }
+
+ private def composeTaskAndSubs(task: TaskBundle): (Future[Any], Seq[Future[Any]]) = {
+ val promise = Promise[Any]()
+ val composedSubs = task.subTasks.map(evaluateTaskAndSubs)
+ composedSubs match {
+ case Nil =>
+ //no subtasks; just execute the main task
+ promise.completeWith(task.action())
+ case list =>
+ var unassignedCompletion: Boolean = true //shared mutex
+ //wait for subtasks to complete
+ list.foreach { result =>
+ result.onComplete { _ =>
+ unassignedCompletion.synchronized {
+ if (unassignedCompletion && composedSubs.forall(matchOnFutureCompletion)) {
+ unassignedCompletion = false
+ if (composedSubs.forall(matchOnFutureSuccess)) {
+ //if all subtasks passed, execute the main task
+ promise.completeWith(task.action())
+ } else {
+ //if some subtasks did not succeed, pass on wrapped failure
+ promise.completeWith(Future(Failure(new TaskNotExecutedException(task))))
+ }
+ }
+ }
+ }
+ }
+ }
+ (promise.future, composedSubs)
+ }
+
+ /**
+ * Does this anticipation of a task report having completed?
+ * @param f the anticipation
+ * @return whether it has been completed (passed or failed)
+ */
+ def matchOnFutureCompletion(f: Future[Any]): Boolean = {
+ /*
+ if 'matchOnFutureCompletion(FUTURE) == false' then 'matchOnFutureSuccess(FUTURE) == matchOnFutureFailure(FUTURE)'
+ if 'matchOnFutureCompletion(FUTURE) == true' then 'matchOnFutureSuccess(FUTURE) != matchOnFutureFailure(FUTURE)'
+ */
+ f.value match {
+ case Some(_) => true
+ case None => false
+ }
+ }
+
+ /**
+ * Does this anticipation of a task report having succeeded?
+ * The only true success is one where there is no `Failure` and no `Exception`.
+ * @param f the anticipation
+ * @return whether it has succeeded
+ */
+ def matchOnFutureSuccess(f: Future[Any]): Boolean = {
+ f.value match {
+ case Some(Success(_: Exception)) => false
+ case Some(Success(Failure(_))) => false
+ case Some(Success(_)) => true
+ case _ => false
+ }
+ }
+
+ /**
+ * Does this anticipation of a task report having failed?
+ * Having not yet completed does not count as a failure.
+ * @param f the anticipation
+ * @return whether it has failed
+ */
+ def matchOnFutureFailure(f: Future[Any]): Boolean = {
+ f.value match {
+ case Some(Failure(_)) => true
+ case Some(Success(_: Exception)) => true
+ case Some(Success(Failure(_))) => true
+ case _ => false
+ }
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/guid/UniqueNumberOps.scala b/src/main/scala/net/psforever/objects/guid/UniqueNumberOps.scala
new file mode 100644
index 00000000..279255dc
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/guid/UniqueNumberOps.scala
@@ -0,0 +1,454 @@
+// Copyright (c) 2017-2021 PSForever
+package net.psforever.objects.guid
+
+import akka.actor.{Actor, ActorContext, ActorRef, Props}
+import akka.pattern.{AskTimeoutException, ask}
+import akka.util.Timeout
+import net.psforever.objects.entity.IdentifiableEntity
+import net.psforever.objects.guid.uns.{
+ AlreadyRegisteredEntity,
+ AlreadyUnregisteredEntity,
+ NumberPoolActor,
+ RegisteredEntity,
+ UnregisteredEntity
+}
+
+import scala.concurrent.ExecutionContext.Implicits.global
+import scala.concurrent.duration._
+import scala.concurrent.{Future, Promise}
+import scala.util.{Failure, Success}
+
+/**
+ * Wrap around converted `NumberPool`s and synchronize a portion of the number registration process
+ * as a part of the global unique identifier (GUID, UID) number system (UNS, "unique number system").
+ * The ultimate goal is to manage a coherent group of unique identifiers for a given "region".
+ * Both parts of the UID system sit atop the `Zone` for easy external access.
+ * The plain part - the `NumberPoolHub` here - is used for low-priority requests
+ * such as checking for existing associations.
+ *
+ * A four part process is used for object registration tasks.
+ * First, the requested `NumberPool` is located among the list of known `NumberPool`s.
+ * Second, an asynchronous request is sent to that pool to retrieve a number.
+ * (Only any number. Only a failing case allows for selection of a specific number.)
+ * Third, the asynchronous request returns and the original information about the request is recovered.
+ * Fourth, both sides of the contract are completed by the object being assigned the number and
+ * the underlying "number source" is made to remember an association between the object and the number.
+ * Short circuits and recoveries as available on all steps though reporting is split between logging and callbacks.
+ * The process of removing the association between a number and object (unregistering) is a similar four part process.
+ *
+ * The important relationship between this `Actor` and the `Map` of `NumberPoolActors` is as a "gate."
+ * A single `Map` is constructed and shared between multiple entry points to the UID system where requests are messaged.
+ * Multiple entry points send messages to the same `NumberPool`.
+ * That `NumberPool` deals with the messages one at a time and sends reply to each entry point that communicated with it.
+ * This process is almost as fast as the process of the `NumberPool` selecting a number.
+ * (At least, both should be fast.)
+ * @param guid the supporting datatype for the unique number distribution
+ * @param poolActors a mapping created from the `NumberPool`s, to achieve synchronized access
+ */
+class UniqueNumberOps(
+ private val guid: NumberPoolHub,
+ private val poolActors: Map[String, ActorRef]
+ ) {
+ /** The timeout used by all number pool `ask` messaging */
+ private implicit val timeout = UniqueNumberOps.timeout
+
+ /**
+ * The entry point for the entity GUID registration process.
+ * A special check is made first to determine if the entity is already registered, and if so to where.
+ * If the entity is not registered, then the process continues.
+ * @param obj the entity to be assigned a GUID
+ * @param poolName the pool from which the entity wants a GUID to be selected
+ * @return the anticipation of this activity being completed
+ */
+ def Register(
+ obj: IdentifiableEntity,
+ poolName: String
+ ): Future[Any] = {
+ val result: Promise[Any] = Promise()
+ if (obj.HasGUID) {
+ alreadyRegisteredTo(obj, poolName) match {
+ case Some(pname) =>
+ result.success(AlreadyRegisteredEntity(RegisteredEntity(obj, pname, guid, obj.GUID.guid)))
+ case None =>
+ result.failure(new RegisteredToWrongPlaceException(obj, obj.GUID.guid))
+ }
+ } else {
+ result.completeWith(registrationProcess(obj, poolName))
+ }
+ result.future
+ }
+
+ /**
+ * The entry point for the entity GUID unregistration process.
+ * A special check is made first to determine where, if at all, the entity is registered.
+ * Obviously, if the entity is not registered somewhere within purview of this UNS, the process can not continue.
+ * If the entity's registration number pool is found, then the process continues.
+ * @param obj the entity to be unassigned its GUID
+ * @return the anticipation of this activity being completed
+ */
+ def Unregister(obj: IdentifiableEntity): Future[Any] = {
+ val result: Promise[Any] = Promise()
+ if (obj.HasGUID) {
+ val number = obj.GUID.guid
+ guid.WhichPool(number) match {
+ case Some(pname) =>
+ result.completeWith(unregistrationProcess(obj, number, pname))
+ case None =>
+ result.failure(new RegisteredToWrongPlaceException(obj, number))
+ }
+ } else {
+ UniqueNumberOps.log.warn(s"$obj is already unregistered")
+ result.success(Future(AlreadyUnregisteredEntity(UnregisteredEntity(obj, "", guid, -1))))
+ }
+ result.future
+ }
+
+ /**
+ * A step of the entity GUID registration process.
+ * Pass control through to the next step.
+ * @see `registrationProcess(IdentifiableEntity, NumberPoolHub, Map[String, ActorRef], String)`
+ * @param obj the entity to be assigned a GUID
+ * @param poolName the pool to which the object is trying to register
+ * @return the anticipation of this activity being completed
+ */
+ private def registrationProcess(
+ obj: IdentifiableEntity,
+ poolName: String
+ ): Future[Any] = {
+ registrationProcess(obj, guid, poolActors, poolName)
+ }
+
+ /**
+ * A step of the entity GUID registration process.
+ * Send a message to the `NumberPool` to request a number.
+ * If a number is received, continue with a successful registration process.
+ * If no number is received, or some other issue occurs, attempt to recover from the error and report it.
+ * This method is designed to be recursive as it is also utilized for the recovery attempt
+ * and must pass all of the necessary information on to that next attempt.
+ * @param obj the entity to be assigned a GUID
+ * @param hub the supporting datatype for the unique number distribution
+ * @param pools a mapping created from the `NumberPool`s, to achieve synchronized access
+ * @param poolName the pool to which the object is trying to register
+ * @return the anticipation of this activity being completed
+ */
+ private def registrationProcess(
+ obj: IdentifiableEntity,
+ hub: NumberPoolHub,
+ pools: Map[String, ActorRef],
+ poolName: String
+ ): Future[Any] = {
+ val promisingResult: Promise[Any] = Promise()
+ pools.get(poolName) match {
+ case Some(pool) =>
+ //cache
+ val localPromise = promisingResult
+ val localTarget = obj
+ val localUns = hub
+ val localPools = pools
+ val localPoolName = poolName
+ val localPool = pool
+
+ val result = ask(pool, NumberPoolActor.GetAnyNumber())(timeout)
+ result.onComplete {
+ case Success(NumberPoolActor.GiveNumber(number)) =>
+ UniqueNumberOps.processRegisterResult(
+ localPromise,
+ localTarget,
+ localUns,
+ localPoolName,
+ localPool,
+ number
+ )
+ case Success(NumberPoolActor.NoNumber(ex)) =>
+ registrationProcessRetry(localPromise, ex, localTarget, localUns, localPools, localPoolName)
+ case msg =>
+ UniqueNumberOps.log.warn(s"unexpected message during $localTarget's registration process - $msg")
+ }
+ result.recover {
+ case ex: AskTimeoutException =>
+ localPromise.failure(new RegisteringException(msg = s"did not register entity $localTarget in time", ex))
+ }
+
+ case None =>
+ //do not log
+ val ex = new Exception(s"can not find pool $poolName")
+ registrationProcessRetry(promisingResult, ex, obj, guid, pools, poolName)
+ }
+ promisingResult.future
+ }
+
+ /**
+ * na
+ * @param promise the ongoing promise to be fulfilled for the future
+ * @param exception an issue that has arrisen, forcing the retry attempt
+ * @param obj the entity to be assigned a GUID
+ * @param hub the supporting datatype for the unique number distribution
+ * @param pools a mapping created from the `NumberPool`s, to achieve synchronized access
+ * @param poolName the pool to which the object is trying to register
+ */
+ def registrationProcessRetry(
+ promise: Promise[Any],
+ exception: Throwable,
+ obj: IdentifiableEntity,
+ hub: NumberPoolHub,
+ pools: Map[String, ActorRef],
+ poolName: String
+ ): Unit = {
+ if (poolName.equals("generic")) {
+ promise.failure(new RegisteringException(msg = s"did not register entity $obj", exception))
+ } else {
+ org.log4s.getLogger("UniqueNumberOps").warn(s"${exception.getLocalizedMessage()} - $poolName")
+ promise.completeWith(registrationProcess(obj, guid, pools, poolName = "generic"))
+ }
+ }
+
+ /**
+ * A step of the entity GUID unregistration process.
+ * Pass control through to the next step.
+ * @see `unregistrationProcess(IdentifiableEntity, NumberPoolHub, Map[String, ActorRef], Int, String)`
+ * @param obj the entity to be unassigned its GUID
+ * @param number the number that was previously drawn from the specified `NumberPool`
+ * @param poolName the pool to which the number will try to be returned
+ * @return the anticipation of this activity being completed
+ */
+ private def unregistrationProcess(
+ obj: IdentifiableEntity,
+ number: Int,
+ poolName: String
+ ): Future[Any] = {
+ unregistrationProcess(obj, guid, poolActors, number, poolName)
+ }
+
+ /**
+ * A step of the entity GUID unregistration process.
+ * ...
+ * @param obj the entity to be unassigned its GUID
+ * @param hub the supporting datatype for the unique number distribution
+ * @param pools a mapping created from the `NumberPool`s, to achieve synchronized access
+ * @param number the number that was previously drawn from the specified `NumberPool`
+ * @param poolName the pool to which the number will try to be returned
+ * @return the anticipation of this activity being completed
+ */
+ private def unregistrationProcess(
+ obj: IdentifiableEntity,
+ hub: NumberPoolHub,
+ pools: Map[String, ActorRef],
+ number: Int,
+ poolName: String
+ ): Future[Any] = {
+ val promisingResult: Promise[Any] = Promise()
+ pools.get(poolName) match {
+ case Some(pool) =>
+ //cache
+ val localPromise = promisingResult
+ val localTarget = obj
+ val localUns = hub
+ val localPoolName = poolName
+ val localPool = pool
+ val localNumber = number
+
+ val result = ask(pool, NumberPoolActor.ReturnNumber(number))
+ result.onComplete {
+ case Success(NumberPoolActor.ReturnNumberResult(_, None)) =>
+ UniqueNumberOps.processUnregisterResult(
+ localPromise,
+ localTarget,
+ localUns,
+ localPoolName,
+ localPool,
+ localNumber
+ )
+ case Success(NumberPoolActor.ReturnNumberResult(_, Some(ex))) => //if there is a problem when returning the number
+ localPromise.failure { new UnregisteringException(msg = s"could not unregister $localTarget with number $localNumber", ex) }
+ case msg =>
+ UniqueNumberOps.log.warn(s"unexpected message $msg during $localTarget's unregistration process")
+ }
+ result.recover {
+ case ex: AskTimeoutException =>
+ localPromise.failure { new UnregisteringException(msg = s"did not unregister entity $localTarget in time", ex) }
+ }
+
+ case None =>
+ //do not log; use callback
+ promisingResult.failure { new UnregisteringException(msg = s"can not find pool $poolName; $obj was not unregistered") }
+ }
+ promisingResult.future
+ }
+
+ /**
+ * Generate a relevant logging message for an object that is trying to register to this UNS
+ * but is actually already registered to this UNS.
+ * Also take note if the entity is (probably) not registered to this UNS.
+ * @param obj the object that was trying to register
+ * @param poolName the pool to which the object was trying to register
+ * @return the pool name to which the entity is registered, if it can be discovered
+ */
+ private def alreadyRegisteredTo(obj: IdentifiableEntity, poolName: String): Option[String] = {
+ val (msg, determinedName) =
+ guid.WhichPool(obj) match {
+ case out @ Some(pname) =>
+ if (poolName.equals(pname)) {
+ (s"to pool $poolName", Some(poolName))
+ } else {
+ (s"but to different pool $pname", out)
+ }
+ case None =>
+ ("but not to any pool known to this system", None)
+ }
+ UniqueNumberOps.log.warn(s"$obj already registered $msg")
+ determinedName
+ }
+}
+
+object UniqueNumberOps {
+ private val log = org.log4s.getLogger
+ private implicit val timeout = Timeout(2.seconds)
+
+ /**
+ * Final step of the object registration process.
+ * This step completes the registration by asking the `NumberPoolHub` to sort out its `NumberSource`.
+ * @param promise the ongoing promise to be fulfilled for the future
+ * @param obj the entity to be assigned a GUID
+ * @param guid the supporting datatype for the unique number distribution
+ * @param poolName the name of the pool to which the object is trying to register
+ * @param pool the pool to which the object is trying to register
+ * @param number the number that was drawn
+ */
+ private def processRegisterResult(
+ promise: Promise[Any],
+ obj: IdentifiableEntity,
+ guid: NumberPoolHub,
+ poolName: String,
+ pool: ActorRef,
+ number: Int
+ ): Unit = {
+ guid.latterPartRegister(obj, number) match {
+ case Success(_) =>
+ promise.success(RegisteredEntity(obj, poolName, guid, number))
+ case Failure(ex) =>
+ //do not log; use callback
+ returnNumberNoCallback(number, pool) //recovery?
+ promise.failure(ex)
+ }
+ }
+
+ /**
+ * A step of the object unregistration process.
+ * This step completes revoking of the object's registration by consulting the `NumberSource`.
+ * @param promise the ongoing promise to be fulfilled for the future
+ * @param obj the entity to be unassigned its GUID
+ * @param guid the supporting datatype for the unique number distribution
+ * @param poolName the name of pool to which the number will try to be returned
+ * @param pool the pool to which the number will try to be returned
+ * @param number the number that was previously drawn from the specified `NumberPool`
+ */
+ private def processUnregisterResult(
+ promise: Promise[Any],
+ obj: IdentifiableEntity,
+ guid: NumberPoolHub,
+ poolName: String,
+ pool: ActorRef,
+ number: Int
+ ): Unit = {
+ guid.latterPartUnregister(number) match {
+ case Some(_) =>
+ obj.Invalidate()
+ promise.success(UnregisteredEntity(obj, poolName, guid, number))
+ case None =>
+ //do not log
+ requestSpecificNumberNoCallback(number, pool) //recovery?
+ promise.failure(new UnregisteringException(msg = s"failed to unregister $obj from number $number; this may be a critical error"))
+ }
+ }
+
+ /**
+ * Access a specific `NumberPool` in a way that doesn't propagate a callback and reset one of its numbers.
+ * The `ask` pattern catches any reply message and ensures nothing happens because of it.
+ * @param number the number that was drawn from a `NumberPool`
+ * @param pool the `NumberPool` from which the `number` was drawn
+ */
+ private def returnNumberNoCallback(number: Int, pool: ActorRef): Unit = {
+ val result = ask(pool, NumberPoolActor.ReturnNumber(number))
+ result.onComplete { _ => ; }
+ result.recover { case _ => ; }
+ }
+
+ /**
+ * Access a specific `NumberPool` in a way that doesn't propagate a callback and claim one of its numbers.
+ * The `ask` pattern catches any reply message and ensures nothing happens because of it.
+ * @param number the number to be drawn from a `NumberPool`
+ * @param pool the `NumberPool` from which the `number` is to be drawn
+ */
+ private def requestSpecificNumberNoCallback(number: Int, pool: ActorRef): Unit = {
+ val result = ask(pool, NumberPoolActor.GetSpecificNumber(number))
+ result.onComplete { _ => ; }
+ result.recover { case _ => ; }
+ }
+}
+
+class RegisteringException(msg: String) extends Exception(msg) {
+ def this(msg: String, cause: Throwable) = {
+ this(msg)
+ initCause(cause)
+ }
+}
+
+class UnregisteringException(msg: String) extends Exception(msg) {
+ def this(msg: String, cause: Throwable) = {
+ this(msg)
+ initCause(cause)
+ }
+}
+
+/**
+ * The entity was registered, but not to the target UNS.
+ * Rookie mistake.
+ * @param obj the entity to be assigned a GUID
+ * @param number the name associated with this entity
+ */
+class RegisteredToWrongPlaceException(obj: IdentifiableEntity, number: Int)
+ extends RuntimeException(s"$obj registered to number $number that is not part of a known or local number pool") {
+
+ def this(obj: IdentifiableEntity, number: Int, cause: Throwable) = {
+ this(obj, number)
+ initCause(cause)
+ }
+}
+
+/**
+ * A class for spawning `Actor`s to manage the number pools and
+ * create a number system operations class to access those pools within the context of registering and unregistering.
+ * This `Actor` persists to maintain the number pool `Actor`s.
+ * Note the `final` do-nothing `receive` method.
+ * This `Actor` should do __nothing__ through message passing.
+ * @see `UniqueNumberOps`
+ * @param hub the number pool management class
+ * @param poolActorConversionFunc the number pool management class
+ */
+class UniqueNumberSetup(
+ hub: NumberPoolHub,
+ poolActorConversionFunc: (ActorContext, NumberPoolHub) => Map[String, ActorRef]
+ ) extends Actor {
+ init()
+
+ final def receive: Receive = { case _ => ; }
+
+ def init(): UniqueNumberOps = {
+ new UniqueNumberOps(hub, poolActorConversionFunc(context, hub))
+ }
+}
+
+object UniqueNumberSetup {
+
+ /**
+ * Transform `NumberPool`s into `NumberPoolActor`s and pair them with their name.
+ * @param poolSource where the raw `NumberPools` are located
+ * @param context used to create the `NumberPoolActor` instances
+ * @return a `Map` of the pool names to the `ActorRef` created from the `NumberPool`
+ */
+ def AllocateNumberPoolActors(context: ActorContext, poolSource: NumberPoolHub): Map[String, ActorRef] = {
+ poolSource.Pools
+ .map { case (pname, pool) => (pname, context.actorOf(Props(classOf[NumberPoolActor], pool), pname)) }
+ .toMap
+ }
+}
diff --git a/src/main/scala/net/psforever/objects/guid/actor/Register.scala b/src/main/scala/net/psforever/objects/guid/actor/Register.scala
deleted file mode 100644
index adc37280..00000000
--- a/src/main/scala/net/psforever/objects/guid/actor/Register.scala
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.guid.actor
-
-import akka.actor.ActorRef
-import net.psforever.objects.entity.IdentifiableEntity
-
-/**
- * A message for accepting object-number registration requests.
- *
- * The callback is actually an `ActorRef` to which a `RegisterSuccess` message or a `RegisterFailure` message is sent.
- * This is as opposed to what a "callback" is normally - a function.
- * @param obj the mandatory object
- * @param name the optional name of the number pool to which this object is registered
- * @param number the optional number pre-selected for registering this object
- * @param callback the optional custom callback for the messages from the success or failure conditions
- */
-final case class Register(
- obj: IdentifiableEntity,
- name: Option[String],
- number: Option[Int],
- callback: Option[ActorRef]
-)
-
-object Register {
-
- /**
- * Overloaded constructor, accepting just the object.
- * @param obj the object to be registered
- * @return a `Register` object
- */
- def apply(obj: IdentifiableEntity): Register = {
- new Register(obj, None, None, None)
- }
-
- /**
- * Overloaded constructor, accepting the object and a callback.
- * @param obj the object to be registered
- * @param callback the custom callback for the messages from the success or failure conditions
- * @return a `Register` object
- */
- def apply(obj: IdentifiableEntity, callback: ActorRef): Register = {
- new Register(obj, None, None, Some(callback))
- }
-
- /**
- * Overloaded constructor, accepting an object and a pre-selected number.
- * @param obj the object to be registered
- * @param number the pre-selected number
- * @return a `Register` object
- */
- def apply(obj: IdentifiableEntity, number: Int): Register = {
- new Register(obj, None, Some(number), None)
- }
-
- /**
- * Overloaded constructor, accepting an object, a pre-selected number, and a callback.
- * @param obj the object to be registered
- * @param number the pre-selected number
- * @param callback the custom callback for the messages from the success or failure conditions
- * @return a `Register` object
- */
- def apply(obj: IdentifiableEntity, number: Int, callback: ActorRef): Register = {
- new Register(obj, None, Some(number), Some(callback))
- }
-
- /**
- * Overloaded constructor, accepting an object and a number pool.
- * @param obj the object to be registered
- * @param name the number pool name
- * @return a `Register` object
- */
- def apply(obj: IdentifiableEntity, name: String): Register = {
- new Register(obj, Some(name), None, None)
- }
-
- /**
- * Overloaded constructor, accepting an object, a number pool, and a callback.
- * @param obj the object to be registered
- * @param name the number pool name
- * @param callback the custom callback for the messages from the success or failure conditions
- * @return a `Register` object
- */
- def apply(obj: IdentifiableEntity, name: String, callback: ActorRef): Register = {
- new Register(obj, Some(name), None, Some(callback))
- }
-}
diff --git a/src/main/scala/net/psforever/objects/guid/actor/UniqueNumberSystem.scala b/src/main/scala/net/psforever/objects/guid/actor/UniqueNumberSystem.scala
deleted file mode 100644
index 06f0eca9..00000000
--- a/src/main/scala/net/psforever/objects/guid/actor/UniqueNumberSystem.scala
+++ /dev/null
@@ -1,348 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.guid.actor
-
-import akka.actor.{Actor, ActorContext, ActorRef, Props}
-import net.psforever.objects.entity.IdentifiableEntity
-import net.psforever.objects.guid.NumberPoolHub
-
-import scala.util.{Failure, Success}
-
-/**
- * An `Actor` that wraps around converted `NumberPool`s and synchronizes a portion of the number registration process.
- * The ultimate goal is to manage a coherent group of unique identifiers for a given "region" (`Zone`).
- * Both parts of the UID system sit atop the `Zone` for easy external access.
- * The plain part - the `NumberPoolHub` here - is used for low-priority requests such as checking for existing associations.
- * This `Actor` is the involved portion that paces registration and unregistration.
- *
- * A four part process is used for object registration tasks.
- * First, the requested `NumberPool` is located among the list of known `NumberPool`s.
- * Second, an asynchronous request is sent to that pool to retrieve a number.
- * (Only any number. Only a failing case allows for selection of a specific number.)
- * Third, the asynchronous request returns and the original information about the request is recovered.
- * Fourth, both sides of the contract are completed by the object being assigned the number and
- * the underlying "number source" is made to remember an association between the object and the number.
- * Short circuits and recoveries as available on all steps though reporting is split between logging and callbacks.
- * The process of removing the association between a number and object (unregistering) is a similar four part process.
- *
- * The important relationship between this `Actor` and the `Map` of `NumberPoolActors` is an "gate."
- * A single `Map` is constructed and shared between multiple entry points to the UID system where requests are messaged.
- * Multiple entry points send messages to the same `NumberPool`.
- * That `NumberPool` deals with the messages one at a time and sends reply to each entry point that communicated with it.
- * This process is almost as fast as the process of the `NumberPool` selecting a number.
- * (At least, both should be fast.)
- * @param guid the `NumberPoolHub` that is partially manipulated by this `Actor`
- * @param poolActors a common mapping created from the `NumberPool`s in `guid`;
- * there is currently no check for this condition save for requests failing
- */
-class UniqueNumberSystem(private val guid: NumberPoolHub, private val poolActors: Map[String, ActorRef]) extends Actor {
-
- /** Information about Register and Unregister requests that persists between messages to a specific `NumberPool`. */
- private val requestQueue: collection.mutable.LongMap[UniqueNumberSystem.GUIDRequest] =
- new collection.mutable.LongMap()
-
- /** The current value for the next request entry's index. */
- private var index: Long = Long.MinValue
- private[this] val log = org.log4s.getLogger
-
- def receive: Receive = {
- case Register(obj, Some(pname), None, call) =>
- val callback = call.getOrElse(sender())
- if (obj.HasGUID) {
- AlreadyRegistered(obj, pname)
- callback ! Success(obj)
- } else {
- val id: Long = index
- index += 1
- requestQueue += id -> UniqueNumberSystem.GUIDRequest(obj, pname, callback)
- RegistrationProcess(pname, id)
- }
-
- //this message is automatically sent by NumberPoolActor
- case NumberPoolActor.GiveNumber(number, id) =>
- id match {
- case Some(nid: Long) =>
- RegistrationProcess(requestQueue.remove(nid), number, nid)
- case _ =>
- log.warn(s"received a number but there is no request to process it; returning number to pool")
- NoCallbackReturnNumber(number) //recovery?
- //no callback is possible
- }
-
- //this message is automatically sent by NumberPoolActor
- case NumberPoolActor.NoNumber(ex, id) =>
- id match {
- case Some(nid: Long) =>
- requestQueue.remove(nid) match {
- case Some(entry) =>
- entry.replyTo ! Failure(ex) //ONLY callback that is possible
- case None => ;
- log.warn(
- s"failed number request and no record of number request - $ex"
- ) //neither a successful request nor an entry of making the request
- }
- case None => ;
- log.warn(
- s"failed number request and no record of number request - $ex"
- ) //neither a successful request nor an entry of making the request
- case _ => ;
- log.warn(s"unrecognized request $id accompanying a failed number request - $ex")
- }
-
- case Unregister(obj, call) =>
- val callback = call.getOrElse(sender())
- try {
- val number = obj.GUID.guid
- guid.WhichPool(number) match {
- case Some(pname) =>
- val id: Long = index
- index += 1
- requestQueue += id -> UniqueNumberSystem.GUIDRequest(obj, pname, callback)
- UnregistrationProcess(pname, number, id)
- case None =>
- callback ! Failure(new Exception(s"the GUID of object $obj - $number - is not a part of this number pool"))
- }
- } catch {
- case _: Exception =>
- log.warn(s"$obj is already unregistered")
- callback ! Success(obj)
- }
-
- //this message is automatically sent by NumberPoolActor
- case NumberPoolActor.ReturnNumberResult(number, None, id) =>
- id match {
- case Some(nid: Long) =>
- UnregistrationProcess(requestQueue.remove(nid), number, nid)
- case _ =>
- log.error(s"returned a number but there is no request to process it; recovering the number from pool")
- NoCallbackGetSpecificNumber(number) //recovery?
- //no callback is possible
- }
-
- //this message is automatically sent by NumberPoolActor
- case NumberPoolActor.ReturnNumberResult(number, Some(ex), id) => //if there is a problem when returning the number
- id match {
- case Some(nid: Long) =>
- requestQueue.remove(nid) match {
- case Some(entry) =>
- entry.replyTo ! Failure(new Exception(s"for ${entry.target} with number $number, ${ex.getMessage}"))
- case None => ;
- log.error(s"could not find original request $nid that caused error $ex, but pool was ${sender()}")
- //no callback is possible
- }
- case _ => ;
- log.error(s"could not find original request $id that caused error $ex, but pool was ${sender()}")
- //no callback is possible
- }
-
- case msg =>
- log.warn(s"unexpected message received - $msg")
- }
-
- /**
- * A step of the object registration process.
- * Send a message to the `NumberPool` to request a number back.
- * @param poolName the pool to which the object is trying to register
- * @param id a potential identifier to associate this request
- */
- private def RegistrationProcess(poolName: String, id: Long): Unit = {
- poolActors.get(poolName) match {
- case Some(pool) =>
- pool ! NumberPoolActor.GetAnyNumber(Some(id))
- case None =>
- //do not log; use callback
- requestQueue.remove(id).get.replyTo ! Failure(
- new Exception(s"can not find pool $poolName; nothing was registered")
- )
- }
- }
-
- /**
- * A step of the object registration process.
- * If there is a successful request object to be found, continue the registration request.
- * @param request the original request data
- * @param number the number that was drawn from a `NumberPool`
- */
- private def RegistrationProcess(request: Option[UniqueNumberSystem.GUIDRequest], number: Int, id: Long): Unit = {
- request match {
- case Some(entry) =>
- processRegisterResult(entry, number)
- case None =>
- log.error(s"returned a number but the rest of the request is missing (id:$id)")
- if (id != Long.MinValue) { //check to ignore endless loop of error-catching
- log.warn("returning number to pool")
- NoCallbackReturnNumber(number) //recovery?
- //no callback is possible
- }
- }
- }
-
- /**
- * A step of the object registration process.
- * This step completes the registration by asking the `NumberPoolHub` to sort out its `NumberSource`.
- * @param entry the original request data
- * @param number the number to use
- */
- private def processRegisterResult(entry: UniqueNumberSystem.GUIDRequest, number: Int): Unit = {
- val obj = entry.target
- guid.latterPartRegister(obj, number) match {
- case Success(_) =>
- entry.replyTo ! Success(obj)
- case Failure(ex) =>
- //do not log; use callback
- NoCallbackReturnNumber(number, entry.targetPool) //recovery?
- entry.replyTo ! Failure(ex)
- }
- }
-
- /**
- * A step of the object unregistration process.
- * Send a message to the `NumberPool` to restore the availability of one of its numbers.
- * @param poolName the pool to which the number will try to be returned
- * @param number the number that was previously drawn from the specified `NumberPool`
- * @param id a potential identifier to associate this request
- */
- private def UnregistrationProcess(poolName: String, number: Int, id: Long): Unit = {
- poolActors.get(poolName) match {
- case Some(pool) =>
- pool ! NumberPoolActor.ReturnNumber(number, Some(id))
- case None =>
- //do not log; use callback
- requestQueue.remove(id).get.replyTo ! Failure(
- new Exception(s"can not find pool $poolName; nothing was de-registered")
- )
- }
- }
-
- /**
- * A step of the object unregistration process.
- * If there is a successful request object to be found, continue the registration request.
- * @param request the original request data
- * @param number the number that was drawn from the `NumberPool`
- */
- private def UnregistrationProcess(request: Option[UniqueNumberSystem.GUIDRequest], number: Int, id: Long): Unit = {
- request match {
- case Some(entry) =>
- processUnregisterResult(entry, number)
- case None =>
- log.error(s"returned a number but the rest of the request is missing (id:$id)")
- if (id != Long.MinValue) { //check to ignore endless loop of error-catching
- log.error("recovering the number from pool")
- NoCallbackGetSpecificNumber(number) //recovery?
- //no callback is possible
- }
- }
- }
-
- /**
- * A step of the object unregistration process.
- * This step completes revoking of the object's registration by consulting the `NumberSource`.
- * @param entry the original request data
- * @param number the number to use
- */
- private def processUnregisterResult(entry: UniqueNumberSystem.GUIDRequest, number: Int): Unit = {
- val obj = entry.target
- guid.latterPartUnregister(number) match {
- case Some(_) =>
- obj.Invalidate()
- entry.replyTo ! Success(obj)
- case None =>
- //do not log; use callback
- NoCallbackGetSpecificNumber(number, entry.targetPool) //recovery?
- entry.replyTo ! Failure(new Exception(s"failed to unregister a number; this may be a critical error"))
- }
- }
-
- /**
- * Generate a relevant logging message for an object that is trying to register is actually already registered.
- * @param obj the object that was trying to register
- * @param poolName the pool to which the object was trying to register
- */
- private def AlreadyRegistered(obj: IdentifiableEntity, poolName: String): Unit = {
- val msg =
- guid.WhichPool(obj) match {
- case Some(pname) =>
- if (poolName.equals(pname)) {
- s"to pool $poolName"
- } else {
- s"but to different pool $pname"
- }
- case None =>
- "but not to any pool known to this system"
- }
- log.warn(s"$obj already registered $msg")
- }
-
- /**
- * Access a specific `NumberPool` in a way that doesn't invoke a callback and reset one of its numbers.
- * @param number the number that was drawn from a `NumberPool`
- */
- private def NoCallbackReturnNumber(number: Int): Unit = {
- guid.WhichPool(number) match {
- case Some(pname) =>
- NoCallbackReturnNumber(number, pname)
- case None =>
- log.error(s"critical: tried to return number $number but could not find containing pool")
- }
- }
-
- /**
- * Access a specific `NumberPool` in a way that doesn't invoke a callback and reset one of its numbers.
- * To avoid fully processing the callback, an id of `Long.MinValue` is used to short circuit the routine.
- * @param number the number that was drawn from a `NumberPool`
- * @param poolName the `NumberPool` from which the `number` was drawn
- * @see `UniqueNumberSystem.UnregistrationProcess(Option[GUIDRequest], Int, Int)`
- */
- private def NoCallbackReturnNumber(number: Int, poolName: String): Unit = {
- poolActors(poolName) ! NumberPoolActor.ReturnNumber(number, Some(Long.MinValue))
- }
-
- /**
- * Access a specific `NumberPool` in a way that doesn't invoke a callback and claim one of its numbers.
- * @param number the number to be drawn from a `NumberPool`
- */
- private def NoCallbackGetSpecificNumber(number: Int): Unit = {
- guid.WhichPool(number) match {
- case Some(pname) =>
- NoCallbackGetSpecificNumber(number, pname)
- case None =>
- log.error(s"critical: tried to re-register number $number but could not find containing pool")
- }
- }
-
- /**
- * Access a specific `NumberPool` in a way that doesn't invoke a callback and claim one of its numbers.
- * To avoid fully processing the callback, an id of `Long.MinValue` is used to short circuit the routine.
- * @param number the number to be drawn from a `NumberPool`
- * @param poolName the `NumberPool` from which the `number` is to be drawn
- * @see `UniqueNumberSystem.RegistrationProcess(Option[GUIDRequest], Int, Int)`
- */
- private def NoCallbackGetSpecificNumber(number: Int, poolName: String): Unit = {
- poolActors(poolName) ! NumberPoolActor.GetSpecificNumber(number, Some(Long.MinValue))
- }
-}
-
-object UniqueNumberSystem {
-
- /**
- * Persistent record of the important information between the time fo request and the time of reply.
- * @param target the object
- * @param targetPool the name of the `NumberPool` being used
- * @param replyTo the callback `ActorRef`
- */
- private final case class GUIDRequest(target: IdentifiableEntity, targetPool: String, replyTo: ActorRef)
-
- /**
- * Transform `NumberPool`s into `NumberPoolActor`s and pair them with their name.
- * @param poolSource where the raw `NumberPools` are located
- * @param context used to create the `NumberPoolActor` instances
- * @return a `Map` of the pool names to the `ActorRef` created from the `NumberPool`
- */
- def AllocateNumberPoolActors(poolSource: NumberPoolHub)(implicit context: ActorContext): Map[String, ActorRef] = {
- poolSource.Pools
- .map({
- case ((pname, pool)) =>
- pname -> context.actorOf(Props(classOf[NumberPoolActor], pool), pname)
- })
- .toMap
- }
-}
diff --git a/src/main/scala/net/psforever/objects/guid/actor/Unregister.scala b/src/main/scala/net/psforever/objects/guid/actor/Unregister.scala
deleted file mode 100644
index c11c7212..00000000
--- a/src/main/scala/net/psforever/objects/guid/actor/Unregister.scala
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.guid.actor
-
-import akka.actor.ActorRef
-import net.psforever.objects.entity.IdentifiableEntity
-
-/**
- * A message for accepting object-number unregistration requests.
- * When given to a number pool (`NumberPoolAccessorActor`), that `Actor` assumes itself to have the object.
- * When given to a hub object (`NumberPoolHubActor`), it will attempt to determine which pool currently has the object.
- *
- * The callback is actually an `ActorRef` to which a `RegisterSuccess` message or a `RegisterFailure` message is sent.
- * This is as opposed to what a "callback" is normally - a function.
- * @param obj the mandatory object
- * @param callback the optional custom callback for the messages from the success or failure conditions
- */
-final case class Unregister(obj: IdentifiableEntity, callback: Option[ActorRef] = None)
-
-object Unregister {
- def apply(obj: IdentifiableEntity, callback: ActorRef): Unregister = {
- Unregister(obj, Some(callback))
- }
-}
diff --git a/src/main/scala/net/psforever/objects/guid/key/AvailabilityPolicy.scala b/src/main/scala/net/psforever/objects/guid/key/AvailabilityPolicy.scala
new file mode 100644
index 00000000..b2d3f11e
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/guid/key/AvailabilityPolicy.scala
@@ -0,0 +1,19 @@
+// Copyright (c) 2017 PSForever
+package net.psforever.objects.guid.key
+
+/**
+ * The availability of individual global unique identifier (GUID) keys is maintained by the given policy.
+ */
+sealed trait AvailabilityPolicy
+
+object AvailabilityPolicy {
+ /**An `Available` key is ready and waiting to be `Leased` for use. */
+ case object Available extends AvailabilityPolicy
+
+ /** A `Leased` key has been issued and is currently being used for some purpose.*/
+ case object Leased extends AvailabilityPolicy
+
+ /** A `Dangling` key ia a unique sort of key that has been `Leased` but has not yet been applied for any specific purpose.
+ * As a policy, it should be used as a status to check but should not be designated on any key. */
+ case object Dangling extends AvailabilityPolicy
+}
diff --git a/src/main/scala/net/psforever/objects/guid/key/LoanedKey.scala b/src/main/scala/net/psforever/objects/guid/key/LoanedKey.scala
index 7af04d9c..00ee381b 100644
--- a/src/main/scala/net/psforever/objects/guid/key/LoanedKey.scala
+++ b/src/main/scala/net/psforever/objects/guid/key/LoanedKey.scala
@@ -2,7 +2,6 @@
package net.psforever.objects.guid.key
import net.psforever.objects.entity.IdentifiableEntity
-import net.psforever.objects.guid.AvailabilityPolicy
/**
* The only indirect public access a queued number monitor object (`Key`) is allowed.
@@ -12,7 +11,7 @@ import net.psforever.objects.guid.AvailabilityPolicy
class LoanedKey(private val guid: Int, private val key: Monitor) {
def GUID: Int = guid
- def Policy: AvailabilityPolicy.Value = key.policy
+ def Policy: AvailabilityPolicy = key.policy
def Object: Option[IdentifiableEntity] = key.obj
@@ -29,9 +28,7 @@ class LoanedKey(private val guid: Int, private val key: Monitor) {
* @return `true`, if the assignment worked; `false`, otherwise
*/
def Object_=(obj: Option[IdentifiableEntity]): Option[IdentifiableEntity] = {
- if (
- key.policy == AvailabilityPolicy.Leased || (key.policy == AvailabilityPolicy.Restricted && key.obj.isEmpty)
- ) {
+ if (key.policy == AvailabilityPolicy.Leased) {
if (key.obj.isDefined) {
key.obj.get.Invalidate()
key.obj = None
diff --git a/src/main/scala/net/psforever/objects/guid/key/Monitor.scala b/src/main/scala/net/psforever/objects/guid/key/Monitor.scala
index e7cc1506..feb999b5 100644
--- a/src/main/scala/net/psforever/objects/guid/key/Monitor.scala
+++ b/src/main/scala/net/psforever/objects/guid/key/Monitor.scala
@@ -2,10 +2,9 @@
package net.psforever.objects.guid.key
import net.psforever.objects.entity.IdentifiableEntity
-import net.psforever.objects.guid.AvailabilityPolicy
trait Monitor {
- var policy: AvailabilityPolicy.Value
+ var policy: AvailabilityPolicy
var obj: Option[IdentifiableEntity]
}
diff --git a/src/main/scala/net/psforever/objects/guid/key/SecureKey.scala b/src/main/scala/net/psforever/objects/guid/key/SecureKey.scala
index 8c333e2e..2c238047 100644
--- a/src/main/scala/net/psforever/objects/guid/key/SecureKey.scala
+++ b/src/main/scala/net/psforever/objects/guid/key/SecureKey.scala
@@ -1,8 +1,6 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.guid.key
-import net.psforever.objects.guid.AvailabilityPolicy
-
/**
* An unmodifiable reference to an active number monitor object (`Key`).
* @param guid the number (globally unique identifier)
@@ -11,7 +9,7 @@ import net.psforever.objects.guid.AvailabilityPolicy
final class SecureKey(private val guid: Int, private val key: Monitor) {
def GUID: Int = guid
- def Policy: AvailabilityPolicy.Value = key.policy
+ def Policy: AvailabilityPolicy = key.policy
import net.psforever.objects.entity.IdentifiableEntity
def Object: Option[IdentifiableEntity] = key.obj
diff --git a/src/main/scala/net/psforever/objects/guid/pool/GenericPool.scala b/src/main/scala/net/psforever/objects/guid/pool/GenericPool.scala
index 4ea78a5c..9a77aa7c 100644
--- a/src/main/scala/net/psforever/objects/guid/pool/GenericPool.scala
+++ b/src/main/scala/net/psforever/objects/guid/pool/GenericPool.scala
@@ -6,8 +6,13 @@ import net.psforever.objects.guid.selector.{NumberSelector, SpecificSelector}
import scala.collection.mutable
import scala.util.{Failure, Success, Try}
-class GenericPool(private val hub: mutable.LongMap[String], private val max: Int) extends NumberPool {
- val numbers: mutable.ListBuffer[Int] = mutable.ListBuffer[Int]()
+class GenericPool(
+ private val hub: mutable.LongMap[String],
+ private val max: Int,
+ private val poolName: String,
+ private val selectionFunc: (List[Long], Int) => Int
+ ) extends NumberPool {
+ private val numbers: mutable.ListBuffer[Int] = mutable.ListBuffer[Int]()
private val selector: SpecificSelector = new SpecificSelector
selector.SelectionIndex = -1
@@ -17,22 +22,26 @@ class GenericPool(private val hub: mutable.LongMap[String], private val max: Int
def Selector: NumberSelector = selector
- def Selector_=(slctr: NumberSelector): Unit = {} //intentionally blank
+ def Selector_=(slctr: NumberSelector): Unit = { /* intentionally blank */ }
def Get(): Try[Int] = {
val specific = selector.SelectionIndex
selector.SelectionIndex = -1 //clear
if (specific == -1) {
- val number = GenericPool.rand(hub.keys.toList, max)
- hub += number.toLong -> "generic"
- numbers += number
- Success(number)
+ val number = selectionFunc(hub.keys.toList, max)
+ if (number > -1) {
+ hub += number.toLong -> poolName
+ numbers += number
+ Success(number)
+ } else {
+ Failure(new Exception("no numbers available in this pool"))
+ }
} else if (hub.get(specific).isEmpty) {
- hub += specific.toLong -> "generic"
+ hub += specific.toLong -> poolName
numbers += specific
Success(specific)
} else {
- Failure(new Exception("selector was not initialized properly, or no numbers available in the pool"))
+ Failure(new Exception("selector may not have been initialized properly"))
}
}
@@ -49,6 +58,43 @@ class GenericPool(private val hub: mutable.LongMap[String], private val max: Int
}
object GenericPool {
+ /**
+ * Overloaded constructor that assigns a "numerical first discovery" function for number selection.
+ * @param hub na
+ * @param max na
+ * @param poolName na
+ * @return a `GenericPool` entity
+ */
+ def apply(
+ hub: mutable.LongMap[String],
+ max: Int,
+ poolName: String
+ ): GenericPool =
+ new GenericPool(hub, max, poolName, GenericPool.first)
+
+ /**
+ * Get some number that is not accounted for in any other fixed pool, making it available in this generic one.
+ *
+ * Returns the first number that is detected as available between two sorted numbers.
+ * @param list all of the non-repeating numbers to be compared
+ * @param domainSize how many numbers can be supported
+ * @return the next available number, or -1
+ */
+ def first(list: List[Long], domainSize: Int): Int = {
+ if (list.size < domainSize) {
+ val sortedList: List[Long] = 0L +: list.sorted :+ domainSize
+ var index: Int = 0
+ val listLen = sortedList.length - 1
+ while(index < listLen && index < domainSize) {
+ val curr = sortedList(index + 1) - sortedList(index)
+ if (curr > 1) {
+ return sortedList(index).toInt + 1
+ }
+ index += 1
+ }
+ }
+ -1
+ }
/**
* Get some number that is not accounted for in any other fixed pool, making it available in this generic one.
@@ -63,7 +109,7 @@ object GenericPool {
* @param domainSize how many numbers can be supported
* @return midpoint of the largest distance between any two of the existing numbers, or -1
*/
- private def rand(list: List[Long], domainSize: Int): Int = {
+ def rand(list: List[Long], domainSize: Int): Int = {
if (list.size < domainSize) {
//get a list of all assigned numbers with an appended min and max
val sortedList: List[Long] = -1L +: list.sorted :+ domainSize.toLong
@@ -78,7 +124,8 @@ object GenericPool {
}
}
//find half of the distance between the two numbers with the greatest delta value
- if (maxDelta > 1) { ((sortedList(maxDeltaIndex + 1) + sortedList(maxDeltaIndex)) / 2f).toInt }
+ if (maxDelta == 2) { sortedList(maxDeltaIndex).toInt + 1 }
+ else if (maxDelta > 1) { ((sortedList(maxDeltaIndex + 1) + sortedList(maxDeltaIndex)) / 2f).toInt }
else { -1 }
} else {
-1
diff --git a/src/main/scala/net/psforever/objects/guid/source/Key.scala b/src/main/scala/net/psforever/objects/guid/source/Key.scala
index ae6be828..c518eb38 100644
--- a/src/main/scala/net/psforever/objects/guid/source/Key.scala
+++ b/src/main/scala/net/psforever/objects/guid/source/Key.scala
@@ -2,11 +2,10 @@
package net.psforever.objects.guid.source
import net.psforever.objects.entity.IdentifiableEntity
-import net.psforever.objects.guid.AvailabilityPolicy
-import net.psforever.objects.guid.key.Monitor
+import net.psforever.objects.guid.key.{AvailabilityPolicy, Monitor}
private class Key extends Monitor {
- var policy: AvailabilityPolicy.Value = AvailabilityPolicy.Available
+ var policy: AvailabilityPolicy = AvailabilityPolicy.Available
var obj: Option[IdentifiableEntity] = None
}
diff --git a/src/main/scala/net/psforever/objects/guid/source/MaxNumberSource.scala b/src/main/scala/net/psforever/objects/guid/source/MaxNumberSource.scala
index 387185fa..47bcd229 100644
--- a/src/main/scala/net/psforever/objects/guid/source/MaxNumberSource.scala
+++ b/src/main/scala/net/psforever/objects/guid/source/MaxNumberSource.scala
@@ -2,8 +2,7 @@
package net.psforever.objects.guid.source
import net.psforever.objects.entity.IdentifiableEntity
-import net.psforever.objects.guid.key.{LoanedKey, SecureKey}
-import net.psforever.objects.guid.AvailabilityPolicy
+import net.psforever.objects.guid.key.{AvailabilityPolicy, LoanedKey, SecureKey}
/**
* A `NumberSource` is considered a master "pool" of numbers from which all numbers are available to be drawn.
@@ -18,13 +17,14 @@ class MaxNumberSource(val max: Int) extends NumberSource {
}
private val ary: Array[Key] = Array.ofDim[Key](max + 1)
(0 to max).foreach(x => { ary(x) = new Key })
- private var allowRestrictions: Boolean = true
def size: Int = ary.length
- def countAvailable: Int = ary.count(key => key.policy == AvailabilityPolicy.Available)
+ def countAvailable: Int = ary.count { _.policy == AvailabilityPolicy.Available }
- def countUsed: Int = ary.count(_.policy != AvailabilityPolicy.Available)
+ def countUsed: Int = ary.count { _.policy == AvailabilityPolicy.Leased }
+
+ def countDangling: Int = ary.count { key => key.policy == AvailabilityPolicy.Leased && key.obj.isEmpty }
def test(number: Int): Boolean = -1 < number && number < size
@@ -78,39 +78,14 @@ class MaxNumberSource(val max: Int) extends NumberSource {
out
}
- /**
- * Produce a modifiable wrapper for the `Monitor` for this number, only if the number has not been used.
- * This wrapped `Monitor` can only be assigned once and the number may not be `returnNumber`ed to this source.
- * @param number the number
- * @return the wrapped `Monitor`
- * @throws ArrayIndexOutOfBoundsException if the requested number is above or below the range
- */
- def restrictNumber(number: Int): Option[LoanedKey] = {
- ary.lift(number) match {
- case Some(key: Key) if allowRestrictions && key.policy != AvailabilityPolicy.Restricted =>
- key.policy = AvailabilityPolicy.Restricted
- Some(new LoanedKey(number, key))
- case _ =>
- None
- }
- }
-
- def finalizeRestrictions: List[Int] = {
- allowRestrictions = false
- ary.zipWithIndex.filter(entry => entry._1.policy == AvailabilityPolicy.Restricted).map(entry => entry._2).toList
- }
-
def clear(): List[IdentifiableEntity] = {
- val leased = ary.filter(_.policy != AvailabilityPolicy.Available)
- leased collect { case key if key.obj.isEmpty =>
- key.policy = AvailabilityPolicy.Available
- }
- leased.toList collect { case key if key.obj.nonEmpty =>
- key.policy = AvailabilityPolicy.Available
- val out = key.obj.get
- key.obj = None
- out
- }
+ ary.foreach { _.policy = AvailabilityPolicy.Available }
+ ary.collect {
+ case key if key.obj.nonEmpty =>
+ val obj = key.obj.get
+ key.obj = None
+ obj
+ }.toList
}
}
diff --git a/src/main/scala/net/psforever/objects/guid/source/NumberSource.scala b/src/main/scala/net/psforever/objects/guid/source/NumberSource.scala
index d2e342da..81d7c0a2 100644
--- a/src/main/scala/net/psforever/objects/guid/source/NumberSource.scala
+++ b/src/main/scala/net/psforever/objects/guid/source/NumberSource.scala
@@ -2,7 +2,7 @@
package net.psforever.objects.guid.source
import net.psforever.objects.entity.IdentifiableEntity
-import net.psforever.objects.guid.key.{LoanedKey, SecureKey}
+import net.psforever.objects.guid.key.{AvailabilityPolicy, LoanedKey, SecureKey}
/**
* A `NumberSource` is considered a master "pool" of numbers from which all numbers are available to be drawn.
@@ -30,6 +30,20 @@ trait NumberSource {
*/
def size: Int
+ /**
+ * Select the type of count desired based on the allocation policy of the key.
+ * @param policy the allocation policy
+ * @return the number of keys belonging to this policy
+ */
+ def count(policy: AvailabilityPolicy): Int = {
+ policy match {
+ case AvailabilityPolicy.Available => countAvailable
+ case AvailabilityPolicy.Leased => countUsed
+ case AvailabilityPolicy.Dangling => countDangling
+ }
+ }
+
+
/**
* The count of numbers that can still be drawn.
* @return the count
@@ -42,6 +56,13 @@ trait NumberSource {
*/
def countUsed: Int
+ /**
+ * The count of numbers that can not be drawn but have not yet been assigned to an entity.
+ * Could only ever be a non-zero count if the number of used keys is a non-zero count.
+ * @return the count
+ */
+ def countDangling: Int
+
/**
* Is this number a member of this number source?
* @param number the number
@@ -97,26 +118,9 @@ trait NumberSource {
*/
def returnNumber(number: Int): Option[IdentifiableEntity]
- /**
- * Produce a modifiable wrapper for the `Monitor` for this number, only if the number has not been used.
- * This wrapped `Monitor` can only be assigned once and the number may not be `returnNumber`ed to this source.
- * @param number the number
- * @return the wrapped `Monitor`
- */
- def restrictNumber(number: Int): Option[LoanedKey]
-
- /**
- * Numbers from this source may not longer be marked as `Restricted`.
- * @return the `List` of all numbers that have been restricted
- */
- def finalizeRestrictions: List[Int]
-
- import net.psforever.objects.entity.IdentifiableEntity
-
/**
* Reset all number `Monitor`s so that their underlying number is not longer treated as assigned.
* Perform some level of housecleaning to ensure that all dependencies are resolved in some manner.
- * This is the only way to free `Monitors` that are marked as `Restricted`.
* @return a `List` of assignments maintained by all the currently-used number `Monitors`
*/
def clear(): List[IdentifiableEntity]
diff --git a/src/main/scala/net/psforever/objects/guid/source/SpecificNumberSource.scala b/src/main/scala/net/psforever/objects/guid/source/SpecificNumberSource.scala
index c0284b27..979b77d5 100644
--- a/src/main/scala/net/psforever/objects/guid/source/SpecificNumberSource.scala
+++ b/src/main/scala/net/psforever/objects/guid/source/SpecificNumberSource.scala
@@ -2,8 +2,7 @@
package net.psforever.objects.guid.source
import net.psforever.objects.entity.IdentifiableEntity
-import net.psforever.objects.guid.AvailabilityPolicy
-import net.psforever.objects.guid.key.{LoanedKey, SecureKey}
+import net.psforever.objects.guid.key.{AvailabilityPolicy, LoanedKey, SecureKey}
/**
* A `NumberSource` is considered a master "pool" of numbers from which all numbers are available to be drawn.
@@ -27,9 +26,11 @@ class SpecificNumberSource(values: Iterable[Int]) extends NumberSource {
def size : Int = ary.size
- def countAvailable : Int = ary.values.count(key => key.policy == AvailabilityPolicy.Available)
+ def countAvailable : Int = ary.values.count { _.policy == AvailabilityPolicy.Available }
- def countUsed : Int = ary.values.count(_.policy != AvailabilityPolicy.Available)
+ def countUsed : Int = ary.values.count { _.policy == AvailabilityPolicy.Leased }
+
+ def countDangling: Int = ary.values.count { key => key.policy == AvailabilityPolicy.Leased && key.obj.isEmpty }
def test(number : Int) : Boolean = ary.get(number).nonEmpty
@@ -74,34 +75,14 @@ class SpecificNumberSource(values: Iterable[Int]) extends NumberSource {
}
}
- def restrictNumber(number : Int) : Option[LoanedKey] = {
- ary.get(number) match {
- case Some(key) if key.policy != AvailabilityPolicy.Restricted =>
- key.policy = AvailabilityPolicy.Restricted
- Some(new LoanedKey(number, key))
- case _ =>
- None
- }
- }
-
- def finalizeRestrictions : List[Int] = {
- ary
- .filter { case (_, key : Key) => key.policy == AvailabilityPolicy.Restricted }
- .keys
- .toList
- }
-
def clear(): List[IdentifiableEntity] = {
- val leased = ary.values.filter(_.policy != AvailabilityPolicy.Available)
- leased collect { case key if key.obj.isEmpty =>
- key.policy = AvailabilityPolicy.Available
- }
- leased.toList collect { case key if key.obj.nonEmpty =>
- key.policy = AvailabilityPolicy.Available
- val out = key.obj.get
- key.obj = None
- out
- }
+ ary.values.foreach { _.policy = AvailabilityPolicy.Available }
+ ary.values.collect {
+ case key if key.obj.nonEmpty =>
+ val obj = key.obj.get
+ key.obj = None
+ obj
+ }.toList
}
}
diff --git a/src/main/scala/net/psforever/objects/guid/actor/NumberPoolActor.scala b/src/main/scala/net/psforever/objects/guid/uns/NumberPoolActor.scala
similarity index 72%
rename from src/main/scala/net/psforever/objects/guid/actor/NumberPoolActor.scala
rename to src/main/scala/net/psforever/objects/guid/uns/NumberPoolActor.scala
index edc0408e..211ae802 100644
--- a/src/main/scala/net/psforever/objects/guid/actor/NumberPoolActor.scala
+++ b/src/main/scala/net/psforever/objects/guid/uns/NumberPoolActor.scala
@@ -1,5 +1,5 @@
// Copyright (c) 2017 PSForever
-package net.psforever.objects.guid.actor
+package net.psforever.objects.guid.uns
import akka.actor.Actor
import net.psforever.objects.guid.pool.NumberPool
@@ -21,27 +21,27 @@ class NumberPoolActor(pool: NumberPool) extends Actor {
private[this] val log = org.log4s.getLogger
def receive: Receive = {
- case NumberPoolActor.GetAnyNumber(id) =>
+ case NumberPoolActor.GetAnyNumber() =>
sender() ! (pool.Get() match {
case Success(value) =>
- NumberPoolActor.GiveNumber(value, id)
- case Failure(ex) => ;
- NumberPoolActor.NoNumber(ex, id)
+ NumberPoolActor.GiveNumber(value)
+ case Failure(ex) =>
+ NumberPoolActor.NoNumber(ex)
})
- case NumberPoolActor.GetSpecificNumber(number, id) =>
+ case NumberPoolActor.GetSpecificNumber(number) =>
sender() ! (NumberPoolActor.GetSpecificNumber(pool, number) match {
case Success(value) =>
- NumberPoolActor.GiveNumber(value, id)
+ NumberPoolActor.GiveNumber(value)
case Failure(ex) => ;
- NumberPoolActor.NoNumber(ex, id)
+ NumberPoolActor.NoNumber(ex)
})
- case NumberPoolActor.ReturnNumber(number, id) =>
+ case NumberPoolActor.ReturnNumber(number) =>
val result = pool.Return(number)
val ex: Option[Throwable] = if (!result) { Some(new Exception("number was not returned")) }
else { None }
- sender() ! NumberPoolActor.ReturnNumberResult(number, ex, id)
+ sender() ! NumberPoolActor.ReturnNumberResult(number, ex)
case msg =>
log.warn(s"Received an unexpected message - ${msg.toString}")
@@ -52,36 +52,36 @@ object NumberPoolActor {
/**
* A message to invoke the current `NumberSelector`'s functionality.
- * @param id a potential identifier to associate this request
*/
- final case class GetAnyNumber(id: Option[Any] = None)
+ final case class GetAnyNumber()
/**
* A message to invoke a `SpecificSelector` to acquire the specific `number`, if it is available in this pool.
* @param number the pre-selected number
- * @param id a potential identifier to associate this request
*/
- final case class GetSpecificNumber(number: Int, id: Option[Any] = None)
+ final case class GetSpecificNumber(number: Int)
/**
* A message to distribute the `number` that was drawn.
* @param number the pre-selected number
- * @param id a potential identifier to associate this request
*/
- final case class GiveNumber(number: Int, id: Option[Any] = None)
+ final case class GiveNumber(number: Int)
- final case class NoNumber(ex: Throwable, id: Option[Any] = None)
+ final case class NoNumber(ex: Throwable)
/**
* A message to invoke the `returnNumber` functionality of the current `NumberSelector`.
* @param number the number
*/
- final case class ReturnNumber(number: Int, id: Option[Any] = None)
+ final case class ReturnNumber(number: Int)
- final case class ReturnNumberResult(number: Int, ex: Option[Throwable], id: Option[Any] = None)
+ final case class ReturnNumberResult(number: Int, ex: Option[Throwable])
/**
* Use the `SpecificSelector` on this pool to extract a specific object from the pool, if it is included and available.
+ * Getting a specific number involves creating the appropriate selector and swapping with the current selector.
+ * This process may involve re-formatting the underlying number pool array once for each selector.
+ * @see `NumberSelector.Format`
* @param pool the `NumberPool` to draw from
* @param number the number requested
* @return the number requested, or an error
diff --git a/src/main/scala/net/psforever/objects/guid/uns/RegisteredEntity.scala b/src/main/scala/net/psforever/objects/guid/uns/RegisteredEntity.scala
new file mode 100644
index 00000000..1499b953
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/guid/uns/RegisteredEntity.scala
@@ -0,0 +1,14 @@
+// Copyright (c) 2021 PSForever
+package net.psforever.objects.guid.uns
+
+import net.psforever.objects.entity.IdentifiableEntity
+import net.psforever.objects.guid.NumberPoolHub
+
+final case class RegisteredEntity(
+ obj: IdentifiableEntity,
+ pool_name: String,
+ guid_system: NumberPoolHub,
+ number: Int
+ )
+
+final case class AlreadyRegisteredEntity(msg: RegisteredEntity)
diff --git a/src/main/scala/net/psforever/objects/guid/uns/UnregisteredEntity.scala b/src/main/scala/net/psforever/objects/guid/uns/UnregisteredEntity.scala
new file mode 100644
index 00000000..715cf7f0
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/guid/uns/UnregisteredEntity.scala
@@ -0,0 +1,14 @@
+// Copyright (c) 2021 PSForever
+package net.psforever.objects.guid.uns
+
+import net.psforever.objects.entity.IdentifiableEntity
+import net.psforever.objects.guid.NumberPoolHub
+
+final case class UnregisteredEntity(
+ obj: IdentifiableEntity,
+ pool_name: String,
+ guid_system: NumberPoolHub,
+ number: Int
+ )
+
+final case class AlreadyUnregisteredEntity(msg: UnregisteredEntity)
diff --git a/src/main/scala/net/psforever/objects/locker/LockerContainerDefinition.scala b/src/main/scala/net/psforever/objects/locker/LockerContainerDefinition.scala
new file mode 100644
index 00000000..3d902f56
--- /dev/null
+++ b/src/main/scala/net/psforever/objects/locker/LockerContainerDefinition.scala
@@ -0,0 +1,17 @@
+// Copyright (c) 2021 PSForever
+package net.psforever.objects.locker
+
+import net.psforever.objects.definition.EquipmentDefinition
+import net.psforever.objects.definition.converter.LockerContainerConverter
+import net.psforever.objects.equipment.EquipmentSize
+
+class LockerContainerDefinition extends EquipmentDefinition(objectId = 456) {
+ Name = "locker_container"
+ Size = EquipmentSize.Inventory
+ Packet = LockerContainerDefinition.converter
+ registerAs = "lockers"
+}
+
+object LockerContainerDefinition {
+ val converter = new LockerContainerConverter()
+}
diff --git a/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala b/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala
index 4cde293a..bc0526a8 100644
--- a/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala
@@ -4,7 +4,7 @@ package net.psforever.objects.serverobject.pad
import akka.actor.{Cancellable, Props}
import net.psforever.objects.avatar.SpecialCarry
import net.psforever.objects.entity.WorldEntity
-import net.psforever.objects.guid.GUIDTask.UnregisterVehicle
+import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.pad.process.{VehicleSpawnControlBase, VehicleSpawnControlConcealPlayer}
import net.psforever.objects.zones.{Zone, ZoneAware, Zoning}
@@ -516,7 +516,7 @@ object VehicleSpawnControl {
if (zone.Vehicles.contains(vehicle)) { //already added to zone
vehicle.Actor ! Vehicle.Deconstruct(Some(0.seconds))
} else { //just registered to zone
- zone.tasks ! UnregisterVehicle(vehicle)(zone.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterVehicle(zone.GUID, vehicle))
}
}
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/shuttle/OrbitalShuttlePadControl.scala b/src/main/scala/net/psforever/objects/serverobject/shuttle/OrbitalShuttlePadControl.scala
index 83f218cb..18f2294c 100644
--- a/src/main/scala/net/psforever/objects/serverobject/shuttle/OrbitalShuttlePadControl.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/shuttle/OrbitalShuttlePadControl.scala
@@ -2,7 +2,7 @@
package net.psforever.objects.serverobject.shuttle
import akka.actor.{Actor, ActorRef}
-import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
+import net.psforever.objects.guid._
import net.psforever.objects.{Player, Vehicle}
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.doors.Door
@@ -14,7 +14,7 @@ import net.psforever.services.hart.{HartTimer, HartTimerActions}
import net.psforever.services.{Service, ServiceManager}
import net.psforever.types.ChatMessageType
-import scala.util.Success
+import scala.concurrent.Future
/**
* An `Actor` that handles messages being dispatched to a specific `OrbitalShuttlePad`.
@@ -114,7 +114,7 @@ class OrbitalShuttlePadControl(pad: OrbitalShuttlePad) extends Actor {
newShuttle.Position = position + Vector3(0, -8.25f, 0).Rz(pad.Orientation.z) //magic offset number
newShuttle.Orientation = pad.Orientation
newShuttle.Faction = pad.Faction
- zone.tasks ! OrbitalShuttlePadControl.registerShuttle(zone, newShuttle, self)
+ TaskWorkflow.execute(OrbitalShuttlePadControl.registerShuttle(zone, newShuttle, self))
context.become(shuttleTime)
case _ => ;
@@ -127,33 +127,23 @@ object OrbitalShuttlePadControl {
* @param zone the zone the shuttle and the pad will occupy
* @param shuttle the vehicle that will be the shuttle
* @param ref a reference to the control agency for the orbital shuttle pad
- * @return a `TaskResolver.GiveTask` object
+ * @return a `TaskBundle` object
*/
- def registerShuttle(zone: Zone, shuttle: Vehicle, ref: ActorRef): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- new Task() {
+ def registerShuttle(zone: Zone, shuttle: Vehicle, ref: ActorRef): TaskBundle = {
+ import scala.concurrent.ExecutionContext.Implicits.global
+ TaskBundle(
+ new StraightforwardTask() {
private val localZone = zone
private val localShuttle = shuttle
private val localSelf = ref
- override def Description: String = s"register an orbital shuttle"
+ override def description(): String = s"register an orbital shuttle"
- override def isComplete : Task.Resolution.Value = if (localShuttle.HasGUID) {
- Task.Resolution.Success
- } else {
- Task.Resolution.Incomplete
- }
-
- def Execute(resolver : ActorRef) : Unit = {
+ def action() : Future[Any] = {
localZone.Transport.tell(Zone.Vehicle.Spawn(localShuttle), localSelf)
- resolver ! Success(true)
+ Future(this)
}
-
- override def onFailure(ex : Throwable) : Unit = {
- super.onFailure(ex)
- localSelf ! Zone.Vehicle.CanNotSpawn(localZone, localShuttle, ex.getMessage)
- }
- }, List(GUIDTask.RegisterVehicle(shuttle)(zone.GUID))
+ }, GUIDTask.registerVehicle(zone.GUID, shuttle)
)
}
diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalDefinition.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalDefinition.scala
index 9259d48e..59f881cd 100644
--- a/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalDefinition.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalDefinition.scala
@@ -13,6 +13,7 @@ import net.psforever.objects.serverobject.structures.AmenityDefinition
abstract class TerminalDefinition(objectId: Int) extends AmenityDefinition(objectId) {
Name = "terminal"
Packet = new TerminalConverter
+ registerAs = "terminals"
/**
* The unimplemented functionality for the entry function of form of activity
diff --git a/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeDefinition.scala b/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeDefinition.scala
index 485bebb8..b01e31f2 100644
--- a/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeDefinition.scala
+++ b/src/main/scala/net/psforever/objects/serverobject/tube/SpawnTubeDefinition.scala
@@ -12,6 +12,7 @@ import net.psforever.objects.serverobject.structures.{Amenity, AmenityDefinition
*/
class SpawnTubeDefinition(object_id: Int) extends AmenityDefinition(object_id) with SpawnPointDefinition {
Packet = new SpawnTubeConverter
+ registerAs = "terminals"
}
object SpawnTubeDefinition {
diff --git a/src/main/scala/net/psforever/objects/vehicles/Utility.scala b/src/main/scala/net/psforever/objects/vehicles/Utility.scala
index 80e1b77b..27a57b5d 100644
--- a/src/main/scala/net/psforever/objects/vehicles/Utility.scala
+++ b/src/main/scala/net/psforever/objects/vehicles/Utility.scala
@@ -228,6 +228,7 @@ object Utility {
extends AmenityDefinition(DeployedItem.router_telepad_deployable.id)
with BaseDeployableDefinition {
Packet = new SmallDeployableConverter
+ registerAs = "terminals"
def Item: DeployedItem.Value = DeployedItem.router_telepad_deployable
}
diff --git a/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala b/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala
index f001da23..438fec3f 100644
--- a/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala
+++ b/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala
@@ -7,7 +7,7 @@ import net.psforever.objects._
import net.psforever.objects.ballistics.VehicleSource
import net.psforever.objects.entity.WorldEntity
import net.psforever.objects.equipment.{Equipment, EquipmentSlot, JammableMountedWeapons}
-import net.psforever.objects.guid.GUIDTask
+import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects.inventory.{GridInventory, InventoryItem}
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
@@ -407,7 +407,7 @@ class VehicleControl(vehicle: Vehicle)
CancelJammeredSound(vehicle)
CancelJammeredStatus(vehicle)
//unregister
- zone.tasks ! GUIDTask.UnregisterVehicle(vehicle)(zone.GUID)
+ TaskWorkflow.execute(GUIDTask.unregisterVehicle(zone.GUID, vehicle))
//banished to the shadow realm
vehicle.Position = Vector3.Zero
//queue final deletion
diff --git a/src/main/scala/net/psforever/objects/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala
index 14a62af1..f775935f 100644
--- a/src/main/scala/net/psforever/objects/zones/Zone.scala
+++ b/src/main/scala/net/psforever/objects/zones/Zone.scala
@@ -2,16 +2,12 @@
package net.psforever.objects.zones
import akka.actor.{ActorContext, ActorRef, Props}
-import akka.routing.RandomPool
import net.psforever.objects.{PlanetSideGameObject, _}
-import net.psforever.objects.ballistics.{Projectile, SourceEntry}
+import net.psforever.objects.ballistics.SourceEntry
import net.psforever.objects.ce.Deployable
-import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment.Equipment
-import net.psforever.objects.guid.{NumberPoolHub, TaskResolver}
-import net.psforever.objects.guid.actor.UniqueNumberSystem
+import net.psforever.objects.guid.{NumberPoolHub, UniqueNumberOps, UniqueNumberSetup}
import net.psforever.objects.guid.key.LoanedKey
-import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.inventory.Container
import net.psforever.objects.serverobject.painbox.{Painbox, PainboxDefinition}
@@ -39,6 +35,7 @@ import net.psforever.actors.session.AvatarActor
import net.psforever.actors.zone.ZoneActor
import net.psforever.objects.avatar.Avatar
import net.psforever.objects.geometry.d3.VolumetricGeometry
+import net.psforever.objects.guid.pool.NumberPool
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.doors.Door
@@ -79,14 +76,16 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
/** Governs general synchronized external requests. */
var actor: typed.ActorRef[ZoneActor.Command] = _
- /** Actor that handles SOI related functionality, for example if a player is in a SOI */
+ /** Actor that handles SOI related functionality, for example if a player is in an SOI */
private var soi = Default.Actor
- /** Used by the globally unique identifier system to coordinate requests. */
- private var accessor: ActorRef = ActorRef.noSender
-
/** The basic support structure for the globally unique number system used by this `Zone`. */
private var guid: NumberPoolHub = new NumberPoolHub(new MaxNumberSource(65536))
+ /** The core of the unique number system, to which requests may be submitted.
+ * @see `UniqueNumberSys`
+ * @see `Zone.Init(ActorContext)`
+ */
+ private[zones] var unops: UniqueNumberOps = _
/** The blockmap structure for partitioning entities and environmental aspects of the zone.
* For a standard 8912`^`2 map, each of the four hundred formal map grids is 445.6m long and wide.
@@ -105,8 +104,6 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
/** Used by the `Zone` to coordinate `Equipment` dropping and collection requests. */
private var ground: ActorRef = Default.Actor
- private var taskResolver: ActorRef = Default.Actor
-
/**
*/
private val constructions: ListBuffer[Deployable] = ListBuffer()
@@ -177,7 +174,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
* First, the `Actor`-driven aspect of the globally unique identifier system for this `Zone` is finalized.
* Second, all supporting `Actor` agents are created, e.g., `ground`.
* Third, the `ZoneMap` server objects are loaded and constructed within that aforementioned system.
- * To avoid being called more than once, there is a test whether the `accessor` for the globally unique identifier system has been changed.
+ * To avoid being called more than once, there is a test whether the globally unique identifier system has been changed.
*
* Execution of this operation should be fail-safe.
* The chances of failure should be mitigated or skipped.
@@ -187,15 +184,9 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
* @param context a reference to an `ActorContext` necessary for `Props`
*/
def init(implicit context: ActorContext): Unit = {
- if (accessor == ActorRef.noSender) {
+ if (unops == null) {
SetupNumberPools()
- taskResolver = CreateTaskResolvers(context)
- accessor = context.actorOf(
- RandomPool(25).props(
- Props(classOf[UniqueNumberSystem], this.guid, UniqueNumberSystem.AllocateNumberPoolActors(this.guid))
- ),
- s"zone-$id-uns"
- )
+ context.actorOf(Props(classOf[UniqueNumberSys], this, this.guid), s"zone-$id-uns")
ground = context.actorOf(Props(classOf[ZoneGroundActor], this, equipmentOnGround), s"zone-$id-ground")
deployables = context.actorOf(Props(classOf[ZoneDeployableActor], this, constructions), s"zone-$id-deployables")
transport = context.actorOf(Props(classOf[ZoneVehicleActor], this, vehicles), s"zone-$id-vehicles")
@@ -334,26 +325,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
}
}
- def SetupNumberPools(): Unit = {
- guid.AddPool("environment", (0 to 3000).toList) //TODO tailor to suit requirements of zone
- //TODO unlump pools later; do not make any single pool too big
- guid.AddPool("dynamic", (3001 to 10000).toList).Selector =
- new RandomSelector //TODO all things will be registered here, for now
- guid.AddPool("b", (10001 to 15000).toList).Selector = new RandomSelector
- guid.AddPool("c", (15001 to 20000).toList).Selector = new RandomSelector
- guid.AddPool("d", (20001 to 25000).toList).Selector = new RandomSelector
- guid.AddPool("e", (25001 to 30000).toList).Selector = new RandomSelector
- guid.AddPool("f", (30001 to 35000).toList).Selector = new RandomSelector
- guid.AddPool("g", (35001 until 40100).toList).Selector = new RandomSelector
- guid.AddPool("projectiles", (Projectile.baseUID until Projectile.rangeUID).toList)
- guid.AddPool("locker-contents", (40150 until 40450).toList).Selector = new RandomSelector
- //TODO disabled temporarily to lighten load times
- //guid.AddPool("h", (40150 to 45000).toList).Selector = new RandomSelector
- //guid.AddPool("i", (45001 to 50000).toList).Selector = new RandomSelector
- //guid.AddPool("j", (50001 to 55000).toList).Selector = new RandomSelector
- //guid.AddPool("k", (55001 to 60000).toList).Selector = new RandomSelector
- //guid.AddPool("l", (60001 to 65535).toList).Selector = new RandomSelector
- }
+ def SetupNumberPools(): Unit = { /* override to tailor to suit requirements of zone */ }
def findSpawns(
faction: PlanetSideEmpire.Value,
@@ -436,14 +408,14 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
def Number: Int = zoneNumber
/**
- * The globally unique identifier system is synchronized via an `Actor` to ensure that concurrent requests do not clash.
+ * The globally unique identifier system ensures that concurrent requests do not clash.
* A clash is merely when the same number is produced more than once by the same system due to concurrent requests.
- * @return synchronized reference to the globally unique identifier system
+ * @return reference to the globally unique identifier system
*/
- def GUID: ActorRef = accessor
+ def GUID: UniqueNumberOps = unops
/**
- * Replace the current globally unique identifier system with a new one.
+ * Replace the current globally unique identifier support structure with a new one.
* The replacement will not occur if the current system is populated or if its synchronized reference has been created.
* The primary use of this function should be testing.
* A warning will be issued.
@@ -475,12 +447,14 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
* @return `true`, if the new pool is created;
* `false`, if the new pool can not be created because the system has already been started
*/
- def AddPool(name: String, pool: Seq[Int]): Boolean = {
- if (accessor == Default.Actor || accessor == null) {
- guid.AddPool(name, pool.toList)
- true
+ def AddPool(name: String, pool: Seq[Int]): Option[NumberPool] = {
+ if (unops == null) {
+ guid.AddPool(name, pool.toList) match {
+ case _: Exception => None
+ case out => Some(out)
+ }
} else {
- false
+ None
}
}
@@ -489,13 +463,12 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
* Throws exceptions for specific reasons if the pool can not be removed before the system has been started.
* @see `NumberPoolHub.RemovePool`
* @param name the name of the pool
- * @return `true`, if the new pool is un-made;
- * `false`, if the new pool can not be removed because the system has already been started
+ * @return `true`, if the pool is un-made;
+ * `false`, if the pool can not be removed (because the system has already been started?)
*/
def RemovePool(name: String): Boolean = {
- if (accessor == Default.Actor) {
- guid.RemovePool(name)
- true
+ if (unops == null) {
+ guid.RemovePool(name).nonEmpty
} else {
false
}
@@ -526,7 +499,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
/**
* Recover an object from the globally unique identifier system by the number that was assigned previously.
- * The object must be upcast into due to the differtence between the storage type and the return type.
+ * The object must be upcast into due to the minor difference between the storage type and the return type.
* @param object_guid the globally unique identifier requested
* @return the associated object, if it exists
* @see `NumberPoolHub(Int)`
@@ -606,7 +579,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
private def BuildSupportObjects(): Unit = {
//guard against errors here, but don't worry about specifics; let ZoneActor.ZoneSetupCheck complain about problems
- val other: ListBuffer[IdentifiableEntity] = new ListBuffer[IdentifiableEntity]()
+ val other: ListBuffer[PlanetSideGameObject] = new ListBuffer[PlanetSideGameObject]()
//turret to weapon
map.turretToWeapon.foreach({
case (turret_guid, weapon_guid) =>
@@ -634,7 +607,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
}
})
//after all fixed GUID's are defined ...
- other.foreach(obj => guid.register(obj, "dynamic"))
+ other.foreach(obj => guid.register(obj, obj.Definition.registerAs))
}
private def MakeBuildings(implicit context: ActorContext): PairMap[Int, Building] = {
@@ -836,11 +809,26 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
vehicleEvents = bus
VehicleEvents
}
+}
- def tasks: ActorRef = taskResolver
-
- protected def CreateTaskResolvers(context: ActorContext, numberCreated: Int = 20): ActorRef = {
- context.actorOf(RandomPool(numberCreated).props(Props[TaskResolver]()), s"zone-$id-taskResolver")
+/**
+ * A local class for spawning `Actor`s to manage the number pools for this zone,
+ * create a number system operations class to access those pools within the context of registering and unregistering,
+ * and assign that number pool operations class to the containing zone
+ * through specific scope access.
+ * @see `UniqueNumberOps`
+ * @see `UniqueNumberSetup`
+ * @see `UniqueNumberSetup.AllocateNumberPoolActors`
+ * @see `Zone.unops`
+ * @param zone the zone in which the operations class will be referenced
+ * @param guid the number pool management class
+ */
+private class UniqueNumberSys(zone: Zone, guid: NumberPoolHub)
+ extends UniqueNumberSetup(guid, UniqueNumberSetup.AllocateNumberPoolActors) {
+ override def init(): UniqueNumberOps = {
+ val unsys = super.init()
+ zone.unops = unsys // zone.unops is accessible from here by virtue of being 'private[zones]`
+ unsys
}
}
diff --git a/src/main/scala/net/psforever/services/RemoverActor.scala b/src/main/scala/net/psforever/services/RemoverActor.scala
index 78c57747..0348a3c1 100644
--- a/src/main/scala/net/psforever/services/RemoverActor.scala
+++ b/src/main/scala/net/psforever/services/RemoverActor.scala
@@ -1,15 +1,15 @@
// Copyright (c) 2017 PSForever
package net.psforever.services
-import akka.actor.{ActorRef, Cancellable}
-import net.psforever.objects.guid.TaskResolver
+import akka.actor.Cancellable
+import net.psforever.objects.guid.{StraightforwardTask, TaskBundle, TaskWorkflow}
import net.psforever.objects.zones.Zone
import net.psforever.objects.{Default, PlanetSideGameObject}
import net.psforever.types.Vector3
import net.psforever.services.support.{SimilarityComparator, SupportActor, SupportActorCaseConversions}
+import scala.concurrent.Future
import scala.concurrent.duration._
-import scala.util.Success
/**
* The base class for a type of "destruction `Actor`" intended to be used for delaying object cleanup activity.
@@ -30,7 +30,7 @@ import scala.util.Success
* and finally unregistering it.
* Some types of object have (de-)implementation variations which should be made explicit through the overrides.
*/
-abstract class RemoverActor(val taskResolver: ActorRef) extends SupportActor[RemoverActor.Entry] {
+abstract class RemoverActor() extends SupportActor[RemoverActor.Entry] {
/**
* The timer that checks whether entries in the first pool are still eligible for that pool.
@@ -249,30 +249,23 @@ abstract class RemoverActor(val taskResolver: ActorRef) extends SupportActor[Rem
def SecondJob(entry: RemoverActor.Entry): Unit = {
entry.obj.Position = Vector3.Zero //somewhere it will not disturb anything
- taskResolver ! FinalTask(entry)
+ TaskWorkflow.execute(FinalTask(entry))
}
- def FinalTask(entry: RemoverActor.Entry): TaskResolver.GiveTask = {
- import net.psforever.objects.guid.Task
- TaskResolver.GiveTask(
- new Task() {
- private val localEntry = entry
- private val localAnnounce = self
+ def FinalTask(entry: RemoverActor.Entry): TaskBundle = {
+ import scala.concurrent.ExecutionContext.Implicits.global
+ TaskBundle(
+ new StraightforwardTask() {
+// private val localEntry = entry
+// private val localAnnounce = self
- override def isComplete: Task.Resolution.Value =
- if (!localEntry.obj.HasGUID) {
- Task.Resolution.Success
- } else {
- Task.Resolution.Incomplete
- }
-
- def Execute(resolver: ActorRef): Unit = {
- resolver ! Success(this)
+ def action(): Future[Any] = {
+ Future(this)
}
- override def onFailure(ex: Throwable): Unit = {
- localAnnounce ! RemoverActor.FailureToWork(localEntry, ex)
- }
+// override def onFailure(ex: Throwable): Unit = {
+// localAnnounce ! RemoverActor.FailureToWork(localEntry, ex)
+// }
},
List(DeletionTask(entry))
)
@@ -319,7 +312,7 @@ abstract class RemoverActor(val taskResolver: ActorRef) extends SupportActor[Rem
* @see `GUIDTask`
* @param entry the entry
*/
- def DeletionTask(entry: RemoverActor.Entry): TaskResolver.GiveTask
+ def DeletionTask(entry: RemoverActor.Entry): TaskBundle
}
object RemoverActor extends SupportActorCaseConversions {
diff --git a/src/main/scala/net/psforever/services/account/AccountPersistenceService.scala b/src/main/scala/net/psforever/services/account/AccountPersistenceService.scala
index b314a69c..76993e10 100644
--- a/src/main/scala/net/psforever/services/account/AccountPersistenceService.scala
+++ b/src/main/scala/net/psforever/services/account/AccountPersistenceService.scala
@@ -6,7 +6,7 @@ import akka.actor.{Actor, ActorRef, Cancellable, Props}
import scala.collection.mutable
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
-import net.psforever.objects.guid.GUIDTask
+import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects._
import net.psforever.objects.avatar.Avatar
import net.psforever.objects.serverobject.mount.Mountable
@@ -389,7 +389,8 @@ class PersistenceMonitor(name: String, squadService: ActorRef) extends Actor {
}
inZone.Population.tell(Zone.Population.Release(avatar), parent)
inZone.AvatarEvents.tell(AvatarServiceMessage(inZone.id, AvatarAction.ObjectDelete(pguid, pguid)), parent)
- inZone.tasks.tell(GUIDTask.UnregisterPlayer(player)(inZone.GUID), parent)
+ TaskWorkflow.execute(GUIDTask.unregisterPlayer(inZone.GUID, player))
+ //inZone.tasks.tell(GUIDTask.UnregisterPlayer(player)(inZone.GUID), parent)
AvatarLogout(avatar)
}
@@ -408,7 +409,8 @@ class PersistenceMonitor(name: String, squadService: ActorRef) extends Actor {
squadService.tell(Service.Leave(Some(avatar.id.toString)), context.parent)
Deployables.Disown(inZone, avatar, context.parent)
inZone.Population.tell(Zone.Population.Leave(avatar), context.parent)
- inZone.tasks.tell(GUIDTask.UnregisterObjectTask(avatar.locker)(inZone.GUID), context.parent)
+ TaskWorkflow.execute(GUIDTask.unregisterObject(inZone.GUID, avatar.locker))
+ //inZone.tasks.tell(GUIDTask.UnregisterObjectTask(avatar.locker)(inZone.GUID), context.parent)
log.info(s"Logout of ${avatar.name}")
}
}
diff --git a/src/main/scala/net/psforever/services/avatar/AvatarService.scala b/src/main/scala/net/psforever/services/avatar/AvatarService.scala
index 3863a806..4d1abe34 100644
--- a/src/main/scala/net/psforever/services/avatar/AvatarService.scala
+++ b/src/main/scala/net/psforever/services/avatar/AvatarService.scala
@@ -10,8 +10,8 @@ import net.psforever.services.avatar.support.{CorpseRemovalActor, DroppedItemRem
import net.psforever.services.{GenericEventBus, RemoverActor, Service}
class AvatarService(zone: Zone) extends Actor {
- private val undertaker: ActorRef = context.actorOf(Props(classOf[CorpseRemovalActor], zone.tasks), s"${zone.id}-corpse-removal-agent")
- private val janitor = context.actorOf(Props(classOf[DroppedItemRemover], zone.tasks), s"${zone.id}-item-remover-agent")
+ private val undertaker: ActorRef = context.actorOf(Props[CorpseRemovalActor](), s"${zone.id}-corpse-removal-agent")
+ private val janitor = context.actorOf(Props[DroppedItemRemover](), s"${zone.id}-item-remover-agent")
private[this] val log = org.log4s.getLogger
diff --git a/src/main/scala/net/psforever/services/avatar/support/CorpseRemovalActor.scala b/src/main/scala/net/psforever/services/avatar/support/CorpseRemovalActor.scala
index b97b707c..062c7f15 100644
--- a/src/main/scala/net/psforever/services/avatar/support/CorpseRemovalActor.scala
+++ b/src/main/scala/net/psforever/services/avatar/support/CorpseRemovalActor.scala
@@ -1,8 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.services.avatar.support
-import akka.actor.ActorRef
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskBundle}
import net.psforever.objects.Player
import net.psforever.types.ExoSuitType
import net.psforever.services.{RemoverActor, Service}
@@ -10,7 +9,7 @@ import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import scala.concurrent.duration._
-class CorpseRemovalActor(taskResolver: ActorRef) extends RemoverActor(taskResolver) {
+class CorpseRemovalActor extends RemoverActor() {
final val FirstStandardDuration: FiniteDuration = 1 minute
final val SecondStandardDuration: FiniteDuration = 500 milliseconds
@@ -32,9 +31,9 @@ class CorpseRemovalActor(taskResolver: ActorRef) extends RemoverActor(taskResolv
def ClearanceTest(entry: RemoverActor.Entry): Boolean = !entry.zone.Corpses.contains(entry.obj)
- def DeletionTask(entry: RemoverActor.Entry): TaskResolver.GiveTask = {
+ def DeletionTask(entry: RemoverActor.Entry): TaskBundle = {
val player = entry.obj.asInstanceOf[Player]
- val task = GUIDTask.UnregisterPlayer(player)(entry.zone.GUID)
+ val task = GUIDTask.unregisterPlayer(entry.zone.GUID, player)
player.ExoSuit = ExoSuitType.Standard
task
}
diff --git a/src/main/scala/net/psforever/services/avatar/support/DroppedItemRemover.scala b/src/main/scala/net/psforever/services/avatar/support/DroppedItemRemover.scala
index 19d63969..80a0ce87 100644
--- a/src/main/scala/net/psforever/services/avatar/support/DroppedItemRemover.scala
+++ b/src/main/scala/net/psforever/services/avatar/support/DroppedItemRemover.scala
@@ -1,15 +1,14 @@
// Copyright (c) 2017 PSForever
package net.psforever.services.avatar.support
-import akka.actor.ActorRef
import net.psforever.objects.equipment.Equipment
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskBundle}
import net.psforever.services.{RemoverActor, Service}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import scala.concurrent.duration._
-class DroppedItemRemover(taskResolver: ActorRef) extends RemoverActor(taskResolver) {
+class DroppedItemRemover extends RemoverActor() {
final val FirstStandardDuration: FiniteDuration = 3 minutes
final val SecondStandardDuration: FiniteDuration = 500 milliseconds
@@ -31,7 +30,7 @@ class DroppedItemRemover(taskResolver: ActorRef) extends RemoverActor(taskResolv
def ClearanceTest(entry: RemoverActor.Entry): Boolean = !entry.zone.EquipmentOnGround.contains(entry.obj)
- def DeletionTask(entry: RemoverActor.Entry): TaskResolver.GiveTask = {
- GUIDTask.UnregisterEquipment(entry.obj.asInstanceOf[Equipment])(entry.zone.GUID)
+ def DeletionTask(entry: RemoverActor.Entry): TaskBundle = {
+ GUIDTask.unregisterEquipment(entry.zone.GUID, entry.obj.asInstanceOf[Equipment])
}
}
diff --git a/src/main/scala/net/psforever/services/local/LocalService.scala b/src/main/scala/net/psforever/services/local/LocalService.scala
index 4c9b5606..33dadf70 100644
--- a/src/main/scala/net/psforever/services/local/LocalService.scala
+++ b/src/main/scala/net/psforever/services/local/LocalService.scala
@@ -19,10 +19,10 @@ class LocalService(zone: Zone) extends Actor {
Props[HackClearActor](), s"${zone.id}-local-hack-clearer"
)
private val hackCapturer = context.actorOf(
- Props(classOf[HackCaptureActor], zone.tasks), s"${zone.id}-local-hack-capturer"
+ Props[HackCaptureActor](), s"${zone.id}-local-hack-capturer"
)
private val captureFlagManager = context.actorOf(
- Props(classOf[CaptureFlagManager], zone.tasks, zone), s"${zone.id}-local-capture-flag-manager"
+ Props(classOf[CaptureFlagManager], zone), s"${zone.id}-local-capture-flag-manager"
)
private[this] val log = org.log4s.getLogger
diff --git a/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala b/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala
index d6d97263..e1d07145 100644
--- a/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala
+++ b/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala
@@ -1,8 +1,9 @@
package net.psforever.services.local.support
import akka.actor.{Actor, ActorRef, Cancellable}
+import net.psforever.login.WorldSession
import net.psforever.objects.{Default, Player}
-import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects.serverobject.llu.CaptureFlag
import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
@@ -16,14 +17,11 @@ import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.types.{ChatMessageType, PlanetSideEmpire, PlanetSideGUID, Vector3}
import scala.concurrent.duration.DurationInt
-import scala.util.Success
-
/**
* Responsible for handling capture flag related lifecycles
- * @param taskResolver A reference to a zone's task resolver actor
*/
-class CaptureFlagManager(val taskResolver: ActorRef, zone: Zone) extends Actor{
+class CaptureFlagManager(zone: Zone) extends Actor{
private[this] val log = org.log4s.getLogger(self.path.name)
var galaxyService: ActorRef = ActorRef.noSender
@@ -105,14 +103,14 @@ class CaptureFlagManager(val taskResolver: ActorRef, zone: Zone) extends Actor{
socket.captureFlag = flag
TrackFlag(flag)
- taskResolver ! CallBackForTask(
- TaskResolver.GiveTask(GUIDTask.RegisterObjectTask(flag)(socket.Zone.GUID).task),
+ TaskWorkflow.execute(WorldSession.CallBackForTask(
+ GUIDTask.registerObject(socket.Zone.GUID, flag),
socket.Zone.LocalEvents,
LocalServiceMessage(
socket.Zone.id,
LocalAction.LluSpawned(PlanetSideGUID(-1), flag)
)
- )
+ ))
// Broadcast chat message for LLU spawn
val owner = flag.Owner.asInstanceOf[Building]
@@ -184,7 +182,7 @@ class CaptureFlagManager(val taskResolver: ActorRef, zone: Zone) extends Actor{
// Unregister LLU from clients,
flag.Zone.LocalEvents ! LocalServiceMessage(flag.Zone.id, LocalAction.LluDespawned(PlanetSideGUID(-1), flag))
// Then unregister it from the GUID pool
- taskResolver ! TaskResolver.GiveTask(GUIDTask.UnregisterObjectTask(flag)(flag.Zone.GUID).task)
+ TaskWorkflow.execute(GUIDTask.unregisterObject(flag.Zone.GUID,flag))
}
private def ChatBroadcast(zone: Zone, message: String, fanfare: Boolean = true): Unit = {
@@ -202,25 +200,6 @@ class CaptureFlagManager(val taskResolver: ActorRef, zone: Zone) extends Actor{
)
)
}
-
- // Todo: Duplicate from SessionActor. Make common.
- def CallBackForTask(task: TaskResolver.GiveTask, sendTo: ActorRef, pass: Any): TaskResolver.GiveTask = {
- TaskResolver.GiveTask(
- new Task() {
- private val localDesc = task.task.Description
- private val destination = sendTo
- private val passMsg = pass
-
- override def Description: String = s"callback for tasking $localDesc"
-
- def Execute(resolver: ActorRef): Unit = {
- destination ! passMsg
- resolver ! Success(this)
- }
- },
- List(task)
- )
- }
}
object CaptureFlagManager {
diff --git a/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala b/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala
index f511fadb..a9e991a0 100644
--- a/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala
+++ b/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala
@@ -1,6 +1,6 @@
package net.psforever.services.local.support
-import akka.actor.{Actor, ActorRef, Cancellable}
+import akka.actor.{Actor, Cancellable}
import net.psforever.actors.zone.{BuildingActor, ZoneActor}
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.hackable.Hackable
@@ -20,7 +20,7 @@ import scala.util.Random
/**
* Responsible for handling the aspects related to hacking control consoles and capturing bases.
*/
-class HackCaptureActor(val taskResolver: ActorRef) extends Actor {
+class HackCaptureActor extends Actor {
private[this] val log = org.log4s.getLogger
private var clearTrigger: Cancellable = Default.Cancellable
diff --git a/src/main/scala/net/psforever/services/vehicle/support/TurretUpgrader.scala b/src/main/scala/net/psforever/services/vehicle/support/TurretUpgrader.scala
index d3709e71..fa93a129 100644
--- a/src/main/scala/net/psforever/services/vehicle/support/TurretUpgrader.scala
+++ b/src/main/scala/net/psforever/services/vehicle/support/TurretUpgrader.scala
@@ -1,10 +1,10 @@
// Copyright (c) 2017 PSForever
package net.psforever.services.vehicle.support
-import akka.actor.{ActorRef, Cancellable}
+import akka.actor.Cancellable
import net.psforever.objects.equipment.EquipmentSlot
import net.psforever.objects.{AmmoBox, Default, PlanetSideGameObject, Tool}
-import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
+import net.psforever.objects.guid._
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.turret.{FacilityTurret, TurretUpgrade, WeaponTurret}
import net.psforever.objects.vehicles.MountedWeapons
@@ -13,8 +13,8 @@ import net.psforever.types.PlanetSideGUID
import net.psforever.services.support.{SimilarityComparator, SupportActor, SupportActorCaseConversions}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
+import scala.concurrent.Future
import scala.concurrent.duration._
-import scala.util.Success
class TurretUpgrader extends SupportActor[TurretUpgrader.Entry] {
var task: Cancellable = Default.Cancellable
@@ -172,39 +172,34 @@ class TurretUpgrader extends SupportActor[TurretUpgrader.Entry] {
val oldBoxesTask = oldBoxes
.filterNot { box => newBoxes.exists(_ eq box) }
- .map(box => GUIDTask.UnregisterEquipment(box)(guid))
+ .map(box => GUIDTask.unregisterEquipment(guid, box))
.toList
- val newBoxesTask = TaskResolver.GiveTask(
- new Task() {
+ import scala.concurrent.ExecutionContext.Implicits.global
+ val newBoxesTask = TaskBundle(
+ new StraightforwardTask() {
private val localFunc: () => Unit = FinishUpgradingTurret(entry)
- override def isComplete = Task.Resolution.Success
-
- def Execute(resolver: ActorRef): Unit = {
- resolver ! Success(this)
- }
-
- override def onSuccess(): Unit = {
- super.onSuccess()
+ def action(): Future[Any] = {
localFunc()
+ Future(this)
}
},
newBoxes
.filterNot { box => oldBoxes.exists(_ eq box) }
- .map(box => GUIDTask.RegisterEquipment(box)(guid))
+ .map(box => GUIDTask.registerEquipment(guid, box))
.toList
)
- target.Zone.tasks ! TaskResolver.GiveTask(
- new Task() {
+ TaskWorkflow.execute(TaskBundle(
+ new StraightforwardTask() {
private val tasks = oldBoxesTask
- def Execute(resolver: ActorRef): Unit = {
- tasks.foreach { resolver ! _ }
- resolver ! Success(this)
+ def action(): Future[Any] = {
+ tasks.foreach { TaskWorkflow.execute }
+ Future(this)
}
},
- List(newBoxesTask)
- )
+ newBoxesTask
+ ))
}
/**
diff --git a/src/main/scala/net/psforever/zones/Zones.scala b/src/main/scala/net/psforever/zones/Zones.scala
index 3e9a4169..d35e6787 100644
--- a/src/main/scala/net/psforever/zones/Zones.scala
+++ b/src/main/scala/net/psforever/zones/Zones.scala
@@ -12,6 +12,7 @@ import io.circe.parser._
import net.psforever.objects.{GlobalDefinitions, LocalLockerItem, LocalProjectile}
import net.psforever.objects.ballistics.Projectile
import net.psforever.objects.definition.BasicDefinition
+import net.psforever.objects.guid.selector.{NumberSelector, RandomSelector, SpecificSelector}
import net.psforever.objects.serverobject.doors.{Door, DoorDefinition, SpawnTubeDoor}
import net.psforever.objects.serverobject.generator.Generator
import net.psforever.objects.serverobject.llu.{CaptureFlagSocket, CaptureFlagSocketDefinition}
@@ -55,6 +56,25 @@ object Zones {
}
}
+ private case class GuidNumberPool(
+ name: String,
+ start: Int,
+ max: Int,
+ selector: String
+ ) {
+ def getSelector() : NumberSelector = {
+ if (selector.equals("random")) new RandomSelector
+ else new SpecificSelector
+ }
+ }
+
+ private implicit val decodeNumberPool: Decoder[GuidNumberPool] = Decoder.forProduct4(
+ "Name",
+ "Start",
+ "Max",
+ "Selector"
+ )(GuidNumberPool.apply)
+
private implicit val decodeZoneMapEntity: Decoder[ZoneMapEntity] = Decoder.forProduct11(
"Id",
"ObjectName",
@@ -585,44 +605,84 @@ object Zones {
}
}
- lazy val zones: Seq[Zone] = ZoneInfo.values.map { info =>
- new Zone(info.id, zoneMaps.find(_.name == info.map.value).get, info.value) {
- override def init(implicit context: ActorContext): Unit = {
- super.init(context)
+ lazy val zones: Seq[Zone] = {
+ val defaultGuids =
+ try {
+ val res = Source.fromResource("guid-pools/default.json")
+ val json = res.mkString
+ res.close()
+ decode[Seq[GuidNumberPool]](json).toOption.get
+ } catch {
+ case _: Exception => Seq()
+ }
- if (!info.id.startsWith("tz")) {
- this.HotSpotCoordinateFunction = Zones.HotSpots.standardRemapping(info.map.scale, 80, 80)
- this.HotSpotTimeFunction = Zones.HotSpots.standardTimeRules
- Zones.initZoneAmenities(this)
+ ZoneInfo.values.map { info =>
+ val guids =
+ try {
+ val res = Source.fromResource(s"guid-pools/${info.id}.json")
+ val json = res.mkString
+ res.close()
+ val custom = decode[Seq[GuidNumberPool]](json).toOption.get
+ customizePools(defaultGuids, custom)
+ } catch {
+ case _: Exception => defaultGuids
}
- info.id match {
- case "home1" =>
- this.Buildings.values.foreach(_.Faction = PlanetSideEmpire.NC)
- case "home2" =>
- this.Buildings.values.foreach(_.Faction = PlanetSideEmpire.TR)
- case "home3" =>
- this.Buildings.values.foreach(_.Faction = PlanetSideEmpire.VS)
- case zoneid if zoneid.startsWith("c") =>
- this.map.cavern = true
- case _ => ()
- }
+ new Zone(info.id, zoneMaps.find(_.name.equals(info.map.value)).get, info.value) {
+ private val addPoolsFunc: () => Unit = addPools(guids, zone = this)
- // Set up warp gate factions aka "sanctuary link". Those names make no sense anymore, don't even ask.
- this.Buildings.foreach {
- case (_, building) if building.Name.startsWith("WG") =>
- building.Name match {
- case "WG_Amerish_to_Solsar" | "WG_Esamir_to_VSSanc" => building.Faction = PlanetSideEmpire.NC
- case "WG_Hossin_to_VSSanc" | "WG_Solsar_to_Amerish" => building.Faction = PlanetSideEmpire.TR
- case "WG_Ceryshen_to_Hossin" | "WG_Forseral_to_Solsar" => building.Faction = PlanetSideEmpire.VS
- case _ => ()
- }
- case _ => ()
+ override def SetupNumberPools() : Unit = addPoolsFunc()
+
+ override def init(implicit context: ActorContext): Unit = {
+ super.init(context)
+
+ if (!info.id.startsWith("tz")) {
+ this.HotSpotCoordinateFunction = Zones.HotSpots.standardRemapping(info.map.scale, 80, 80)
+ this.HotSpotTimeFunction = Zones.HotSpots.standardTimeRules
+ Zones.initZoneAmenities(this)
+ }
+
+ info.id match {
+ case "home1" =>
+ this.Buildings.values.foreach(_.Faction = PlanetSideEmpire.NC)
+ case "home2" =>
+ this.Buildings.values.foreach(_.Faction = PlanetSideEmpire.TR)
+ case "home3" =>
+ this.Buildings.values.foreach(_.Faction = PlanetSideEmpire.VS)
+ case zoneid if zoneid.startsWith("c") =>
+ this.map.cavern = true
+ case _ => ;
+ }
+
+ // Set up warp gate factions aka "sanctuary link". Those names make no sense anymore, don't even ask.
+ this.Buildings.foreach {
+ case (_, building) if building.Name.startsWith("WG") =>
+ building.Name match {
+ case "WG_Amerish_to_Solsar" | "WG_Esamir_to_VSSanc" => building.Faction = PlanetSideEmpire.NC
+ case "WG_Hossin_to_VSSanc" | "WG_Solsar_to_Amerish" => building.Faction = PlanetSideEmpire.TR
+ case "WG_Ceryshen_to_Hossin" | "WG_Forseral_to_Solsar" => building.Faction = PlanetSideEmpire.VS
+ case _ => ;
+ }
+ case _ => ;
+ }
}
}
}
}
+ private def customizePools(base: Seq[GuidNumberPool], custom: Seq[GuidNumberPool]): Seq[GuidNumberPool] = {
+ val exclude = custom.map { _.name }
+ val remainder = base.filterNot { entry => exclude.contains { entry.name } }
+ custom ++ remainder
+ }
+
+ private def addPools(guids: Seq[GuidNumberPool], zone: Zone)(): Unit = {
+ guids.foreach { entry =>
+ zone.AddPool(name = entry.name, (entry.start to entry.max).toList)
+ .foreach { _.Selector = entry.getSelector() }
+ }
+ }
+
def initZoneAmenities(zone: Zone): Unit = {
initResourceSilos(zone)
initWarpGates(zone)
diff --git a/src/test/scala/objects/DamageableTest.scala b/src/test/scala/objects/DamageableTest.scala
index cd0ee589..8c2719c4 100644
--- a/src/test/scala/objects/DamageableTest.scala
+++ b/src/test/scala/objects/DamageableTest.scala
@@ -1614,7 +1614,6 @@ class DamageableVehicleDestroyMountedTest extends FreedContextActorTest {
override def Activity = activityProbe.ref
override def AvatarEvents = avatarProbe.ref
override def VehicleEvents = vehicleProbe.ref
- override def tasks = catchall.ref
import akka.actor.typed.scaladsl.adapter._
this.actor = catchall.ref.toTyped[ZoneActor.Command]
}
diff --git a/src/test/scala/objects/DeployableBehaviorTest.scala b/src/test/scala/objects/DeployableBehaviorTest.scala
index 38e4703b..ada501a3 100644
--- a/src/test/scala/objects/DeployableBehaviorTest.scala
+++ b/src/test/scala/objects/DeployableBehaviorTest.scala
@@ -9,7 +9,7 @@ import net.psforever.actors.zone.ZoneActor
import net.psforever.objects.avatar.{Avatar, Certification, PlayerControl}
import net.psforever.objects.{ConstructionItem, Deployables, GlobalDefinitions, Player}
import net.psforever.objects.ce.{Deployable, DeployedItem}
-import net.psforever.objects.guid.{NumberPoolHub, TaskResolver}
+import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.zones.{Zone, ZoneDeployableActor, ZoneMap}
import net.psforever.packet.game._
@@ -141,7 +141,6 @@ class DeployableBehaviorSetupOwnedP2Test extends FreedContextActorTest {
override def Deployables: ActorRef = deployables
override def Players = List(avatar)
override def LivePlayers = List(player)
- override def tasks: ActorRef = eventsProbe.ref
this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command]
}
@@ -165,7 +164,7 @@ class DeployableBehaviorSetupOwnedP2Test extends FreedContextActorTest {
assert(!avatar.deployables.Contains(jmine), "owned setup test, 2 - avatar already owns deployable")
zone.Deployables ! Zone.Deployable.BuildByOwner(jmine, player, citem)
//assert(false, "test needs to be fixed")
- val eventsMsgs = eventsProbe.receiveN(8, 10.seconds)
+ val eventsMsgs = eventsProbe.receiveN(7, 10.seconds)
eventsMsgs.head match {
case AvatarServiceMessage(
"TestCharacter1",
@@ -220,11 +219,6 @@ class DeployableBehaviorSetupOwnedP2Test extends FreedContextActorTest {
case _ =>
assert(false, "owned setup test, 2 - construction tool not deleted")
}
- eventsMsgs(7) match {
- case TaskResolver.GiveTask(_, _) => ;
- case _ =>
- assert(false, "owned setup test, 2 - construction tool not unregistered")
- }
assert(player.Slot(slot = 0).Equipment.isEmpty, "owned setup test, 2 - player hand should be empty")
assert(deployableList.contains(jmine), "owned setup test, 2 - deployable not appended to list")
assert(avatar.deployables.Contains(jmine), "owned setup test, 2 - avatar does not own deployable")
@@ -244,7 +238,6 @@ class DeployableBehaviorDeconstructTest extends ActorTest {
GUID(guid)
override def AvatarEvents: ActorRef = eventsProbe.ref
override def LocalEvents: ActorRef = eventsProbe.ref
- override def tasks: ActorRef = eventsProbe.ref
override def Deployables: ActorRef = deployables
this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command]
@@ -261,7 +254,7 @@ class DeployableBehaviorDeconstructTest extends ActorTest {
assert(deployableList.contains(jmine), "deconstruct test - deployable not appended to list")
jmine.Actor ! Deployable.Deconstruct()
- val eventsMsgs = eventsProbe.receiveN(3, 10.seconds)
+ val eventsMsgs = eventsProbe.receiveN(2, 10.seconds)
eventsMsgs.head match {
case LocalServiceMessage("test", LocalAction.EliminateDeployable(`jmine`, PlanetSideGUID(1), Vector3(1,2,3), 2)) => ;
case _ => assert(false, "deconstruct test - not eliminating deployable")
@@ -277,10 +270,6 @@ class DeployableBehaviorDeconstructTest extends ActorTest {
) => ;
case _ => assert(false, "owned deconstruct test - not removing icon")
}
- eventsMsgs(2) match {
- case TaskResolver.GiveTask(_, _) => ;
- case _ => assert(false, "deconstruct test - not unregistering deployable")
- }
assert(!deployableList.contains(jmine), "deconstruct test - deployable not removed from list")
}
}
@@ -304,7 +293,6 @@ class DeployableBehaviorDeconstructOwnedTest extends FreedContextActorTest {
override def Deployables: ActorRef = deployables
override def Players = List(avatar)
override def LivePlayers = List(player)
- override def tasks: ActorRef = eventsProbe.ref
this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command]
}
@@ -324,12 +312,12 @@ class DeployableBehaviorDeconstructOwnedTest extends FreedContextActorTest {
"DeployableBehavior" should {
"deconstruct and alert owner" in {
zone.Deployables ! Zone.Deployable.BuildByOwner(jmine, player, citem)
- eventsProbe.receiveN(8, 10.seconds)
+ eventsProbe.receiveN(7, 10.seconds)
assert(deployableList.contains(jmine), "owned deconstruct test - deployable not appended to list")
assert(avatar.deployables.Contains(jmine), "owned deconstruct test - avatar does not own deployable")
jmine.Actor ! Deployable.Deconstruct()
- val eventsMsgs = eventsProbe.receiveN(4, 10.seconds)
+ val eventsMsgs = eventsProbe.receiveN(3, 10.seconds)
eventsMsgs.head match {
case LocalServiceMessage("test", LocalAction.EliminateDeployable(`jmine`, PlanetSideGUID(1), Vector3(1,2,3), 2)) => ;
case _ => assert(false, "owned deconstruct test - not eliminating deployable")
@@ -349,10 +337,6 @@ class DeployableBehaviorDeconstructOwnedTest extends FreedContextActorTest {
) => ;
case _ => assert(false, "owned deconstruct test - not removing icon")
}
- eventsMsgs(3) match {
- case TaskResolver.GiveTask(_, _) => ;
- case _ => assert(false, "owned deconstruct test - not unregistering deployable")
- }
assert(deployableList.isEmpty, "owned deconstruct test - deployable still in list")
assert(!avatar.deployables.Contains(jmine), "owned deconstruct test - avatar still owns deployable")
diff --git a/src/test/scala/objects/DeployableTest.scala b/src/test/scala/objects/DeployableTest.scala
index 7a9713f7..38562f44 100644
--- a/src/test/scala/objects/DeployableTest.scala
+++ b/src/test/scala/objects/DeployableTest.scala
@@ -330,7 +330,6 @@ class ExplosiveDeployableJammerTest extends ActorTest {
override def Deployables: ActorRef = deployables
override def Players = List(avatar1, avatar2)
override def LivePlayers = List(player1, player2)
- override def tasks: ActorRef = eventsProbe.ref
}
player1.Spawn()
player2.Spawn()
@@ -408,7 +407,6 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest {
override def Deployables: ActorRef = deployables
override def Players = List(avatar1, avatar2)
override def LivePlayers = List(player1, player2)
- override def tasks: ActorRef = eventsProbe.ref
this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command]
}
@@ -518,7 +516,6 @@ class ExplosiveDeployableDestructionTest extends ActorTest {
override def Deployables: ActorRef = deployables
override def Players = List(avatar1, avatar2)
override def LivePlayers = List(player1, player2)
- override def tasks: ActorRef = eventsProbe.ref
}
player1.Spawn()
player1.Actor = player1Probe.ref
diff --git a/src/test/scala/objects/OrbitalShuttlePadTest.scala b/src/test/scala/objects/OrbitalShuttlePadTest.scala
index 877bef4e..82db9ac3 100644
--- a/src/test/scala/objects/OrbitalShuttlePadTest.scala
+++ b/src/test/scala/objects/OrbitalShuttlePadTest.scala
@@ -2,13 +2,11 @@
package objects
import akka.actor.{ActorRef, Props}
-import akka.routing.RandomPool
import akka.testkit.TestProbe
import base.FreedContextActorTest
import net.psforever.actors.zone.{BuildingActor, ZoneActor}
-import net.psforever.objects.guid.actor.UniqueNumberSystem
import net.psforever.objects.{GlobalDefinitions, Vehicle}
-import net.psforever.objects.guid.{NumberPoolHub, TaskResolver}
+import net.psforever.objects.guid.{NumberPoolHub, UniqueNumberOps, UniqueNumberSetup}
import net.psforever.objects.guid.source.MaxNumberSource
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.serverobject.shuttle.{OrbitalShuttle, OrbitalShuttlePad, OrbitalShuttlePadControl, ShuttleAmenity}
@@ -23,7 +21,7 @@ import scala.collection.concurrent.TrieMap
import scala.collection.mutable.ListBuffer
import scala.concurrent.duration._
-class OrbitalShuttlePadControltest extends FreedContextActorTest {
+class OrbitalShuttlePadControlTest extends FreedContextActorTest {
import akka.actor.typed.scaladsl.adapter._
system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id)
val services = ServiceManager.boot(system)
@@ -32,22 +30,17 @@ class OrbitalShuttlePadControltest extends FreedContextActorTest {
expectNoMessage(1000 milliseconds)
var buildingMap = new TrieMap[Int, Building]()
val vehicles = ListBuffer[Vehicle]()
- val guid = new NumberPoolHub(new MaxNumberSource(max = 15))
- guid.AddPool("dynamic", (11 to 15).toList)
+ val guid = new NumberPoolHub(new MaxNumberSource(max = 20))
+ guid.AddPool("vehicles", (11 to 15).toList)
+ guid.AddPool("tools", (16 to 19).toList)
val catchall = new TestProbe(system).ref
- val resolver = context.actorOf(RandomPool(1).props(Props[TaskResolver]()), s"test-taskResolver")
- val uns = context.actorOf(
- RandomPool(1).props(
- Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystem.AllocateNumberPoolActors(this.guid))
- ),
- s"test-uns"
- )
+ val unops = new UniqueNumberOps(guid, UniqueNumberSetup.AllocateNumberPoolActors(context, guid))
val zone = new Zone("test", new ZoneMap("test-map"), 0) {
val transport: ActorRef = context.actorOf(Props(classOf[ZoneVehicleActor], this, vehicles), s"zone-test-vehicles")
override def SetupNumberPools() = {}
GUID(guid)
- override def GUID = { uns }
+ override def GUID = { unops }
override def AvatarEvents = catchall
override def LocalEvents = catchall
override def VehicleEvents = catchall
@@ -55,7 +48,6 @@ class OrbitalShuttlePadControltest extends FreedContextActorTest {
override def Transport = { transport }
override def Vehicles = { vehicles.toList }
override def Buildings = { buildingMap.toMap }
- override def tasks = { resolver }
import akka.actor.typed.scaladsl.adapter._
this.actor = new TestProbe(system).ref.toTyped[ZoneActor.Command]
diff --git a/src/test/scala/objects/TaskWorkflowTest.scala b/src/test/scala/objects/TaskWorkflowTest.scala
new file mode 100644
index 00000000..2ab736a4
--- /dev/null
+++ b/src/test/scala/objects/TaskWorkflowTest.scala
@@ -0,0 +1,116 @@
+// Copyright (c) 2021 PSForever
+package objects
+
+import net.psforever.objects.guid.{Task, TaskBundle, TaskWorkflow}
+import org.scalatest.flatspec.AsyncFlatSpec
+
+import scala.concurrent.Future
+import scala.util.Failure
+
+class AsyncTaskWorkflowTest extends AsyncFlatSpec {
+ case class StringAppendTask(product: StringBuilder, str: String) extends Task {
+ def action() = { Future({ product.append(str) }) }
+ def undo() = {
+ val index = product.indexOf(str)
+ product.replace(index, index + str.length, "[successful task undo]")
+ }
+ def isSuccessful() = { product.indexOf(str) > -1 }
+ }
+
+ case class FailedStringAppendTask(product: StringBuilder, str: String) extends Task {
+ def action() = { Future(Failure(new Exception("intentional failure"))) }
+ def undo() = {
+ val index = product.indexOf(str)
+ product.replace(index, index + str.length, "[failed task undo]")
+ }
+ def isSuccessful() = { product.indexOf(str) > -1 }
+ }
+
+ behavior of "TaskWorkFlow"
+
+ it should "append a string as a task" in {
+ val test: StringBuilder = new StringBuilder()
+ assert(test.mkString.isEmpty)
+ val result = TaskWorkflow.execute(TaskBundle(StringAppendTask(test, "hello")))
+ result map { _ =>
+ assert(test.mkString.equals("hello"), "async result does not equal 'hello'")
+ }
+ }
+
+ it should "append the strings in order of subtask then main task" in {
+ val test: StringBuilder = new StringBuilder()
+ assert(test.mkString.isEmpty)
+ val result = TaskWorkflow.execute(TaskBundle(StringAppendTask(test, " world"), StringAppendTask(test, "hello")))
+ result map { _ =>
+ assert(test.mkString.equals("hello world"), "async result does not equal 'hello world'")
+ }
+ }
+
+ it should "append the strings in order of subtasks then main task, with the subtasks being in either order" in {
+ val test: StringBuilder = new StringBuilder()
+ assert(test.mkString.isEmpty)
+ val result = TaskWorkflow.execute(TaskBundle(
+ StringAppendTask(test, " world"),
+ Seq(
+ TaskBundle(StringAppendTask(test, " hello")),
+ TaskBundle(StringAppendTask(test, " or goodbye"))
+ )
+ ))
+ result map { _ =>
+ val output = test.mkString
+ assert(
+ output.equals(" or goodbye hello world") || output.equals(" hello or goodbye world"),
+ s"async result '$output' does not equal either pattern"
+ )
+ }
+ }
+
+ it should "if a task fails, do not undo it" in {
+ val test: StringBuilder = new StringBuilder()
+ assert(test.mkString.isEmpty)
+ val result = TaskWorkflow.execute(TaskBundle(FailedStringAppendTask(test, " world")))
+ result map { _ =>
+ val output = test.mkString
+ assert(output.equals(""),"async result was written when should have not been written")
+ //see implementation of FailedStringAppendTask.undo
+ }
+ }
+
+ it should "if a middling subtask fails, its parent task will not be executed or undone, but its own subtask will be undone (1)" in {
+ val test: StringBuilder = new StringBuilder()
+ assert(test.mkString.isEmpty)
+ val result = TaskWorkflow.execute(TaskBundle(
+ StringAppendTask(test, " world"),
+ TaskBundle(FailedStringAppendTask(test, "hello"), StringAppendTask(test, " or goodbye")))
+ )
+ result map { _ =>
+ val output = test.mkString
+ assert(output.equals("[successful task undo]"),s"async result, formerly successful, was written as if it had failed - $output")
+ //see implementation of StringAppendTask.undo
+ }
+ }
+
+ it should "if a middling subtask fails, its parent task will not be executed or undone, but its own subtasks will be undone (2)" in {
+ val test: StringBuilder = new StringBuilder()
+ assert(test.mkString.isEmpty)
+ val result = TaskWorkflow.execute(TaskBundle(
+ StringAppendTask(test, " world"),
+ TaskBundle(FailedStringAppendTask(test, "hello"), List(
+ TaskBundle(StringAppendTask(test, " or goodbye")),
+ TaskBundle(StringAppendTask(test, " or something"))
+ ))
+ ))
+ result map { _ =>
+ val output = test.mkString
+ assert(
+ output.equals("[successful task undo][successful task undo]"),
+ s"async result, formerly successful, was written as if it had failed - $output"
+ )
+ //see implementation of StringAppendTask.undo
+ }
+ }
+}
+
+object TaskWorkflowTest {
+ /** placeholder */
+}
diff --git a/src/test/scala/objects/VehicleControlTest.scala b/src/test/scala/objects/VehicleControlTest.scala
index 8379868f..43645b8f 100644
--- a/src/test/scala/objects/VehicleControlTest.scala
+++ b/src/test/scala/objects/VehicleControlTest.scala
@@ -212,7 +212,6 @@ class VehicleControlPrepareForDeletionMountedCargoTest extends FreedContextActor
override def SetupNumberPools(): Unit = {}
override def VehicleEvents = vehicleProbe.ref
- override def tasks = catchall.ref
}
zone.actor = system.spawn(ZoneActor(zone), "test-zone-actor")
// crappy workaround but without it the zone doesn't get initialized in time
diff --git a/src/test/scala/objects/ZoneTest.scala b/src/test/scala/objects/ZoneTest.scala
index 77a5bd78..6c4f4dfe 100644
--- a/src/test/scala/objects/ZoneTest.scala
+++ b/src/test/scala/objects/ZoneTest.scala
@@ -130,7 +130,7 @@ class ZoneActorTest extends ActorTest {
zone.actor = system.spawn(ZoneActor(zone), "test-add-pool-actor-init")
expectNoMessage(Duration.create(500, "ms"))
- assert(!zone.AddPool("test1", 1 to 2))
+ assert(zone.AddPool("test1", 1 to 2).isEmpty)
}
"refuse to remove number pools after the Actor is started" in {
diff --git a/src/test/scala/objects/guidtask/GUIDTaskRegisterAmmoTest.scala b/src/test/scala/objects/guidtask/GUIDTaskRegisterAmmoTest.scala
index 089f3d53..34bd51bd 100644
--- a/src/test/scala/objects/guidtask/GUIDTaskRegisterAmmoTest.scala
+++ b/src/test/scala/objects/guidtask/GUIDTaskRegisterAmmoTest.scala
@@ -3,19 +3,21 @@ package objects.guidtask
import base.ActorTest
import net.psforever.objects._
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
+
+import scala.concurrent.duration._
class GUIDTaskRegisterAmmoTest extends ActorTest {
"RegisterEquipment -> RegisterObjectTask" in {
- val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = AmmoBox(GlobalDefinitions.energy_cell)
+ val (_, uns, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = AmmoBox(GlobalDefinitions.energy_cell)
assert(!obj.HasGUID)
- taskResolver ! TaskResolver.GiveTask(
+ TaskWorkflow.execute(TaskBundle(
new GUIDTaskTest.RegisterTestTask(probe.ref),
- List(GUIDTask.RegisterEquipment(obj)(uns))
- )
- probe.expectMsg(scala.util.Success)
+ GUIDTask.registerEquipment(uns, obj)
+ ))
+ probe.expectMsg(5.second, scala.util.Success(true))
assert(obj.HasGUID)
}
}
diff --git a/src/test/scala/objects/guidtask/GUIDTaskRegisterAvatarTest.scala b/src/test/scala/objects/guidtask/GUIDTaskRegisterAvatarTest.scala
index 4f4171fa..09138409 100644
--- a/src/test/scala/objects/guidtask/GUIDTaskRegisterAvatarTest.scala
+++ b/src/test/scala/objects/guidtask/GUIDTaskRegisterAvatarTest.scala
@@ -4,15 +4,17 @@ package objects.guidtask
import base.ActorTest
import net.psforever.objects._
import net.psforever.objects.avatar.Avatar
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
import net.psforever.objects.locker.LockerEquipment
import net.psforever.types.{CharacterSex, CharacterVoice, PlanetSideEmpire}
+import scala.concurrent.duration._
+
class GUIDTaskRegisterAvatarTest extends ActorTest {
"RegisterAvatar" in {
- val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = Player(Avatar(0, "test", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute))
- val obj_wep = Tool(GlobalDefinitions.beamer)
+ val (_, uns, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = Player(Avatar(0, "test", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute))
+ val obj_wep = Tool(GlobalDefinitions.beamer)
obj.Slot(0).Equipment = obj_wep
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj_wep.AmmoSlots.head.Box = obj_wep_ammo
@@ -28,11 +30,11 @@ class GUIDTaskRegisterAvatarTest extends ActorTest {
assert(!obj_inv_ammo.HasGUID)
assert(!obj_locker.HasGUID)
assert(obj_locker_ammo.HasGUID)
- taskResolver ! TaskResolver.GiveTask(
+ TaskWorkflow.execute(TaskBundle(
new GUIDTaskTest.RegisterTestTask(probe.ref),
- List(GUIDTask.RegisterAvatar(obj)(uns))
- )
- probe.expectMsg(scala.util.Success)
+ GUIDTask.registerAvatar(uns, obj)
+ ))
+ probe.expectMsg(5.second, scala.util.Success(true))
assert(obj.HasGUID)
assert(obj_wep.HasGUID)
assert(obj_wep_ammo.HasGUID)
diff --git a/src/test/scala/objects/guidtask/GUIDTaskRegisterObjectTest.scala b/src/test/scala/objects/guidtask/GUIDTaskRegisterObjectTest.scala
index 7f424f30..8377378e 100644
--- a/src/test/scala/objects/guidtask/GUIDTaskRegisterObjectTest.scala
+++ b/src/test/scala/objects/guidtask/GUIDTaskRegisterObjectTest.scala
@@ -2,19 +2,21 @@
package objects.guidtask
import base.ActorTest
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
+
+import scala.concurrent.duration._
class GUIDTaskRegisterObjectTest extends ActorTest {
"RegisterObjectTask" in {
- val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = new GUIDTaskTest.TestObject
+ val (_, uns, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = new GUIDTaskTest.TestObject
assert(!obj.HasGUID)
- taskResolver ! TaskResolver.GiveTask(
+ TaskWorkflow.execute(TaskBundle(
new GUIDTaskTest.RegisterTestTask(probe.ref),
- List(GUIDTask.RegisterObjectTask(obj)(uns))
- )
- probe.expectMsg(scala.util.Success)
+ GUIDTask.registerObject(uns, obj)
+ ))
+ probe.expectMsg(5.second, scala.util.Success(true))
assert(obj.HasGUID)
}
}
diff --git a/src/test/scala/objects/guidtask/GUIDTaskRegisterPlayerTest.scala b/src/test/scala/objects/guidtask/GUIDTaskRegisterPlayerTest.scala
index bb9ba900..bbb945d1 100644
--- a/src/test/scala/objects/guidtask/GUIDTaskRegisterPlayerTest.scala
+++ b/src/test/scala/objects/guidtask/GUIDTaskRegisterPlayerTest.scala
@@ -4,15 +4,17 @@ package objects.guidtask
import base.ActorTest
import net.psforever.objects._
import net.psforever.objects.avatar.Avatar
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
import net.psforever.objects.locker.LockerEquipment
import net.psforever.types.{CharacterSex, CharacterVoice, PlanetSideEmpire}
+import scala.concurrent.duration._
+
class GUIDTaskRegisterPlayerTest extends ActorTest {
"RegisterPlayer" in {
- val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = Player(Avatar(0, "test", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute))
- val obj_wep = Tool(GlobalDefinitions.beamer)
+ val (_, uns, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = Player(Avatar(0, "test", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute))
+ val obj_wep = Tool(GlobalDefinitions.beamer)
obj.Slot(0).Equipment = obj_wep
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj_wep.AmmoSlots.head.Box = obj_wep_ammo
@@ -28,11 +30,11 @@ class GUIDTaskRegisterPlayerTest extends ActorTest {
assert(!obj_inv_ammo.HasGUID)
assert(!obj_locker.HasGUID)
assert(obj_locker_ammo.HasGUID)
- taskResolver ! TaskResolver.GiveTask(
+ TaskWorkflow.execute(TaskBundle(
new GUIDTaskTest.RegisterTestTask(probe.ref),
- List(GUIDTask.RegisterPlayer(obj)(uns))
- )
- probe.expectMsg(scala.util.Success)
+ GUIDTask.registerPlayer(uns, obj)
+ ))
+ probe.expectMsg(5.second, scala.util.Success(true))
assert(obj.HasGUID)
assert(obj_wep.HasGUID)
assert(obj_wep_ammo.HasGUID)
diff --git a/src/test/scala/objects/guidtask/GUIDTaskRegisterToolTest.scala b/src/test/scala/objects/guidtask/GUIDTaskRegisterToolTest.scala
index 9bf0a1ff..243e6afc 100644
--- a/src/test/scala/objects/guidtask/GUIDTaskRegisterToolTest.scala
+++ b/src/test/scala/objects/guidtask/GUIDTaskRegisterToolTest.scala
@@ -3,21 +3,23 @@ package objects.guidtask
import base.ActorTest
import net.psforever.objects._
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
+
+import scala.concurrent.duration._
class GUIDTaskRegisterToolTest extends ActorTest {
"RegisterEquipment -> RegisterTool" in {
- val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = Tool(GlobalDefinitions.beamer)
+ val (_, uns, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = Tool(GlobalDefinitions.beamer)
obj.AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.energy_cell)
assert(!obj.HasGUID)
assert(!obj.AmmoSlots.head.Box.HasGUID)
- taskResolver ! TaskResolver.GiveTask(
+ TaskWorkflow.execute(TaskBundle(
new GUIDTaskTest.RegisterTestTask(probe.ref),
- List(GUIDTask.RegisterEquipment(obj)(uns))
- )
- probe.expectMsg(scala.util.Success)
+ GUIDTask.registerEquipment(uns, obj)
+ ))
+ probe.expectMsg(5.second, scala.util.Success(true))
assert(obj.HasGUID)
assert(obj.AmmoSlots.head.Box.HasGUID)
}
diff --git a/src/test/scala/objects/guidtask/GUIDTaskRegisterTurretTest.scala b/src/test/scala/objects/guidtask/GUIDTaskRegisterTurretTest.scala
index c53cd747..0c5f2131 100644
--- a/src/test/scala/objects/guidtask/GUIDTaskRegisterTurretTest.scala
+++ b/src/test/scala/objects/guidtask/GUIDTaskRegisterTurretTest.scala
@@ -3,25 +3,27 @@ package objects.guidtask
import base.ActorTest
import net.psforever.objects._
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
+
+import scala.concurrent.duration._
class GUIDTaskRegisterTurretTest extends ActorTest {
"RegisterDeployableTurret" in {
- val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = new TurretDeployable(GlobalDefinitions.portable_manned_turret_vs)
- val obj_wep = obj.Weapons(1).Equipment.get
- val obj_ammo = obj_wep.asInstanceOf[Tool].AmmoSlot.Box
- val obj_res = obj.Inventory.Items.map(_.obj)
+ val (_, uns, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = new TurretDeployable(GlobalDefinitions.portable_manned_turret_vs)
+ val obj_wep = obj.Weapons(1).Equipment.get
+ val obj_ammo = obj_wep.asInstanceOf[Tool].AmmoSlot.Box
+ val obj_res = obj.Inventory.Items.map(_.obj)
assert(!obj.HasGUID)
assert(!obj_wep.HasGUID)
assert(!obj_ammo.HasGUID)
obj_res.foreach(box => !box.HasGUID)
- taskResolver ! TaskResolver.GiveTask(
+ TaskWorkflow.execute(TaskBundle(
new GUIDTaskTest.RegisterTestTask(probe.ref),
- List(GUIDTask.RegisterDeployableTurret(obj)(uns))
- )
- probe.expectMsg(scala.util.Success)
+ GUIDTask.registerDeployableTurret(uns, obj)
+ ))
+ probe.expectMsg(5.second, scala.util.Success(true))
assert(obj.HasGUID)
assert(obj_wep.HasGUID)
assert(obj_ammo.HasGUID)
diff --git a/src/test/scala/objects/guidtask/GUIDTaskRegisterVehicleTest.scala b/src/test/scala/objects/guidtask/GUIDTaskRegisterVehicleTest.scala
index f33e7b71..a0416c15 100644
--- a/src/test/scala/objects/guidtask/GUIDTaskRegisterVehicleTest.scala
+++ b/src/test/scala/objects/guidtask/GUIDTaskRegisterVehicleTest.scala
@@ -3,13 +3,15 @@ package objects.guidtask
import base.ActorTest
import net.psforever.objects._
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
+
+import scala.concurrent.duration._
class GUIDTaskRegisterVehicleTest extends ActorTest {
"RegisterVehicle" in {
- val (_, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = Vehicle(GlobalDefinitions.fury)
- val obj_wep = obj.WeaponControlledFromSeat(0).get
+ val (_, uns, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = Vehicle(GlobalDefinitions.fury)
+ val obj_wep = obj.WeaponControlledFromSeat(0).get
val obj_wep_ammo = (obj.WeaponControlledFromSeat(0).get.asInstanceOf[Tool].AmmoSlots.head.Box =
AmmoBox(GlobalDefinitions.hellfire_ammo)).get
obj.Trunk += 30 -> AmmoBox(GlobalDefinitions.hellfire_ammo)
@@ -19,11 +21,11 @@ class GUIDTaskRegisterVehicleTest extends ActorTest {
assert(!obj_wep.HasGUID)
assert(!obj_wep_ammo.HasGUID)
assert(!obj_trunk_ammo.HasGUID)
- taskResolver ! TaskResolver.GiveTask(
+ TaskWorkflow.execute(TaskBundle(
new GUIDTaskTest.RegisterTestTask(probe.ref),
- List(GUIDTask.RegisterVehicle(obj)(uns))
- )
- probe.expectMsg(scala.util.Success)
+ GUIDTask.registerVehicle(uns, obj)
+ ))
+ probe.expectMsg(5.second, scala.util.Success(true))
assert(obj.HasGUID)
assert(obj_wep.HasGUID)
assert(obj_wep_ammo.HasGUID)
diff --git a/src/test/scala/objects/guidtask/GUIDTaskTest.scala b/src/test/scala/objects/guidtask/GUIDTaskTest.scala
index 935f91cd..d2d94a61 100644
--- a/src/test/scala/objects/guidtask/GUIDTaskTest.scala
+++ b/src/test/scala/objects/guidtask/GUIDTaskTest.scala
@@ -2,50 +2,54 @@
package objects.guidtask
import java.util.logging.LogManager
+
import scala.util.Success
import akka.actor.{ActorRef, ActorSystem, Props}
import akka.testkit.TestProbe
import net.psforever.objects.entity.IdentifiableEntity
-import net.psforever.objects.guid.actor.{NumberPoolActor, UniqueNumberSystem}
import net.psforever.objects.guid.selector.RandomSelector
import net.psforever.objects.guid.source.MaxNumberSource
-import net.psforever.objects.guid.{NumberPoolHub, Task, TaskResolver}
+import net.psforever.objects.guid.uns.NumberPoolActor
+import net.psforever.objects.guid.{NumberPoolHub, StraightforwardTask, UniqueNumberOps}
+
+import scala.concurrent.Future
object GUIDTaskTest {
class TestObject extends IdentifiableEntity
- class RegisterTestTask(probe: ActorRef) extends Task {
- def Execute(resolver: ActorRef): Unit = {
- probe ! Success
- resolver ! Success(this)
+ class RegisterTestTask(probe: ActorRef) extends StraightforwardTask {
+ def action(): Future[Any] = {
+ probe ! Success(true)
+ Future(this)(scala.concurrent.ExecutionContext.Implicits.global)
}
}
- def CommonTestSetup(implicit system: ActorSystem): (NumberPoolHub, ActorRef, ActorRef, TestProbe) = {
- import akka.actor.Props
- import akka.routing.RandomPool
+ def CommonTestSetup(implicit system: ActorSystem): (NumberPoolHub, UniqueNumberOps, TestProbe) = {
import akka.testkit.TestProbe
- val guid: NumberPoolHub = new NumberPoolHub(new MaxNumberSource(110))
- guid.AddPool("dynamic", (1 to 100).toList).Selector = new RandomSelector //TODO name is hardcoded for now
- val uns = system.actorOf(
- RandomPool(25).props(Props(classOf[UniqueNumberSystem], guid, GUIDTaskTest.AllocateNumberPoolActors(guid))),
- "uns"
- )
- val taskResolver = system.actorOf(RandomPool(15).props(Props[TaskResolver]()), "resolver")
+ val guid: NumberPoolHub = new NumberPoolHub(new MaxNumberSource(90))
+ guid.AddPool("players", (1 to 10).toList).Selector = new RandomSelector
+ guid.AddPool("lockers", (11 to 20).toList).Selector = new RandomSelector
+ guid.AddPool("ammo", (21 to 30).toList).Selector = new RandomSelector
+ guid.AddPool("tools", (31 to 40).toList).Selector = new RandomSelector
+ guid.AddPool("vehicles", (41 to 50).toList).Selector = new RandomSelector
+ guid.AddPool("terminals", (51 to 60).toList).Selector = new RandomSelector
+ guid.AddPool("items", (61 to 70).toList).Selector = new RandomSelector
+ guid.AddPool("deployables", (71 to 80).toList).Selector = new RandomSelector
+ val uns = new UniqueNumberOps(guid, AllocateNumberPoolActors(guid)(system))
LogManager.getLogManager.reset() //suppresses any internal loggers created by the above elements
- (guid, uns, taskResolver, TestProbe())
+ (guid, uns, TestProbe())
}
/**
- * @see `UniqueNumberSystem.AllocateNumberPoolActors(NumberPoolHub)(implicit ActorContext)`
+ * @see `UniqueNumberSetup.AllocateNumberPoolActors(NumberPoolHub)(implicit ActorContext)`
*/
def AllocateNumberPoolActors(poolSource: NumberPoolHub)(implicit system: ActorSystem): Map[String, ActorRef] = {
poolSource.Pools
- .map({
- case ((pname, pool)) =>
+ .map {
+ case (pname, pool) =>
pname -> system.actorOf(Props(classOf[NumberPoolActor], pool), pname)
- })
+ }
.toMap
}
}
diff --git a/src/test/scala/objects/guidtask/GUIDTaskUnregisterAmmoTest.scala b/src/test/scala/objects/guidtask/GUIDTaskUnregisterAmmoTest.scala
index fb15d1b1..4dc90d7b 100644
--- a/src/test/scala/objects/guidtask/GUIDTaskUnregisterAmmoTest.scala
+++ b/src/test/scala/objects/guidtask/GUIDTaskUnregisterAmmoTest.scala
@@ -3,20 +3,22 @@ package objects.guidtask
import base.ActorTest
import net.psforever.objects._
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
+
+import scala.concurrent.duration._
class GUIDTaskUnregisterAmmoTest extends ActorTest {
"UnregisterEquipment -> UnregisterObjectTask" in {
- val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = AmmoBox(GlobalDefinitions.energy_cell)
- guid.register(obj, "dynamic")
+ val (guid, uns, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = AmmoBox(GlobalDefinitions.energy_cell)
+ guid.register(obj, name = "ammo")
assert(obj.HasGUID)
- taskResolver ! TaskResolver.GiveTask(
+ TaskWorkflow.execute(TaskBundle(
new GUIDTaskTest.RegisterTestTask(probe.ref),
- List(GUIDTask.UnregisterEquipment(obj)(uns))
- )
- probe.expectMsg(scala.util.Success)
+ GUIDTask.unregisterEquipment(uns, obj)
+ ))
+ probe.expectMsg(5.second, scala.util.Success(true))
assert(!obj.HasGUID)
}
}
diff --git a/src/test/scala/objects/guidtask/GUIDTaskUnregisterAvatarTest.scala b/src/test/scala/objects/guidtask/GUIDTaskUnregisterAvatarTest.scala
index 9121e317..53c7d919 100644
--- a/src/test/scala/objects/guidtask/GUIDTaskUnregisterAvatarTest.scala
+++ b/src/test/scala/objects/guidtask/GUIDTaskUnregisterAvatarTest.scala
@@ -4,15 +4,17 @@ package objects.guidtask
import base.ActorTest
import net.psforever.objects._
import net.psforever.objects.avatar.Avatar
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
import net.psforever.objects.locker.LockerEquipment
import net.psforever.types.{CharacterSex, CharacterVoice, PlanetSideEmpire}
+import scala.concurrent.duration._
+
class GUIDTaskUnregisterAvatarTest extends ActorTest {
"UnregisterAvatar" in {
- val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = Player(Avatar(0, "test", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute))
- val obj_wep = Tool(GlobalDefinitions.beamer)
+ val (guid, uns, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = Player(Avatar(0, "test", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute))
+ val obj_wep = Tool(GlobalDefinitions.beamer)
obj.Slot(0).Equipment = obj_wep
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj_wep.AmmoSlots.head.Box = obj_wep_ammo
@@ -21,12 +23,12 @@ class GUIDTaskUnregisterAvatarTest extends ActorTest {
val obj_locker = obj.Slot(5).Equipment.get
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj_locker.asInstanceOf[LockerEquipment].Inventory += 0 -> obj_locker_ammo
- guid.register(obj, "dynamic")
- guid.register(obj_wep, "dynamic")
- guid.register(obj_wep_ammo, "dynamic")
- guid.register(obj_inv_ammo, "dynamic")
- guid.register(obj_locker, "dynamic")
- guid.register(obj_locker_ammo, "dynamic")
+ guid.register(obj, name = "players")
+ guid.register(obj_wep, name = "tools")
+ guid.register(obj_wep_ammo, name = "ammo")
+ guid.register(obj_inv_ammo, name = "ammo")
+ guid.register(obj_locker, name = "lockers")
+ guid.register(obj_locker_ammo, name = "amoo")
assert(obj.HasGUID)
assert(obj_wep.HasGUID)
@@ -34,11 +36,11 @@ class GUIDTaskUnregisterAvatarTest extends ActorTest {
assert(obj_inv_ammo.HasGUID)
assert(obj_locker.HasGUID)
assert(obj_locker_ammo.HasGUID)
- taskResolver ! TaskResolver.GiveTask(
+ TaskWorkflow.execute(TaskBundle(
new GUIDTaskTest.RegisterTestTask(probe.ref),
- List(GUIDTask.UnregisterAvatar(obj)(uns))
- )
- probe.expectMsg(scala.util.Success)
+ GUIDTask.unregisterAvatar(uns, obj)
+ ))
+ probe.expectMsg(5.second, scala.util.Success(true))
assert(!obj.HasGUID)
assert(!obj_wep.HasGUID)
assert(!obj_wep_ammo.HasGUID)
diff --git a/src/test/scala/objects/guidtask/GUIDTaskUnregisterObjectTest.scala b/src/test/scala/objects/guidtask/GUIDTaskUnregisterObjectTest.scala
index 3318b579..af8c4084 100644
--- a/src/test/scala/objects/guidtask/GUIDTaskUnregisterObjectTest.scala
+++ b/src/test/scala/objects/guidtask/GUIDTaskUnregisterObjectTest.scala
@@ -2,20 +2,22 @@
package objects.guidtask
import base.ActorTest
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
+
+import scala.concurrent.duration._
class GUIDTaskUnregisterObjectTest extends ActorTest {
"UnregisterObjectTask" in {
- val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = new GUIDTaskTest.TestObject
- guid.register(obj, "dynamic")
+ val (guid, uns, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = new GUIDTaskTest.TestObject
+ guid.register(obj)
assert(obj.HasGUID)
- taskResolver ! TaskResolver.GiveTask(
+ TaskWorkflow.execute(TaskBundle(
new GUIDTaskTest.RegisterTestTask(probe.ref),
- List(GUIDTask.UnregisterObjectTask(obj)(uns))
- )
- probe.expectMsg(scala.util.Success)
+ GUIDTask.unregisterObject(uns, obj)
+ ))
+ probe.expectMsg(5.second, scala.util.Success(true))
assert(!obj.HasGUID)
}
}
diff --git a/src/test/scala/objects/guidtask/GUIDTaskUnregisterPlayerTest.scala b/src/test/scala/objects/guidtask/GUIDTaskUnregisterPlayerTest.scala
index a10df710..7192ec3a 100644
--- a/src/test/scala/objects/guidtask/GUIDTaskUnregisterPlayerTest.scala
+++ b/src/test/scala/objects/guidtask/GUIDTaskUnregisterPlayerTest.scala
@@ -4,15 +4,17 @@ package objects.guidtask
import base.ActorTest
import net.psforever.objects._
import net.psforever.objects.avatar.Avatar
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
import net.psforever.objects.locker.LockerEquipment
import net.psforever.types.{CharacterSex, CharacterVoice, PlanetSideEmpire}
+import scala.concurrent.duration._
+
class GUIDTaskUnregisterPlayerTest extends ActorTest {
"UnregisterPlayer" in {
- val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = Player(Avatar(0, "test", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute))
- val obj_wep = Tool(GlobalDefinitions.beamer)
+ val (guid, uns, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = Player(Avatar(0, "test", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute))
+ val obj_wep = Tool(GlobalDefinitions.beamer)
obj.Slot(0).Equipment = obj_wep
val obj_wep_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj_wep.AmmoSlots.head.Box = obj_wep_ammo
@@ -21,12 +23,12 @@ class GUIDTaskUnregisterPlayerTest extends ActorTest {
val obj_locker = obj.Slot(5).Equipment.get
val obj_locker_ammo = AmmoBox(GlobalDefinitions.energy_cell)
obj_locker.asInstanceOf[LockerEquipment].Inventory += 0 -> obj_locker_ammo
- guid.register(obj, "dynamic")
- guid.register(obj_wep, "dynamic")
- guid.register(obj_wep_ammo, "dynamic")
- guid.register(obj_inv_ammo, "dynamic")
- guid.register(obj_locker, "dynamic")
- guid.register(obj_locker_ammo, "dynamic")
+ guid.register(obj, name = "players")
+ guid.register(obj_wep, name = "tools")
+ guid.register(obj_wep_ammo, name = "ammo")
+ guid.register(obj_inv_ammo, name = "ammo")
+ guid.register(obj_locker, name = "lockers")
+ guid.register(obj_locker_ammo, name = "ammo")
assert(obj.HasGUID)
assert(obj_wep.HasGUID)
@@ -34,11 +36,11 @@ class GUIDTaskUnregisterPlayerTest extends ActorTest {
assert(obj_inv_ammo.HasGUID)
assert(obj_locker.HasGUID)
assert(obj_locker_ammo.HasGUID)
- taskResolver ! TaskResolver.GiveTask(
+ TaskWorkflow.execute(TaskBundle(
new GUIDTaskTest.RegisterTestTask(probe.ref),
- List(GUIDTask.UnregisterPlayer(obj)(uns))
- )
- probe.expectMsg(scala.util.Success)
+ GUIDTask.unregisterPlayer(uns, obj)
+ ))
+ probe.expectMsg(5.second, scala.util.Success(true))
assert(!obj.HasGUID)
assert(!obj_wep.HasGUID)
assert(!obj_wep_ammo.HasGUID)
diff --git a/src/test/scala/objects/guidtask/GUIDTaskUnregisterToolTest.scala b/src/test/scala/objects/guidtask/GUIDTaskUnregisterToolTest.scala
index 7f9fc5f4..9a30dd1f 100644
--- a/src/test/scala/objects/guidtask/GUIDTaskUnregisterToolTest.scala
+++ b/src/test/scala/objects/guidtask/GUIDTaskUnregisterToolTest.scala
@@ -3,23 +3,25 @@ package objects.guidtask
import base.ActorTest
import net.psforever.objects._
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
+
+import scala.concurrent.duration._
class GUIDTaskUnregisterToolTest extends ActorTest {
"UnregisterEquipment -> UnregisterTool" in {
- val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = Tool(GlobalDefinitions.beamer)
+ val (guid, uns, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = Tool(GlobalDefinitions.beamer)
obj.AmmoSlots.head.Box = AmmoBox(GlobalDefinitions.energy_cell)
- guid.register(obj, "dynamic")
- guid.register(obj.AmmoSlots.head.Box, "dynamic")
+ guid.register(obj, name = "tools")
+ guid.register(obj.AmmoSlots.head.Box, name ="ammo")
assert(obj.HasGUID)
assert(obj.AmmoSlots.head.Box.HasGUID)
- taskResolver ! TaskResolver.GiveTask(
+ TaskWorkflow.execute(TaskBundle(
new GUIDTaskTest.RegisterTestTask(probe.ref),
- List(GUIDTask.UnregisterEquipment(obj)(uns))
- )
- probe.expectMsg(scala.util.Success)
+ GUIDTask.unregisterEquipment(uns, obj)
+ ))
+ probe.expectMsg(5.second, scala.util.Success(true))
assert(!obj.HasGUID)
assert(!obj.AmmoSlots.head.Box.HasGUID)
}
diff --git a/src/test/scala/objects/guidtask/GUIDTaskUnregisterTurretTest.scala b/src/test/scala/objects/guidtask/GUIDTaskUnregisterTurretTest.scala
index a8e3629a..00d40952 100644
--- a/src/test/scala/objects/guidtask/GUIDTaskUnregisterTurretTest.scala
+++ b/src/test/scala/objects/guidtask/GUIDTaskUnregisterTurretTest.scala
@@ -3,29 +3,31 @@ package objects.guidtask
import base.ActorTest
import net.psforever.objects._
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
+
+import scala.concurrent.duration._
class GUIDTaskUnregisterTurretTest extends ActorTest {
"UnregisterDeployableTurret" in {
- val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = new TurretDeployable(GlobalDefinitions.portable_manned_turret_vs)
- val obj_wep = obj.Weapons(1).Equipment.get
- val obj_ammo = obj_wep.asInstanceOf[Tool].AmmoSlot.Box
- val obj_res = obj.Inventory.Items.map(_.obj)
- guid.register(obj, "dynamic")
- guid.register(obj_wep, "dynamic")
- guid.register(obj_ammo, "dynamic")
- obj_res.foreach(box => guid.register(box, "dynamic"))
+ val (guid, uns, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = new TurretDeployable(GlobalDefinitions.portable_manned_turret_vs)
+ val obj_wep = obj.Weapons(1).Equipment.get
+ val obj_ammo = obj_wep.asInstanceOf[Tool].AmmoSlot.Box
+ val obj_res = obj.Inventory.Items.map(_.obj)
+ guid.register(obj, name = "deployables")
+ guid.register(obj_wep, name = "tools")
+ guid.register(obj_ammo, name = "ammo")
+ obj_res.foreach(box => guid.register(box, name = "ammo"))
assert(obj.HasGUID)
assert(obj_wep.HasGUID)
assert(obj_ammo.HasGUID)
obj_res.foreach(box => box.HasGUID)
- taskResolver ! TaskResolver.GiveTask(
+ TaskWorkflow.execute(TaskBundle(
new GUIDTaskTest.RegisterTestTask(probe.ref),
- List(GUIDTask.UnregisterDeployableTurret(obj)(uns))
- )
- probe.expectMsg(scala.util.Success)
+ GUIDTask.unregisterDeployableTurret(uns, obj)
+ ))
+ probe.expectMsg(5.second, scala.util.Success(true))
assert(!obj.HasGUID)
assert(!obj_wep.HasGUID)
assert(!obj_ammo.HasGUID)
diff --git a/src/test/scala/objects/guidtask/GUIDTaskUnregisterVehicleTest.scala b/src/test/scala/objects/guidtask/GUIDTaskUnregisterVehicleTest.scala
index 11317a94..850cea31 100644
--- a/src/test/scala/objects/guidtask/GUIDTaskUnregisterVehicleTest.scala
+++ b/src/test/scala/objects/guidtask/GUIDTaskUnregisterVehicleTest.scala
@@ -3,31 +3,33 @@ package objects.guidtask
import base.ActorTest
import net.psforever.objects._
-import net.psforever.objects.guid.{GUIDTask, TaskResolver}
+import net.psforever.objects.guid.{GUIDTask, TaskBundle, TaskWorkflow}
+
+import scala.concurrent.duration._
class GUIDTaskUnregisterVehicleTest extends ActorTest {
"RegisterVehicle" in {
- val (guid, uns, taskResolver, probe) = GUIDTaskTest.CommonTestSetup
- val obj = Vehicle(GlobalDefinitions.fury)
- val obj_wep = obj.WeaponControlledFromSeat(0).get
+ val (guid, uns, probe) = GUIDTaskTest.CommonTestSetup
+ val obj = Vehicle(GlobalDefinitions.fury)
+ val obj_wep = obj.WeaponControlledFromSeat(0).get
val obj_wep_ammo = (obj.WeaponControlledFromSeat(0).get.asInstanceOf[Tool].AmmoSlots.head.Box =
AmmoBox(GlobalDefinitions.hellfire_ammo)).get
obj.Trunk += 30 -> AmmoBox(GlobalDefinitions.hellfire_ammo)
val obj_trunk_ammo = obj.Trunk.Items(0).obj
- guid.register(obj, "dynamic")
- guid.register(obj_wep, "dynamic")
- guid.register(obj_wep_ammo, "dynamic")
- guid.register(obj_trunk_ammo, "dynamic")
+ guid.register(obj, name = "vehicles")
+ guid.register(obj_wep, name = "tools")
+ guid.register(obj_wep_ammo, name = "ammo")
+ guid.register(obj_trunk_ammo, name = "ammo")
assert(obj.HasGUID)
assert(obj_wep.HasGUID)
assert(obj_wep_ammo.HasGUID)
assert(obj_trunk_ammo.HasGUID)
- taskResolver ! TaskResolver.GiveTask(
+ TaskWorkflow.execute(TaskBundle(
new GUIDTaskTest.RegisterTestTask(probe.ref),
- List(GUIDTask.UnregisterVehicle(obj)(uns))
- )
- probe.expectMsg(scala.util.Success)
+ GUIDTask.unregisterVehicle(uns, obj)
+ ))
+ probe.expectMsg(5.second, scala.util.Success(true))
assert(!obj.HasGUID)
assert(!obj_wep.HasGUID)
assert(!obj_wep_ammo.HasGUID)
diff --git a/src/test/scala/objects/number/NumberPoolActorTest.scala b/src/test/scala/objects/number/NumberPoolActorTest.scala
index a3357b36..51eb1627 100644
--- a/src/test/scala/objects/number/NumberPoolActorTest.scala
+++ b/src/test/scala/objects/number/NumberPoolActorTest.scala
@@ -3,9 +3,9 @@ package objects.number
import akka.actor.Props
import base.ActorTest
-import net.psforever.objects.guid.actor.NumberPoolActor
import net.psforever.objects.guid.pool.ExclusivePool
import net.psforever.objects.guid.selector.RandomSelector
+import net.psforever.objects.guid.uns.NumberPoolActor
import scala.concurrent.duration.Duration
@@ -29,7 +29,7 @@ class NumberPoolActorTest1 extends ActorTest {
pool.Selector = new RandomSelector
val poolActor = system.actorOf(Props(classOf[NumberPoolActor], pool), name = "poolActor2")
poolActor ! NumberPoolActor.GetSpecificNumber(37)
- expectMsg(NumberPoolActor.GiveNumber(37, None))
+ expectMsg(NumberPoolActor.GiveNumber(37))
}
}
}
@@ -41,7 +41,7 @@ class NumberPoolActorTest2 extends ActorTest {
pool.Selector = new RandomSelector
val poolActor = system.actorOf(Props(classOf[NumberPoolActor], pool), name = "poolActor3")
poolActor ! NumberPoolActor.GetAnyNumber()
- expectMsg(NumberPoolActor.GiveNumber(25, None))
+ expectMsg(NumberPoolActor.GiveNumber(25))
poolActor ! NumberPoolActor.GetAnyNumber()
val msg = receiveOne(Duration.create(500, "ms"))
diff --git a/src/test/scala/objects/number/NumberPoolHubTest.scala b/src/test/scala/objects/number/NumberPoolHubTest.scala
index 6266f214..c9ef6b0f 100644
--- a/src/test/scala/objects/number/NumberPoolHubTest.scala
+++ b/src/test/scala/objects/number/NumberPoolHubTest.scala
@@ -262,46 +262,6 @@ class NumberPoolHubTest extends Specification {
}
}
- "not affect the hidden restricted pool by adding a new pool" in {
- val src = new MaxNumberSource(51)
- src.restrictNumber(4)
- src.restrictNumber(8) //in fibonacci
- src.restrictNumber(10)
- src.restrictNumber(12)
- val hub = new NumberPoolHub(src)
- hub.AddPool("fibonacci", numberList) must throwA[IllegalArgumentException]
- }
-
- "not register an object to a number belonging to the restricted pool" in {
- val src = new MaxNumberSource(51)
- src.restrictNumber(4)
- val hub = new NumberPoolHub(src)
- val obj = new EntityTestClass()
- hub.register(obj, 4).isFailure mustEqual true
- }
-
- "not register an object to the restricted pool directly" in {
- val src = new MaxNumberSource(51)
-// src.restrictNumber(4)
- val hub = new NumberPoolHub(src)
- val obj = new EntityTestClass()
- hub.register(obj, "").isFailure mustEqual true //the empty string represents the restricted pool
- }
-
- "not register a number belonging to the restricted pool" in {
- val src = new MaxNumberSource(51)
- src.restrictNumber(4)
- val hub = new NumberPoolHub(src)
- hub.register(4).isFailure mustEqual true
- }
-
- "not unregister a number belonging to the restricted pool" in {
- val src = new MaxNumberSource(51)
- src.restrictNumber(4)
- val hub = new NumberPoolHub(src)
- hub.unregister(4).isFailure mustEqual true
- }
-
"identity an object that is registered to it" in {
val hub1 = new NumberPoolHub(new MaxNumberSource(10))
val hub2 = new NumberPoolHub(new MaxNumberSource(10))
@@ -316,28 +276,21 @@ class NumberPoolHubTest extends Specification {
hub2.isRegistered(obj1) mustEqual false
}
- "identity a number that is registered to it" in {
+ "identity an entity that is registered to it" in {
val src1 = new MaxNumberSource(5)
val hub1 = new NumberPoolHub(src1)
val src2 = new MaxNumberSource(10)
- src2.restrictNumber(0)
- src2.restrictNumber(1)
- src2.restrictNumber(2)
- src2.restrictNumber(3)
- src2.restrictNumber(4)
- src2.restrictNumber(5)
val hub2 = new NumberPoolHub(src2)
val obj1 = new EntityTestClass()
val obj2 = new EntityTestClass()
hub1.register(obj1)
hub2.register(obj2)
- val num1 = obj1.GUID.guid
- val num2 = obj2.GUID.guid
- hub1.isRegistered(num1) mustEqual true
- hub2.isRegistered(num2) mustEqual true
- hub1.isRegistered(num2) mustEqual false
- hub2.isRegistered(num1) mustEqual false
+ obj1.GUID mustEqual obj2.GUID
+ hub1.isRegistered(obj1) mustEqual true
+ hub2.isRegistered(obj2) mustEqual true
+ hub1.isRegistered(obj2) mustEqual false
+ hub2.isRegistered(obj1) mustEqual false
}
}
}
diff --git a/src/test/scala/objects/number/NumberPoolTest.scala b/src/test/scala/objects/number/NumberPoolTest.scala
index 628b8838..a41753be 100644
--- a/src/test/scala/objects/number/NumberPoolTest.scala
+++ b/src/test/scala/objects/number/NumberPoolTest.scala
@@ -135,13 +135,13 @@ class NumberPoolTest extends Specification {
"GenericPool" should {
"construct" in {
- new GenericPool(mutable.LongMap[String](), 11)
+ GenericPool(mutable.LongMap[String](), max = 11, poolName = "generic")
ok
}
"get a provided number" in {
val map = mutable.LongMap[String]()
- val obj = new GenericPool(map, 11)
+ val obj = GenericPool(map, max = 11, poolName = "generic")
obj.Numbers.isEmpty mustEqual true
obj.Selector.asInstanceOf[SpecificSelector].SelectionIndex = 5
obj.Get() match {
@@ -157,7 +157,7 @@ class NumberPoolTest extends Specification {
"return a number" in {
val map = mutable.LongMap[String]()
- val obj = new GenericPool(map, 11)
+ val obj = GenericPool(map, max = 11, poolName = "generic")
obj.Selector.asInstanceOf[SpecificSelector].SelectionIndex = 5
obj.Get()
map.get(5).contains("generic") mustEqual true
@@ -170,7 +170,7 @@ class NumberPoolTest extends Specification {
"block on numbers that are already defined" in {
val map = mutable.LongMap[String]()
map += 5L -> "test" //5 is defined
- val obj = new GenericPool(map, 11)
+ val obj = GenericPool(map, max = 11, poolName = "generic")
obj.Numbers.isEmpty mustEqual true
obj.Selector.asInstanceOf[SpecificSelector].SelectionIndex = 5 //5 is requested
obj.Get() match {
@@ -183,10 +183,10 @@ class NumberPoolTest extends Specification {
"get a free number on own if none provided" in {
val map = mutable.LongMap[String]()
- val obj = new GenericPool(map, 11)
+ val obj = GenericPool(map, max = 11, poolName = "generic")
obj.Get() match {
case Success(number) =>
- number mustEqual 5
+ number mustEqual 1
case _ =>
ko
}
@@ -195,10 +195,10 @@ class NumberPoolTest extends Specification {
"get a free number that is not already defined" in {
val map = mutable.LongMap[String]()
map += 5L -> "test" //5 is defined; think, -1 :: 5 :: 11
- val obj = new GenericPool(map, 11)
+ val obj = GenericPool(map, max = 11, poolName = "generic")
obj.Get() match {
case Success(number) =>
- number mustEqual 2 // think, -1 :: 2 :: 5 :: 11
+ number mustEqual 1 // think, -1 :: 2 :: 5 :: 11
case _ => ko
}
@@ -208,10 +208,10 @@ class NumberPoolTest extends Specification {
val map = mutable.LongMap[String]()
map += 5L -> "test" //5 is defined; think, -1 :: 5 :: 11
map += 4L -> "test" //4 is defined; think, -1 :: 4 :: 5 :: 11
- val obj = new GenericPool(map, 11)
+ val obj = GenericPool(map, max = 11, poolName = "generic")
obj.Get() match {
case Success(number) =>
- number mustEqual 8 // think, -1 :: 4 :: 5 :: 8 :: 11
+ number mustEqual 1 // think, -1 :: 4 :: 5 :: 8 :: 11
case _ =>
ko
}
diff --git a/src/test/scala/objects/number/NumberSourceTest.scala b/src/test/scala/objects/number/NumberSourceTest.scala
index e69f0442..dae1140d 100644
--- a/src/test/scala/objects/number/NumberSourceTest.scala
+++ b/src/test/scala/objects/number/NumberSourceTest.scala
@@ -1,8 +1,7 @@
// Copyright (c) 2017 PSForever
package objects.number
-import net.psforever.objects.guid.AvailabilityPolicy
-import net.psforever.objects.guid.key.{LoanedKey, SecureKey}
+import net.psforever.objects.guid.key.{AvailabilityPolicy, LoanedKey, SecureKey}
import net.psforever.types.PlanetSideGUID
import org.specs2.mutable.Specification
@@ -72,46 +71,6 @@ class NumberSourceTest extends Specification {
obj.countUsed mustEqual 0
}
- "restrict a number (unassigned)" in {
- val obj = MaxNumberSource(25)
- val result: Option[LoanedKey] = obj.restrictNumber(5)
- result.isDefined mustEqual true
- result.get.GUID mustEqual 5
- result.get.Policy mustEqual AvailabilityPolicy.Restricted
- result.get.Object.isEmpty mustEqual true
- }
-
- "restrict a number (assigned + multiple assignments)" in {
- val obj = MaxNumberSource(25)
- val test1 = new TestClass()
- val test2 = new TestClass()
- val result: Option[LoanedKey] = obj.restrictNumber(5)
- result.get.GUID mustEqual 5
- result.get.Policy mustEqual AvailabilityPolicy.Restricted
- result.get.Object.isEmpty mustEqual true
- result.get.Object = None //assignment 1
- result.get.Object.isEmpty mustEqual true //still unassigned
- result.get.Object = test1 //assignment 2
- result.get.Object.contains(test1) mustEqual true
- result.get.Object = test2 //assignment 3
- result.get.Object.contains(test1) mustEqual true //same as above
- }
-
- "return a restricted number (correctly fail)" in {
- val obj = MaxNumberSource(25)
- val test = new TestClass()
- val result: Option[LoanedKey] = obj.restrictNumber(5)
- result.get.GUID mustEqual 5
- result.get.Policy mustEqual AvailabilityPolicy.Restricted
- result.get.Object = test
-
- obj.returnNumber(5)
- val result2: Option[SecureKey] = obj.get(5)
- result2.get.GUID mustEqual 5
- result2.get.Policy mustEqual AvailabilityPolicy.Restricted
- result2.get.Object.contains(test) mustEqual true
- }
-
"return a secure key" in {
val obj = MaxNumberSource(25)
val test = new TestClass()
@@ -124,19 +83,6 @@ class NumberSourceTest extends Specification {
obj.returnNumber(result2.get).contains(test) mustEqual true
}
- "restrict a previously-assigned number" in {
- val obj = MaxNumberSource(25)
- val test = new TestClass()
- val result1: Option[LoanedKey] = obj.getAvailable(5)
- result1.isDefined mustEqual true
- result1.get.Policy mustEqual AvailabilityPolicy.Leased
- result1.get.Object = test
- val result2: Option[LoanedKey] = obj.restrictNumber(5)
- result2.isDefined mustEqual true
- result2.get.Policy mustEqual AvailabilityPolicy.Restricted
- result2.get.Object.contains(test) mustEqual true
- }
-
"check a number (not previously gotten)" in {
val obj = MaxNumberSource(25)
val result2: Option[SecureKey] = obj.get(5)
@@ -194,9 +140,9 @@ class NumberSourceTest extends Specification {
obj.getAvailable(5) //no assignment
obj.getAvailable(10).get.Object = test1
obj.getAvailable(15).get.Object = test2
- obj.restrictNumber(15)
- obj.restrictNumber(20).get.Object = test3
+ obj.getAvailable(20).get.Object = test3
obj.countUsed mustEqual 4
+ obj.countDangling mustEqual 1
val list: List[IdentifiableEntity] = obj.clear()
obj.countUsed mustEqual 0
@@ -273,46 +219,6 @@ class NumberSourceTest extends Specification {
obj.countUsed mustEqual 0
}
- "restrict a number (unassigned)" in {
- val obj = SpecificNumberSource(List(25))
- val result: Option[LoanedKey] = obj.restrictNumber(number = 25)
- result.isDefined mustEqual true
- result.get.GUID mustEqual 25
- result.get.Policy mustEqual AvailabilityPolicy.Restricted
- result.get.Object.isEmpty mustEqual true
- }
-
- "restrict a number (assigned + multiple assignments)" in {
- val obj = SpecificNumberSource(List(25, 26))
- val test1 = new TestClass()
- val test2 = new TestClass()
- val result: Option[LoanedKey] = obj.restrictNumber(number = 25)
- result.get.GUID mustEqual 25
- result.get.Policy mustEqual AvailabilityPolicy.Restricted
- result.get.Object.isEmpty mustEqual true
- result.get.Object = None //assignment 1
- result.get.Object.isEmpty mustEqual true //still unassigned
- result.get.Object = test1 //assignment 2
- result.get.Object.contains(test1) mustEqual true
- result.get.Object = test2 //assignment 3
- result.get.Object.contains(test1) mustEqual true //same as above
- }
-
- "return a restricted number (correctly fail)" in {
- val obj = SpecificNumberSource(List(25))
- val test = new TestClass()
- val result: Option[LoanedKey] = obj.restrictNumber(number = 25)
- result.get.GUID mustEqual 25
- result.get.Policy mustEqual AvailabilityPolicy.Restricted
- result.get.Object = test
-
- obj.returnNumber(number = 25)
- val result2: Option[SecureKey] = obj.get(25)
- result2.get.GUID mustEqual 25
- result2.get.Policy mustEqual AvailabilityPolicy.Restricted
- result2.get.Object.contains(test) mustEqual true
- }
-
"return a secure key" in {
val obj = SpecificNumberSource(List(25))
val test = new TestClass()
@@ -325,19 +231,6 @@ class NumberSourceTest extends Specification {
obj.returnNumber(result2.get).contains(test) mustEqual true
}
- "restrict a previously-assigned number" in {
- val obj = SpecificNumberSource(List(25))
- val test = new TestClass()
- val result1: Option[LoanedKey] = obj.getAvailable(number = 25)
- result1.isDefined mustEqual true
- result1.get.Policy mustEqual AvailabilityPolicy.Leased
- result1.get.Object = test
- val result2: Option[LoanedKey] = obj.restrictNumber(number = 25)
- result2.isDefined mustEqual true
- result2.get.Policy mustEqual AvailabilityPolicy.Restricted
- result2.get.Object.contains(test) mustEqual true
- }
-
"check a number (not previously gotten)" in {
val obj = SpecificNumberSource(List(25))
val result2: Option[SecureKey] = obj.get(25)
@@ -395,9 +288,9 @@ class NumberSourceTest extends Specification {
obj.getAvailable(25) //no assignment
obj.getAvailable(26).get.Object = test1
obj.getAvailable(28).get.Object = test2
- obj.restrictNumber(28)
- obj.restrictNumber(30).get.Object = test3
+ obj.getAvailable(30).get.Object = test3
obj.countUsed mustEqual 4
+ obj.countDangling mustEqual 1
val list: List[IdentifiableEntity] = obj.clear()
obj.countUsed mustEqual 0
diff --git a/src/test/scala/objects/number/RegisterTest.scala b/src/test/scala/objects/number/RegisterTest.scala
deleted file mode 100644
index fbce828e..00000000
--- a/src/test/scala/objects/number/RegisterTest.scala
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2017 PSForever
-package objects.number
-
-import akka.actor.ActorRef
-import net.psforever.objects.guid.actor.Register
-import org.specs2.mutable.Specification
-
-class RegisterTest extends Specification {
- val obj = new net.psforever.objects.entity.IdentifiableEntity() {}
-
- "Register" should {
- "construct (object)" in {
- val reg = Register(obj)
- reg.obj mustEqual obj
- reg.number.isEmpty mustEqual true
- reg.name.isEmpty mustEqual true
- reg.callback.isEmpty mustEqual true
- }
-
- "construct (object, callback)" in {
- val reg = Register(obj, ActorRef.noSender)
- reg.obj mustEqual obj
- reg.number.isEmpty mustEqual true
- reg.name.isEmpty mustEqual true
- reg.callback.contains(ActorRef.noSender) mustEqual true
- }
-
- "construct (object, suggested number)" in {
- val reg = Register(obj, 5)
- reg.obj mustEqual obj
- reg.number.contains(5) mustEqual true
- reg.name.isEmpty mustEqual true
- reg.callback.isEmpty mustEqual true
- }
-
- "construct (object, suggested number, callback)" in {
- val reg = Register(obj, 5, ActorRef.noSender)
- reg.obj mustEqual obj
- reg.number.contains(5) mustEqual true
- reg.name.isEmpty mustEqual true
- reg.callback.contains(ActorRef.noSender) mustEqual true
- }
-
- "construct (object, pool name)" in {
- val reg = Register(obj, "pool")
- reg.obj mustEqual obj
- reg.number.isEmpty mustEqual true
- reg.name.contains("pool") mustEqual true
- reg.callback.isEmpty mustEqual true
- }
-
- "construct (object, pool name, callback)" in {
- val reg = Register(obj, "pool", ActorRef.noSender)
- reg.obj mustEqual obj
- reg.number.isEmpty mustEqual true
- reg.name.contains("pool") mustEqual true
- reg.callback.contains(ActorRef.noSender) mustEqual true
- }
- }
-}
diff --git a/src/test/scala/objects/number/UniqueNumberOpsTest.scala b/src/test/scala/objects/number/UniqueNumberOpsTest.scala
new file mode 100644
index 00000000..f52eee57
--- /dev/null
+++ b/src/test/scala/objects/number/UniqueNumberOpsTest.scala
@@ -0,0 +1,53 @@
+// Copyright (c) 2017 PSForever
+package objects.number
+
+import akka.actor.{Actor, ActorRef, ActorSystem, Props}
+import net.psforever.objects.entity.IdentifiableEntity
+import net.psforever.objects.guid.{NumberPoolHub, UniqueNumberOps, UniqueNumberSetup}
+import net.psforever.objects.guid.source.MaxNumberSource
+import org.scalatest.flatspec.AsyncFlatSpec
+import akka.pattern.ask
+import akka.util.Timeout
+
+import scala.concurrent.Promise
+import scala.concurrent.duration._
+import scala.util.Success
+
+class UniqueNumberOpsTest extends AsyncFlatSpec {
+ behavior of "UniqueNumberOps"
+
+ it should "UniqueNumberOpsTest" in {
+ val promise: Promise[Any] = Promise()
+ val sys = ActorSystem()
+ val source = new MaxNumberSource(max = 21)
+ val hub = new NumberPoolHub(source)
+ hub.AddPool(name = "default", List(0,1,2,3,5,8,13,21))
+ val entity = new UniqueNumberOpsTest.EntityTestClass()
+ assert(!entity.HasGUID)
+ assert(source.countUsed == 0)
+
+ ask(sys.actorOf(Props[UniqueNumberOpsTest.NumberPoolBuilder](), "test"), hub)(Timeout(2.seconds)).onComplete {
+ case Success(pools: Map[_,_]) =>
+ val unops = new UniqueNumberOps(hub, pools.asInstanceOf[Map[String, ActorRef]])
+ promise.completeWith { unops.Register(entity, poolName = "default") }
+ case _ =>
+ promise.failure(new Exception(""))
+ }
+ promise.future map { _ =>
+ assert(entity.HasGUID)
+ assert(source.countUsed == 1)
+ }
+ }
+}
+
+object UniqueNumberOpsTest {
+ class EntityTestClass extends IdentifiableEntity
+
+ class NumberPoolBuilder extends Actor {
+ def receive: Receive = {
+ case hub: NumberPoolHub =>
+ sender() ! UniqueNumberSetup.AllocateNumberPoolActors(context, hub)
+ case _ => ;
+ }
+ }
+}
diff --git a/src/test/scala/objects/number/UniqueNumberSystemTest.scala b/src/test/scala/objects/number/UniqueNumberSystemTest.scala
deleted file mode 100644
index e26bc5a7..00000000
--- a/src/test/scala/objects/number/UniqueNumberSystemTest.scala
+++ /dev/null
@@ -1,386 +0,0 @@
-// Copyright (c) 2017 PSForever
-package objects.number
-
-import akka.actor.{ActorRef, ActorSystem, Props}
-import base.ActorTest
-import net.psforever.objects.entity.IdentifiableEntity
-import net.psforever.objects.guid.NumberPoolHub
-import net.psforever.objects.guid.actor.{NumberPoolActor, Register, UniqueNumberSystem, Unregister}
-import net.psforever.objects.guid.selector.RandomSelector
-import net.psforever.objects.guid.source.MaxNumberSource
-import net.psforever.types.PlanetSideGUID
-
-import scala.concurrent.duration._
-import scala.util.{Failure, Success}
-
-class AllocateNumberPoolActors extends ActorTest {
- "AllocateNumberPoolActors" in {
- val src: MaxNumberSource = MaxNumberSource(6000)
- val guid: NumberPoolHub = new NumberPoolHub(src)
- guid.AddPool("pool1", (1001 to 2000).toList)
- guid.AddPool("pool2", (3001 to 4000).toList)
- guid.AddPool("pool3", (5001 to 6000).toList)
- val actorMap = UniqueNumberSystemTest.AllocateNumberPoolActors(guid)
- assert(actorMap.size == 4)
- assert(actorMap.get("generic").isDefined) //automatically generated
- assert(actorMap.get("pool1").isDefined)
- assert(actorMap.get("pool2").isDefined)
- assert(actorMap.get("pool3").isDefined)
- }
-}
-
-class UniqueNumberSystemTest extends ActorTest() {
- "UniqueNumberSystem" should {
- "constructor" in {
- val src: MaxNumberSource = MaxNumberSource(6000)
- val guid: NumberPoolHub = new NumberPoolHub(src)
- guid.AddPool("pool1", (1001 to 2000).toList)
- guid.AddPool("pool2", (3001 to 4000).toList)
- guid.AddPool("pool3", (5001 to 6000).toList)
- system.actorOf(
- Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)),
- "uns"
- )
- //as long as it constructs ...
- }
- }
-}
-
-class UniqueNumberSystemTest1 extends ActorTest() {
- class EntityTestClass extends IdentifiableEntity
-
- "UniqueNumberSystem" should {
- "Register (success)" in {
- val src: MaxNumberSource = MaxNumberSource(6000)
- val guid: NumberPoolHub = new NumberPoolHub(src)
- val pool1 = (1001 to 2000).toList
- val pool2 = (3001 to 4000).toList
- val pool3 = (5001 to 6000).toList
- guid.AddPool("pool1", pool1).Selector = new RandomSelector
- guid.AddPool("pool2", pool2).Selector = new RandomSelector
- guid.AddPool("pool3", pool3).Selector = new RandomSelector
- val uns = system.actorOf(
- Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)),
- "uns"
- )
- assert(src.countUsed == 0)
- //pool1
- for (_ <- 1 to 100) {
- val testObj = new EntityTestClass()
- uns ! Register(testObj, "pool1")
- val msg = receiveOne(Duration.create(500, "ms"))
- assert(msg.isInstanceOf[Success[_]])
- assert(pool1.contains(testObj.GUID.guid))
- }
- //pool2
- for (_ <- 1 to 100) {
- val testObj = new EntityTestClass()
- uns ! Register(testObj, "pool2")
- val msg = receiveOne(Duration.create(500, "ms"))
- assert(msg.isInstanceOf[Success[_]])
- assert(pool2.contains(testObj.GUID.guid))
- }
- //pool3
- for (_ <- 1 to 100) {
- val testObj = new EntityTestClass()
- uns ! Register(testObj, "pool3")
- val msg = receiveOne(Duration.create(500, "ms"))
- assert(msg.isInstanceOf[Success[_]])
- assert(pool3.contains(testObj.GUID.guid))
- }
- assert(src.countUsed == 300)
- }
- }
-}
-
-class UniqueNumberSystemTest2 extends ActorTest() {
- class EntityTestClass extends IdentifiableEntity
-
- "UniqueNumberSystem" should {
- "Register (success; already registered)" in {
- val src: MaxNumberSource = MaxNumberSource(6000)
- val guid: NumberPoolHub = new NumberPoolHub(src)
- guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
- guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
- guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
- val uns = system.actorOf(
- Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)),
- "uns"
- )
- val testObj = new EntityTestClass()
- assert(!testObj.HasGUID)
- assert(src.countUsed == 0)
-
- uns ! Register(testObj, "pool1")
- val msg1 = receiveOne(Duration.create(500, "ms"))
- assert(msg1.isInstanceOf[Success[_]])
- assert(testObj.HasGUID)
- assert(src.countUsed == 1)
-
- val id = testObj.GUID.guid
- uns ! Register(testObj, "pool2") //different pool; makes no difference
- val msg2 = receiveOne(Duration.create(500, "ms"))
- assert(msg2.isInstanceOf[Success[_]])
- assert(testObj.HasGUID)
- assert(src.countUsed == 1)
- assert(testObj.GUID.guid == id) //unchanged
- }
- }
- //a log.warn should have been generated during this test
-}
-
-class UniqueNumberSystemTest3 extends ActorTest() {
- class EntityTestClass extends IdentifiableEntity
-
- "UniqueNumberSystem" should {
- "Register (failure; no pool)" in {
- val src: MaxNumberSource = MaxNumberSource(6000)
- val guid: NumberPoolHub = new NumberPoolHub(src)
- guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
- guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
- guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
- val uns = system.actorOf(
- Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)),
- "uns"
- )
- val testObj = new EntityTestClass()
- assert(!testObj.HasGUID)
- assert(src.countUsed == 0)
-
- uns ! Register(testObj, "pool4")
- val msg1 = receiveOne(Duration.create(500, "ms"))
- assert(msg1.isInstanceOf[Failure[_]])
- assert(!testObj.HasGUID)
- assert(src.countUsed == 0)
- }
- }
-}
-
-class UniqueNumberSystemTest4 extends ActorTest() {
- class EntityTestClass extends IdentifiableEntity
-
- "UniqueNumberSystem" should {
- "Register (failure; empty pool)" in {
- val src: MaxNumberSource = MaxNumberSource(6000)
- val guid: NumberPoolHub = new NumberPoolHub(src)
- guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
- guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
- guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
- guid.AddPool("pool4", 50 :: Nil).Selector = new RandomSelector //list of one element; can not add an empty list
- val uns = system.actorOf(
- Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)),
- "uns"
- )
-
- val testObj1 = new EntityTestClass()
- uns ! Register(testObj1, "pool4")
- val msg1 = receiveOne(Duration.create(500, "ms"))
- assert(msg1.isInstanceOf[Success[_]]) //pool4 is now empty
-
- val testObj2 = new EntityTestClass()
- uns ! Register(testObj2, "pool4")
- val msg2 = receiveOne(Duration.create(500, "ms"))
- assert(msg2.isInstanceOf[Failure[_]])
- }
- }
-}
-
-class UniqueNumberSystemTest5 extends ActorTest() {
- class EntityTestClass extends IdentifiableEntity
-
- "UniqueNumberSystem" should {
- "Unregister (success)" in {
- val src: MaxNumberSource = MaxNumberSource(6000)
- val guid: NumberPoolHub = new NumberPoolHub(src)
- val pool2 = (3001 to 4000).toList
- guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
- guid.AddPool("pool2", pool2).Selector = new RandomSelector
- guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
- val uns = system.actorOf(
- Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)),
- "uns"
- )
- val testObj = new EntityTestClass()
- assert(!testObj.HasGUID)
- assert(src.countUsed == 0)
-
- uns ! Register(testObj, "pool2")
- val msg1 = receiveOne(Duration.create(2000, "ms"))
- assert(msg1.isInstanceOf[Success[_]])
- assert(testObj.HasGUID)
- assert(pool2.contains(testObj.GUID.guid))
- assert(src.countUsed == 1)
-
- uns ! Unregister(testObj)
- val msg2 = receiveOne(Duration.create(2000, "ms"))
- assert(msg2.isInstanceOf[Success[_]])
- assert(!testObj.HasGUID)
- assert(src.countUsed == 0)
- }
- }
-}
-
-class UniqueNumberSystemTest6 extends ActorTest() {
- class EntityTestClass extends IdentifiableEntity
-
- "UniqueNumberSystem" should {
- "Unregister (success; object not registered at all)" in {
- val src: MaxNumberSource = MaxNumberSource(6000)
- val guid: NumberPoolHub = new NumberPoolHub(src)
- guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
- guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
- guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
- val uns = system.actorOf(
- Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)),
- "uns"
- )
- val testObj = new EntityTestClass()
- assert(!testObj.HasGUID)
- assert(src.countUsed == 0)
-
- uns ! Unregister(testObj)
- val msg1 = receiveOne(Duration.create(500, "ms"))
- assert(msg1.isInstanceOf[Success[_]])
- assert(!testObj.HasGUID)
- assert(src.countUsed == 0)
- }
- }
-}
-
-class UniqueNumberSystemTest7 extends ActorTest() {
- class EntityTestClass extends IdentifiableEntity
-
- "UniqueNumberSystem" should {
- "Unregister (failure; number not in system)" in {
- val src: MaxNumberSource = MaxNumberSource(6000)
- val guid: NumberPoolHub = new NumberPoolHub(src)
- guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
- guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
- guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
- val uns = system.actorOf(
- Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)),
- "uns"
- )
- val testObj = new EntityTestClass()
- testObj.GUID = PlanetSideGUID(6001) //fake registering; number too high
- assert(testObj.HasGUID)
- assert(src.countUsed == 0)
-
- uns ! Unregister(testObj)
- val msg1 = receiveOne(Duration.create(500, "ms"))
- assert(msg1.isInstanceOf[Failure[_]])
- assert(testObj.HasGUID)
- assert(src.countUsed == 0)
- }
- }
-}
-
-class UniqueNumberSystemTest8 extends ActorTest() {
- class EntityTestClass extends IdentifiableEntity
-
- "UniqueNumberSystem" should {
- "Unregister (failure; object is not registered to that number)" in {
- val src: MaxNumberSource = MaxNumberSource(6000)
- val guid: NumberPoolHub = new NumberPoolHub(src)
- guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
- guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
- guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
- val uns = system.actorOf(
- Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)),
- "uns"
- )
- val testObj = new EntityTestClass()
- testObj.GUID = PlanetSideGUID(3500) //fake registering
- assert(testObj.HasGUID)
- assert(src.countUsed == 0)
-
- uns ! Unregister(testObj)
- val msg1 = receiveOne(Duration.create(500, "ms"))
- assert(msg1.isInstanceOf[Failure[_]])
- assert(testObj.HasGUID)
- assert(src.countUsed == 0)
- }
- }
-}
-
-class UniqueNumberSystemTest9 extends ActorTest() {
- class EntityTestClass extends IdentifiableEntity
-
- "UniqueNumberSystem" should {
- "Failures (manually walking the failure cases)" in {
- val src: MaxNumberSource = MaxNumberSource(6000)
- val guid: NumberPoolHub = new NumberPoolHub(src)
- guid.AddPool("pool1", (1001 to 2000).toList).Selector = new RandomSelector
- guid.AddPool("pool2", (3001 to 4000).toList).Selector = new RandomSelector
- guid.AddPool("pool3", (5001 to 6000).toList).Selector = new RandomSelector
- val uns = system.actorOf(
- Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)),
- "uns"
- )
- val excp = new Exception("EXCEPTION MESSAGE")
- expectNoMessage(Duration.create(200, "ms"))
-
- //GiveNumber
- uns ! NumberPoolActor.GiveNumber(1001, Some("test")) //no task associated with id="test"
- uns ! NumberPoolActor.GiveNumber(1000, Some("test")) //no task associated with id="test" and number is not pooled
- uns ! NumberPoolActor.GiveNumber(1000, Some(1)) //the task could theoretically exist, but does not
- //NoNumber
- uns ! NumberPoolActor.NoNumber(excp, Some(1))
- uns ! NumberPoolActor.NoNumber(excp, None)
- uns ! NumberPoolActor.NoNumber(excp, Some("test"))
- //ReturnNumberResult A
- uns ! NumberPoolActor.ReturnNumberResult(1001, None, Some("test"))
- uns ! NumberPoolActor.ReturnNumberResult(1000, None, Some("test"))
- uns ! NumberPoolActor.ReturnNumberResult(1001, None, Some(1))
- uns ! NumberPoolActor.ReturnNumberResult(1000, None, Some(1))
- //ReturnNumberResult B
- uns ! NumberPoolActor.ReturnNumberResult(1001, Some(excp), Some("test"))
- uns ! NumberPoolActor.ReturnNumberResult(1001, Some(excp), Some(1))
- }
- }
-}
-
-class UniqueNumberSystemTestA extends ActorTest {
- class EntityTestClass extends IdentifiableEntity
-
- "UniqueNumberSystem" should {
- "remain consistent between registrations" in {
- val src: MaxNumberSource = MaxNumberSource(10)
- val guid: NumberPoolHub = new NumberPoolHub(src)
- guid.AddPool("pool1", (0 until 10).toList).Selector = new RandomSelector
- val uns = system.actorOf(
- Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystemTest.AllocateNumberPoolActors(guid)),
- "uns"
- )
- expectNoMessage(Duration.create(200, "ms"))
-
- assert(src.countUsed == 0)
- (0 to 4).foreach(i => { assert(guid.register(new EntityTestClass(), i).isSuccess) })
- assert(src.countUsed == 5)
-
- (0 to 5).foreach(_ => { uns ! Register(new EntityTestClass(), "pool1") })
- assert(receiveOne(200 milliseconds).isInstanceOf[Success[_]]) //6th
- assert(receiveOne(200 milliseconds).isInstanceOf[Success[_]]) //7th
- assert(receiveOne(200 milliseconds).isInstanceOf[Success[_]]) //8th
- assert(receiveOne(200 milliseconds).isInstanceOf[Success[_]]) //9th
- assert(receiveOne(200 milliseconds).isInstanceOf[Success[_]]) //10th
- assert(receiveOne(200 milliseconds).isInstanceOf[Failure[_]]) //no more
- assert(src.countUsed == 10)
- }
- }
-}
-
-object UniqueNumberSystemTest {
-
- /**
- * @see `UniqueNumberSystem.AllocateNumberPoolActors(NumberPoolHub)(implicit ActorContext)`
- */
- def AllocateNumberPoolActors(poolSource: NumberPoolHub)(implicit system: ActorSystem): Map[String, ActorRef] = {
- poolSource.Pools
- .map({
- case (pname, pool) =>
- pname -> system.actorOf(Props(classOf[NumberPoolActor], pool), pname)
- })
- .toMap
- }
-}
diff --git a/src/test/scala/service/RemoverActorTest.scala b/src/test/scala/service/RemoverActorTest.scala
deleted file mode 100644
index 643be63e..00000000
--- a/src/test/scala/service/RemoverActorTest.scala
+++ /dev/null
@@ -1,555 +0,0 @@
-// Copyright (c) 2017 PSForever
-package service
-
-/* Temporary imports */
-import akka.actor.ActorRef
-import net.psforever.objects.definition.EquipmentDefinition
-import net.psforever.objects.equipment.Equipment
-import net.psforever.types.PlanetSideGUID
-import net.psforever.services.RemoverActor
-
-//import akka.actor.{ActorRef, Props}
-//import akka.routing.RandomPool
-//import akka.testkit.TestProbe
-//import base.ActorTest
-//import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Tool}
-//import net.psforever.objects.definition.{EquipmentDefinition, ObjectDefinition}
-//import net.psforever.objects.equipment.Equipment
-//import net.psforever.objects.guid.TaskResolver
-//import net.psforever.objects.zones.{Zone, ZoneMap}
-//import net.psforever.types.PlanetSideGUID
-//import net.psforever.services.{RemoverActor, ServiceManager}
-
-import scala.concurrent.duration._
-import scala.util.Success
-
-//class StandardRemoverActorTest extends ActorTest {
-// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
-//
-// "RemoverActor" should {
-// "handle a simple task" in {
-// expectNoMessage(500 milliseconds)
-// val remover = system.actorOf(
-// Props(classOf[ActorTest.SupportActorInterface], Props[RemoverActorTest.TestRemover], self),
-// "test-remover"
-// )
-// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere)
-//
-// val reply1 = receiveOne(500 milliseconds)
-// assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
-// val reply2 = receiveOne(500 milliseconds)
-// assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
-// expectNoMessage(1 seconds) //delay
-// val reply3 = receiveOne(500 milliseconds)
-// assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
-// val reply4 = receiveOne(500 milliseconds)
-// assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
-// val reply5 = receiveOne(500 milliseconds)
-// assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
-// val reply6 = receiveOne(500 milliseconds)
-// assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
-// val reply7 = receiveOne(500 milliseconds)
-// assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
-// }
-// }
-//}
-
-//class DelayedRemoverActorTest extends ActorTest {
-// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
-//
-// "RemoverActor" should {
-// "handle a simple task (timed)" in {
-// expectNoMessage(500 milliseconds)
-// val remover = system.actorOf(
-// Props(classOf[ActorTest.SupportActorInterface], Props[RemoverActorTest.TestRemover], self),
-// "test-remover"
-// )
-// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(100 milliseconds))
-//
-// val reply1 = receiveOne(500 milliseconds)
-// assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
-// val reply2 = receiveOne(500 milliseconds)
-// assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
-// //no delay
-// val reply3 = receiveOne(500 milliseconds)
-// assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
-// val reply4 = receiveOne(500 milliseconds)
-// assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
-// val reply5 = receiveOne(500 milliseconds)
-// assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
-// val reply6 = receiveOne(500 milliseconds)
-// assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
-// val reply7 = receiveOne(500 milliseconds)
-// assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
-// }
-// }
-//}
-
-//class ExcludedRemoverActorTest extends ActorTest {
-// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
-// val AlternateTestObject = new PlanetSideGameObject() { def Definition = new ObjectDefinition(0) { } }
-//
-// "RemoverActor" should {
-// "allow only specific objects" in {
-// expectNoMessage(500 milliseconds)
-// val probe = TestProbe()
-// val remover = system.actorOf(
-// Props(classOf[ActorTest.SupportActorInterface], Props[RemoverActorTest.TestRemover], self),
-// "test-remover"
-// )
-// remover ! RemoverActor.AddTask(AlternateTestObject, Zone.Nowhere)
-//
-// val reply1 = probe.receiveOne(200 milliseconds)
-// assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
-// expectNoMessage(2 seconds)
-// //RemoverActor is stalled because it received an object that it was not allowed to act upon
-// }
-// }
-//}
-
-//class MultipleRemoverActorTest extends ActorTest {
-// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
-// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
-//
-// "RemoverActor" should {
-// "work on parallel tasks" in {
-// expectNoMessage(500 milliseconds)
-// val probe = TestProbe()
-// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
-// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere)
-// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere)
-//
-// val replies = probe.receiveN(14, 5 seconds)
-// var ita : Int = 0
-// var ija : Int = 0
-// var fja : Int = 0
-// var cta : Int = 0
-// var sja : Int = 0
-// var dta : Int = 0
-// var dtr : Int = 0
-// replies.collect {
-// case RemoverActorTest.InclusionTestAlert() => ita += 1
-// case RemoverActorTest.InitialJobAlert() => ija += 1
-// case RemoverActorTest.FirstJobAlert() => fja += 1
-// case RemoverActorTest.ClearanceTestAlert() => cta += 1
-// case RemoverActorTest.SecondJobAlert() => sja += 1
-// case RemoverActorTest.DeletionTaskAlert() => dta += 1
-// case RemoverActorTest.DeletionTaskRunAlert() => dtr += 1
-// case msg => assert(false, s"$msg")
-// }
-// assert(ita == 2 && ija == 2 && fja == 2 && cta == 2 && sja == 2 && dta == 2 && dtr == 2)
-// }
-// }
-//}
-//
-//class HurrySpecificRemoverActorTest extends ActorTest {
-// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
-//
-// "RemoverActor" should {
-// "be able to hurry certain tasks" in {
-// expectNoMessage(500 milliseconds)
-// val probe = TestProbe()
-// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
-// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(10 minutes)) //TEN MINUTE WAIT
-//
-// val reply1 = probe.receiveOne(200 milliseconds)
-// assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
-// val reply2 = probe.receiveOne(200 milliseconds)
-// assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
-// probe.expectNoMessage(3 seconds) //long delay, longer than standard but not yet 10 minutes
-// remover ! RemoverActor.HurrySpecific(List(RemoverActorTest.TestObject), Zone.Nowhere) //hurried
-// val reply3 = probe.receiveOne(300 milliseconds)
-// assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
-// val reply4 = probe.receiveOne(300 milliseconds)
-// assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
-// val reply5 = probe.receiveOne(300 milliseconds)
-// assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
-// val reply6 = probe.receiveOne(500 milliseconds)
-// assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
-// val reply7 = probe.receiveOne(500 milliseconds)
-// assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
-// }
-// }
-//}
-//
-//class HurrySelectionRemoverActorTest extends ActorTest {
-// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
-// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
-//
-// "RemoverActor" should {
-// "be able to hurry certain tasks, but let others finish normally" in {
-// expectNoMessage(500 milliseconds)
-// val probe = TestProbe()
-// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
-// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
-// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(10 seconds))
-//
-// val replies = probe.receiveN(4, 5 seconds)
-// var ita : Int = 0
-// var ija : Int = 0
-// replies.collect {
-// case RemoverActorTest.InclusionTestAlert() => ita += 1
-// case RemoverActorTest.InitialJobAlert() => ija += 1
-// case msg => assert(false, s"$msg")
-// }
-// assert(ita == 2 && ija == 2)
-// probe.expectNoMessage(3 seconds) //long delay, longer than standard but not yet 5 seconds
-// remover ! RemoverActor.HurrySpecific(List(RemoverActorTest.TestObject), Zone.Nowhere) //hurried
-// //first
-// val reply3a = probe.receiveOne(300 milliseconds)
-// assert(reply3a.isInstanceOf[RemoverActorTest.FirstJobAlert])
-// val reply4a = probe.receiveOne(300 milliseconds)
-// assert(reply4a.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
-// val reply5a = probe.receiveOne(300 milliseconds)
-// assert(reply5a.isInstanceOf[RemoverActorTest.SecondJobAlert])
-// val reply6a = probe.receiveOne(500 milliseconds)
-// assert(reply6a.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
-// val reply7a = probe.receiveOne(500 milliseconds)
-// assert(reply7a.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
-// //second
-// remover ! RemoverActor.HurrySpecific(List(TestObject2), Zone.Nowhere) //hurried
-// val reply3b = probe.receiveOne(300 milliseconds)
-// assert(reply3b.isInstanceOf[RemoverActorTest.FirstJobAlert])
-// val reply4b = probe.receiveOne(300 milliseconds)
-// assert(reply4b.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
-// val reply5b = probe.receiveOne(300 milliseconds)
-// assert(reply5b.isInstanceOf[RemoverActorTest.SecondJobAlert])
-// val reply6b = probe.receiveOne(500 milliseconds)
-// assert(reply6b.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
-// val reply7b = probe.receiveOne(500 milliseconds)
-// assert(reply7b.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
-// }
-// }
-//}
-//
-//class HurryMultipleRemoverActorTest extends ActorTest {
-// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
-// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
-// final val TestObject3 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(3) } }
-//
-// "RemoverActor" should {
-// "be able to hurry certain tasks, but only valid ones" in {
-// expectNoMessage(500 milliseconds)
-// val probe = TestProbe()
-// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
-// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
-// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(5 seconds))
-//
-// val replies = probe.receiveN(4, 5 seconds)
-// var ita : Int = 0
-// var ija : Int = 0
-// replies.collect {
-// case RemoverActorTest.InclusionTestAlert() => ita += 1
-// case RemoverActorTest.InitialJobAlert() => ija += 1
-// case msg => assert(false, s"$msg")
-// }
-// assert(ita == 2 && ija == 2)
-// probe.expectNoMessage(3 seconds) //long delay, longer than standard but not yet 5 seconds
-// remover ! RemoverActor.HurrySpecific(List(RemoverActorTest.TestObject, TestObject3), Zone.Nowhere) //multiple hurried, only one valid
-// //first
-// val reply3a = probe.receiveOne(300 milliseconds)
-// assert(reply3a.isInstanceOf[RemoverActorTest.FirstJobAlert])
-// val reply4a = probe.receiveOne(300 milliseconds)
-// assert(reply4a.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
-// val reply5a = probe.receiveOne(300 milliseconds)
-// assert(reply5a.isInstanceOf[RemoverActorTest.SecondJobAlert])
-// val reply6a = probe.receiveOne(500 milliseconds)
-// assert(reply6a.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
-// val reply7a = probe.receiveOne(500 milliseconds)
-// assert(reply7a.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
-// //second
-// remover ! RemoverActor.HurrySpecific(List(TestObject2), Zone.Nowhere) //hurried
-// val reply3b = probe.receiveOne(300 milliseconds)
-// assert(reply3b.isInstanceOf[RemoverActorTest.FirstJobAlert])
-// val reply4b = probe.receiveOne(300 milliseconds)
-// assert(reply4b.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
-// val reply5b = probe.receiveOne(300 milliseconds)
-// assert(reply5b.isInstanceOf[RemoverActorTest.SecondJobAlert])
-// val reply6b = probe.receiveOne(500 milliseconds)
-// assert(reply6b.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
-// val reply7b = probe.receiveOne(500 milliseconds)
-// assert(reply7b.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
-// }
-// }
-//}
-//
-//class HurryByZoneRemoverActorTest extends ActorTest {
-// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
-// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
-// final val TestObject3 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(3) } }
-// final val zone = new Zone("test", new ZoneMap("test-map"), 11)
-//
-// "RemoverActor" should {
-// "be able to hurry certain tasks by their zone" in {
-// expectNoMessage(500 milliseconds)
-// val probe = TestProbe()
-// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
-// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
-// remover ! RemoverActor.AddTask(TestObject2, zone, Some(5 seconds))
-// remover ! RemoverActor.AddTask(TestObject3, Zone.Nowhere, Some(5 seconds))
-//
-// val replies1 = probe.receiveN(6, 5 seconds)
-// var ita : Int = 0
-// var ija : Int = 0
-// replies1.collect {
-// case RemoverActorTest.InclusionTestAlert() => ita += 1
-// case RemoverActorTest.InitialJobAlert() => ija += 1
-// case msg => assert(false, s"$msg")
-// }
-// assert(ita == 3 && ija == 3)
-// probe.expectNoMessage(3 seconds) //long delay, longer than standard but not yet 5 seconds
-// remover ! RemoverActor.HurrySpecific(List(), Zone.Nowhere) //multiple hurried, only the two entries with Zone.Nowhere
-// //
-// val replies2 = probe.receiveN(10, 5 seconds)
-// var fja : Int = 0
-// var cta : Int = 0
-// var sja : Int = 0
-// var dta : Int = 0
-// var dtr : Int = 0
-// replies2.collect {
-// case RemoverActorTest.InclusionTestAlert() => ita += 1
-// case RemoverActorTest.InitialJobAlert() => ija += 1
-// case RemoverActorTest.FirstJobAlert() => fja += 1
-// case RemoverActorTest.ClearanceTestAlert() => cta += 1
-// case RemoverActorTest.SecondJobAlert() => sja += 1
-// case RemoverActorTest.DeletionTaskAlert() => dta += 1
-// case RemoverActorTest.DeletionTaskRunAlert() => dtr += 1
-// case msg => assert(false, s"$msg")
-// }
-// assert(fja == 2 && cta == 2 && sja == 2 && dta == 2 && dtr == 2)
-// //final
-// remover ! RemoverActor.HurrySpecific(List(), zone) //hurried
-// val reply3b = probe.receiveOne(300 milliseconds)
-// assert(reply3b.isInstanceOf[RemoverActorTest.FirstJobAlert])
-// val reply4b = probe.receiveOne(300 milliseconds)
-// assert(reply4b.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
-// val reply5b = probe.receiveOne(300 milliseconds)
-// assert(reply5b.isInstanceOf[RemoverActorTest.SecondJobAlert])
-// val reply6b = probe.receiveOne(500 milliseconds)
-// assert(reply6b.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
-// val reply7b = probe.receiveOne(500 milliseconds)
-// assert(reply7b.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
-// }
-// }
-//}
-//
-//class HurryAllRemoverActorTest extends ActorTest {
-// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
-// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
-// final val TestObject3 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(3) } }
-//
-// "RemoverActor" should {
-// "be able to hurry all tasks to completion" in {
-// expectNoMessage(500 milliseconds)
-// val probe = TestProbe()
-// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
-// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(20 seconds))
-// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(15 seconds))
-// remover ! RemoverActor.AddTask(TestObject3, Zone.Nowhere, Some(10 seconds))
-//
-// val replies1 = probe.receiveN(6, 5 seconds)
-// var ita : Int = 0
-// var ija : Int = 0
-// replies1.collect {
-// case RemoverActorTest.InclusionTestAlert() => ita += 1
-// case RemoverActorTest.InitialJobAlert() => ija += 1
-// case msg => assert(false, s"$msg")
-// }
-// assert(ita == 3 && ija == 3)
-// probe.expectNoMessage(3 seconds) //long delay, longer than standard but not yet longer than any of the tasks
-// remover ! RemoverActor.HurryAll() //all hurried
-// //
-// val replies2 = probe.receiveN(15, 5 seconds)
-// var fja : Int = 0
-// var cta : Int = 0
-// var sja : Int = 0
-// var dta : Int = 0
-// var dtr : Int = 0
-// replies2.collect {
-// case RemoverActorTest.InclusionTestAlert() => ita += 1
-// case RemoverActorTest.InitialJobAlert() => ija += 1
-// case RemoverActorTest.FirstJobAlert() => fja += 1
-// case RemoverActorTest.ClearanceTestAlert() => cta += 1
-// case RemoverActorTest.SecondJobAlert() => sja += 1
-// case RemoverActorTest.DeletionTaskAlert() => dta += 1
-// case RemoverActorTest.DeletionTaskRunAlert() => dtr += 1
-// case msg => assert(false, s"$msg")
-// }
-// assert(fja == 3 && cta == 3 && sja == 3 && dta == 3 && dtr == 3)
-// }
-// }
-//}
-//
-//class ClearSelectionRemoverActorTest extends ActorTest {
-// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
-// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
-//
-// "RemoverActor" should {
-// "be able to clear certain tasks" in {
-// expectNoMessage(500 milliseconds)
-// val probe = TestProbe()
-// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
-// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
-// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(5 seconds))
-//
-// val replies = probe.receiveN(4, 5 seconds)
-// var ita : Int = 0
-// var ija : Int = 0
-// replies.collect {
-// case RemoverActorTest.InclusionTestAlert() => ita += 1
-// case RemoverActorTest.InitialJobAlert() => ija += 1
-// case msg => assert(false, s"$msg")
-// }
-// assert(ita == 2 && ija == 2)
-// probe.expectNoMessage(4 seconds) //long delay, longer than standard but not yet 5 seconds
-// remover ! RemoverActor.ClearSpecific(List(RemoverActorTest.TestObject), Zone.Nowhere) //cleared
-// //
-// val reply3 = probe.receiveOne(2 seconds)
-// assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
-// val reply4 = probe.receiveOne(300 milliseconds)
-// assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
-// val reply5 = probe.receiveOne(300 milliseconds)
-// assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
-// val reply6 = probe.receiveOne(500 milliseconds)
-// assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
-// val reply7 = probe.receiveOne(500 milliseconds)
-// assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
-// //wait
-// probe.expectNoMessage(2 seconds) //nothing more to do
-// }
-// }
-//}
-//
-//class ClearAllRemoverActorTest extends ActorTest {
-// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
-// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
-//
-// "RemoverActor" should {
-// "be able to clear all tasks, with no more work on them" in {
-// expectNoMessage(500 milliseconds)
-// val probe = TestProbe()
-// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
-// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
-// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(5 seconds))
-//
-// val replies = probe.receiveN(4, 5 seconds)
-// var ita : Int = 0
-// var ija : Int = 0
-// replies.collect {
-// case RemoverActorTest.InclusionTestAlert() => ita += 1
-// case RemoverActorTest.InitialJobAlert() => ija += 1
-// case msg => assert(false, s"$msg")
-// }
-// assert(ita == 2 && ija == 2)
-// probe.expectNoMessage(4 seconds) //long delay, longer than standard but not yet 5 seconds
-// remover ! RemoverActor.ClearAll() //cleared
-// //wait
-// probe.expectNoMessage(3 seconds) //nothing more to do
-// }
-// }
-//}
-//
-//class EarlyDeathRemoverActorTest extends ActorTest {
-// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
-// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
-//
-// "RemoverActor" should {
-// "be able to hurry certain tasks" in {
-// expectNoMessage(500 milliseconds)
-// val probe = TestProbe()
-// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
-// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
-// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(5 seconds))
-//
-// val replies = probe.receiveN(4, 5 seconds)
-// var ita : Int = 0
-// var ija : Int = 0
-// replies.collect {
-// case RemoverActorTest.InclusionTestAlert() => ita += 1
-// case RemoverActorTest.InitialJobAlert() => ija += 1
-// case msg => assert(false, s"$msg")
-// }
-// assert(ita == 2 && ija == 2)
-// probe.expectNoMessage(2 seconds)
-// remover ! akka.actor.PoisonPill
-// //
-// val replies2 = probe.receiveN(8, 5 seconds)
-// var fja : Int = 0
-// var cta : Int = 0
-// var sja : Int = 0
-// var dta : Int = 0
-// var dtr : Int = 0
-// replies2.collect {
-// case RemoverActorTest.FirstJobAlert() => fja += 1
-// case RemoverActorTest.ClearanceTestAlert() => cta += 1
-// case RemoverActorTest.SecondJobAlert() => sja += 1
-// case RemoverActorTest.DeletionTaskAlert() => dta += 1
-// case RemoverActorTest.DeletionTaskRunAlert() => dtr += 1
-// case msg => assert(false, s"$msg")
-// }
-// assert(fja == 2 && cta == 0 && sja == 2 && dta == 2 && dtr == 2) //no clearance tests
-// }
-// }
-//}
-
-object RemoverActorTest {
- final val TestObject = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(1) } }
-
- final case class InclusionTestAlert()
-
- final case class InitialJobAlert()
-
- final case class FirstJobAlert()
-
- final case class SecondJobAlert()
-
- final case class ClearanceTestAlert()
-
- final case class DeletionTaskAlert()
-
- final case class DeletionTaskRunAlert()
-
- class TestRemover(taskResolver: ActorRef) extends RemoverActor(taskResolver) {
- import net.psforever.objects.guid.{Task, TaskResolver}
- val FirstStandardDuration = 1 seconds
-
- val SecondStandardDuration = 100 milliseconds
-
- def InclusionTest(entry: RemoverActor.Entry): Boolean = {
- context.parent ! InclusionTestAlert()
- true
- }
-
- def InitialJob(entry: RemoverActor.Entry): Unit = {
- context.parent ! InitialJobAlert()
- }
-
- def FirstJob(entry: RemoverActor.Entry): Unit = {
- context.parent ! FirstJobAlert()
- }
-
- override def SecondJob(entry: RemoverActor.Entry): Unit = {
- context.parent ! SecondJobAlert()
- super.SecondJob(entry)
- }
-
- def ClearanceTest(entry: RemoverActor.Entry): Boolean = {
- context.parent ! ClearanceTestAlert()
- true
- }
-
- def DeletionTask(entry: RemoverActor.Entry): TaskResolver.GiveTask = {
- context.parent ! DeletionTaskAlert()
- TaskResolver.GiveTask(new Task() {
- private val localProbe = context.parent
-
- override def isComplete = Task.Resolution.Success
-
- def Execute(resolver: ActorRef): Unit = {
- context.parent ! DeletionTaskRunAlert()
- resolver ! Success(this)
- }
- })
- }
- }
-}