mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-07-13 07:24:57 +00:00
mostly, comments for the event system files; the event system stamp now determines how the out channel forms; some case class entities have become case object entities
This commit is contained in:
parent
de84e31547
commit
40d5721914
29 changed files with 331 additions and 116 deletions
|
|
@ -46,7 +46,7 @@ class AvatarService3Test extends ActorTest {
|
||||||
"subscribe to a specific channel" in {
|
"subscribe to a specific channel" in {
|
||||||
val service = system.actorOf(Props(classOf[AvatarService]), AvatarServiceTest.TestName)
|
val service = system.actorOf(Props(classOf[AvatarService]), AvatarServiceTest.TestName)
|
||||||
service ! Service.Join("test")
|
service ! Service.Join("test")
|
||||||
service ! Service.Leave()
|
service ! Service.LeaveAll
|
||||||
assert(true)
|
assert(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -58,7 +58,7 @@ class AvatarService4Test extends ActorTest {
|
||||||
ServiceManager.boot(system)
|
ServiceManager.boot(system)
|
||||||
val service = system.actorOf(Props(classOf[AvatarService]), AvatarServiceTest.TestName)
|
val service = system.actorOf(Props(classOf[AvatarService]), AvatarServiceTest.TestName)
|
||||||
service ! Service.Join("test")
|
service ! Service.Join("test")
|
||||||
service ! Service.LeaveAll()
|
service ! Service.LeaveAll
|
||||||
assert(true)
|
assert(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,7 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
||||||
val channel = player.Name
|
val channel = player.Name
|
||||||
val events = continent.AvatarEvents
|
val events = continent.AvatarEvents
|
||||||
seeSpectatorsIn = None
|
seeSpectatorsIn = None
|
||||||
events ! Service.Leave(Some("spectator"))
|
events ! Service.Leave("spectator")
|
||||||
continent
|
continent
|
||||||
.AllPlayers
|
.AllPlayers
|
||||||
.filter(_.spectator)
|
.filter(_.spectator)
|
||||||
|
|
|
||||||
|
|
@ -250,9 +250,9 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act
|
||||||
//todo we might be able to piggyback this for squad recalls later
|
//todo we might be able to piggyback this for squad recalls later
|
||||||
if(session.zone eq zone) {
|
if(session.zone eq zone) {
|
||||||
sessionLogic.zoning.zoneReload = true
|
sessionLogic.zoning.zoneReload = true
|
||||||
zone.AvatarEvents ! Service.Leave()
|
zone.AvatarEvents ! Service.LeaveAll
|
||||||
zone.LocalEvents ! Service.Leave()
|
zone.LocalEvents ! Service.LeaveAll
|
||||||
zone.VehicleEvents ! Service.Leave()
|
zone.VehicleEvents ! Service.LeaveAll
|
||||||
zone.AvatarEvents ! Service.Join(player.Name) //must manually restore this subscriptions
|
zone.AvatarEvents ! Service.Join(player.Name) //must manually restore this subscriptions
|
||||||
sessionLogic.zoning.spawn.handleNewPlayerLoaded(player) //will restart subscriptions and dispatch a LoadMapMessage
|
sessionLogic.zoning.spawn.handleNewPlayerLoaded(player) //will restart subscriptions and dispatch a LoadMapMessage
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -232,7 +232,7 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context:
|
||||||
case VehicleAction.TransferPassengerChannel(oldChannel, tempChannel, vehicle, vehicleToDelete) if isNotSameTarget =>
|
case VehicleAction.TransferPassengerChannel(oldChannel, tempChannel, vehicle, vehicleToDelete) if isNotSameTarget =>
|
||||||
sessionLogic.zoning.interstellarFerry = Some(vehicle)
|
sessionLogic.zoning.interstellarFerry = Some(vehicle)
|
||||||
sessionLogic.zoning.interstellarFerryTopLevelGUID = Some(vehicleToDelete)
|
sessionLogic.zoning.interstellarFerryTopLevelGUID = Some(vehicleToDelete)
|
||||||
continent.VehicleEvents ! Service.Leave(Some(oldChannel)) //old vehicle-specific channel (was s"${vehicle.Actor}")
|
continent.VehicleEvents ! Service.Leave(oldChannel) //old vehicle-specific channel (was s"${vehicle.Actor}")
|
||||||
galaxyService ! Service.Join(tempChannel) //temporary vehicle-specific channel
|
galaxyService ! Service.Join(tempChannel) //temporary vehicle-specific channel
|
||||||
log.debug(s"TransferPassengerChannel: ${player.Name} now subscribed to $tempChannel for vehicle gating")
|
log.debug(s"TransferPassengerChannel: ${player.Name} now subscribed to $tempChannel for vehicle gating")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -743,7 +743,7 @@ class GeneralOperations(
|
||||||
* @param channel the channel name
|
* @param channel the channel name
|
||||||
*/
|
*/
|
||||||
private def unaccessContainerChannel(events: ActorRef, channel: String): Unit = {
|
private def unaccessContainerChannel(events: ActorRef, channel: String): Unit = {
|
||||||
events ! Service.Leave(Some(channel))
|
events ! Service.Leave(channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -616,12 +616,12 @@ class SessionData(
|
||||||
squadResponseOpt.foreach(_.stop())
|
squadResponseOpt.foreach(_.stop())
|
||||||
zoningOpt.foreach(_.stop())
|
zoningOpt.foreach(_.stop())
|
||||||
chatOpt.foreach(_.stop())
|
chatOpt.foreach(_.stop())
|
||||||
continent.AvatarEvents ! Service.Leave()
|
continent.AvatarEvents ! Service.LeaveAll
|
||||||
continent.LocalEvents ! Service.Leave()
|
continent.LocalEvents ! Service.LeaveAll
|
||||||
continent.VehicleEvents ! Service.Leave()
|
continent.VehicleEvents ! Service.LeaveAll
|
||||||
galaxyService ! Service.Leave()
|
galaxyService ! Service.LeaveAll
|
||||||
if (avatar != null && squadService != Default.Actor) {
|
if (avatar != null && squadService != Default.Actor) {
|
||||||
squadService ! Service.Leave()
|
squadService ! Service.LeaveAll
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -620,9 +620,9 @@ class ZoningOperations(
|
||||||
//the only zone-level event system subscription necessary before BeginZoningMessage (for persistence purposes)
|
//the only zone-level event system subscription necessary before BeginZoningMessage (for persistence purposes)
|
||||||
zone.AvatarEvents ! Service.Join(player.Name)
|
zone.AvatarEvents ! Service.Join(player.Name)
|
||||||
sessionLogic.persist()
|
sessionLogic.persist()
|
||||||
oldZone.AvatarEvents ! Service.Leave()
|
oldZone.AvatarEvents ! Service.LeaveAll
|
||||||
oldZone.LocalEvents ! Service.Leave()
|
oldZone.LocalEvents ! Service.LeaveAll
|
||||||
oldZone.VehicleEvents ! Service.Leave()
|
oldZone.VehicleEvents ! Service.LeaveAll
|
||||||
|
|
||||||
if (player.isAlive && zoningType != Zoning.Method.Reset) {
|
if (player.isAlive && zoningType != Zoning.Method.Reset) {
|
||||||
if (player.HasGUID) {
|
if (player.HasGUID) {
|
||||||
|
|
@ -651,9 +651,9 @@ class ZoningOperations(
|
||||||
val oldZone = session.zone
|
val oldZone = session.zone
|
||||||
session = session.copy(zone = foundZone)
|
session = session.copy(zone = foundZone)
|
||||||
sessionLogic.persist()
|
sessionLogic.persist()
|
||||||
oldZone.AvatarEvents ! Service.Leave()
|
oldZone.AvatarEvents ! Service.LeaveAll
|
||||||
oldZone.LocalEvents ! Service.Leave()
|
oldZone.LocalEvents ! Service.LeaveAll
|
||||||
oldZone.VehicleEvents ! Service.Leave()
|
oldZone.VehicleEvents ! Service.LeaveAll
|
||||||
//the only zone-level event system subscription necessary before BeginZoningMessage (for persistence purposes)
|
//the only zone-level event system subscription necessary before BeginZoningMessage (for persistence purposes)
|
||||||
foundZone.AvatarEvents ! Service.Join(player.Name)
|
foundZone.AvatarEvents ! Service.Join(player.Name)
|
||||||
foundZone.Population ! Zone.Population.Join(avatar)
|
foundZone.Population ! Zone.Population.Join(avatar)
|
||||||
|
|
@ -762,7 +762,7 @@ class ZoningOperations(
|
||||||
case _ =>
|
case _ =>
|
||||||
interstellarFerry match {
|
interstellarFerry match {
|
||||||
case None =>
|
case None =>
|
||||||
galaxyService ! Service.Leave(Some(temp_channel)) //no longer being transferred between zones
|
galaxyService ! Service.Leave(temp_channel) //no longer being transferred between zones
|
||||||
interstellarFerryTopLevelGUID = None
|
interstellarFerryTopLevelGUID = None
|
||||||
case Some(_) => () //wait patiently
|
case Some(_) => () //wait patiently
|
||||||
}
|
}
|
||||||
|
|
@ -770,7 +770,7 @@ class ZoningOperations(
|
||||||
}
|
}
|
||||||
|
|
||||||
private def handleTransferPassengerVehicle(vehicle: Vehicle, temporaryChannel: String): Unit = {
|
private def handleTransferPassengerVehicle(vehicle: Vehicle, temporaryChannel: String): Unit = {
|
||||||
galaxyService ! Service.Leave(Some(temporaryChannel)) //temporary vehicle-specific channel (see above)
|
galaxyService ! Service.Leave(temporaryChannel) //temporary vehicle-specific channel (see above)
|
||||||
spawn.deadState = DeadState.Release
|
spawn.deadState = DeadState.Release
|
||||||
sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, player.Faction, unk5=true))
|
sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, player.Faction, unk5=true))
|
||||||
interstellarFerry = Some(vehicle) //on the other continent and registered to that continent's GUID system
|
interstellarFerry = Some(vehicle) //on the other continent and registered to that continent's GUID system
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ class PainboxControl(painbox: Painbox) extends PoweredAmenityControl {
|
||||||
}
|
}
|
||||||
|
|
||||||
var commonBehavior: Receive = {
|
var commonBehavior: Receive = {
|
||||||
case Service.Startup() =>
|
case Service.Startup =>
|
||||||
if (!disabled && domain.midpoint == Vector3.Zero) {
|
if (!disabled && domain.midpoint == Vector3.Zero) {
|
||||||
initialStartup()
|
initialStartup()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ class ResourceSiloControl(resourceSilo: ResourceSilo)
|
||||||
private var drainMultiplier: Float = 1.0f
|
private var drainMultiplier: Float = 1.0f
|
||||||
|
|
||||||
def receive: Receive = {
|
def receive: Receive = {
|
||||||
case Service.Startup() =>
|
case Service.Startup =>
|
||||||
resourceSilo.Owner match {
|
resourceSilo.Owner match {
|
||||||
case building: Building =>
|
case building: Building =>
|
||||||
UpdateChargeLevel(amount = 0)
|
UpdateChargeLevel(amount = 0)
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ class OrbitalShuttlePadControl(pad: OrbitalShuttlePad) extends Actor {
|
||||||
* register and add the shuttle as a common vehicle of the said zone
|
* register and add the shuttle as a common vehicle of the said zone
|
||||||
*/
|
*/
|
||||||
val startUp: Receive = {
|
val startUp: Receive = {
|
||||||
case Service.Startup() =>
|
case Service.Startup =>
|
||||||
import net.psforever.types.Vector3
|
import net.psforever.types.Vector3
|
||||||
import net.psforever.types.Vector3.DistanceSquared
|
import net.psforever.types.Vector3.DistanceSquared
|
||||||
import net.psforever.objects.GlobalDefinitions._
|
import net.psforever.objects.GlobalDefinitions._
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ object ProximityTerminal {
|
||||||
if (obj.Actor == Default.Actor) {
|
if (obj.Actor == Default.Actor) {
|
||||||
obj.Actor =
|
obj.Actor =
|
||||||
context.actorOf(Props(classOf[ProximityTerminalControl], obj), PlanetSideServerObject.UniqueActorName(obj))
|
context.actorOf(Props(classOf[ProximityTerminalControl], obj), PlanetSideServerObject.UniqueActorName(obj))
|
||||||
obj.Actor ! Service.Startup()
|
obj.Actor ! Service.Startup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1718,14 +1718,14 @@ object Zone {
|
||||||
.flatMap(_.Amenities.filter(_.Definition == GlobalDefinitions.resource_silo))
|
.flatMap(_.Amenities.filter(_.Definition == GlobalDefinitions.resource_silo))
|
||||||
.collect {
|
.collect {
|
||||||
case silo: ResourceSilo =>
|
case silo: ResourceSilo =>
|
||||||
silo.Actor ! Service.Startup()
|
silo.Actor ! Service.Startup
|
||||||
}
|
}
|
||||||
//some painfields need to look for their closest door
|
//some painfields need to look for their closest door
|
||||||
buildings.values
|
buildings.values
|
||||||
.flatMap(_.Amenities.filter(_.Definition.isInstanceOf[PainboxDefinition]))
|
.flatMap(_.Amenities.filter(_.Definition.isInstanceOf[PainboxDefinition]))
|
||||||
.collect {
|
.collect {
|
||||||
case painbox: Painbox =>
|
case painbox: Painbox =>
|
||||||
painbox.Actor ! Service.Startup()
|
painbox.Actor ! Service.Startup
|
||||||
}
|
}
|
||||||
//the orbital_buildings in sanctuary zones have to establish their shuttle routes
|
//the orbital_buildings in sanctuary zones have to establish their shuttle routes
|
||||||
map.shuttleBays
|
map.shuttleBays
|
||||||
|
|
@ -1733,7 +1733,7 @@ object Zone {
|
||||||
guid(_)
|
guid(_)
|
||||||
}
|
}
|
||||||
.collect { case Some(obj: OrbitalShuttlePad) =>
|
.collect { case Some(obj: OrbitalShuttlePad) =>
|
||||||
obj.Actor ! Service.Startup()
|
obj.Actor ! Service.Startup
|
||||||
}
|
}
|
||||||
//allocate soi information
|
//allocate soi information
|
||||||
zone.soi ! SOI.Build()
|
zone.soi ! SOI.Build()
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import net.psforever.types.PlanetSideGUID
|
||||||
object Service {
|
object Service {
|
||||||
final val defaultPlayerGUID: PlanetSideGUID = PlanetSideGUID(0)
|
final val defaultPlayerGUID: PlanetSideGUID = PlanetSideGUID(0)
|
||||||
|
|
||||||
final case class Startup()
|
case object Startup
|
||||||
|
|
||||||
final case class Join(channel: String, sendJoinConfirmation: Boolean)
|
final case class Join(channel: String, sendJoinConfirmation: Boolean)
|
||||||
|
|
||||||
|
|
@ -17,7 +17,7 @@ object Service {
|
||||||
|
|
||||||
final case class JoinConfirmation(eventSystem: ActorRef, channel: String)
|
final case class JoinConfirmation(eventSystem: ActorRef, channel: String)
|
||||||
|
|
||||||
final case class Leave(channel: Option[String] = None)
|
final case class Leave(channel: String)
|
||||||
|
|
||||||
final case class LeaveAll()
|
case object LeaveAll
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -453,7 +453,7 @@ class PersistenceMonitor(
|
||||||
*/
|
*/
|
||||||
def AvatarLogout(avatar: Avatar): Unit = {
|
def AvatarLogout(avatar: Avatar): Unit = {
|
||||||
LivePlayerList.Remove(avatar.id)
|
LivePlayerList.Remove(avatar.id)
|
||||||
squadService.tell(Service.Leave(Some(avatar.id.toString)), context.parent)
|
squadService.tell(Service.Leave(avatar.id.toString), context.parent)
|
||||||
galaxyService.tell(GalaxyServiceMessage(GalaxyAction.LogStatusChange(avatar.name)), context.parent)
|
galaxyService.tell(GalaxyServiceMessage(GalaxyAction.LogStatusChange(avatar.name)), context.parent)
|
||||||
Deployables.Disown(inZone, avatar, context.parent)
|
Deployables.Disown(inZone, avatar, context.parent)
|
||||||
inZone.Population.tell(Zone.Population.Leave(avatar), context.parent)
|
inZone.Population.tell(Zone.Population.Leave(avatar), context.parent)
|
||||||
|
|
|
||||||
|
|
@ -4,42 +4,78 @@ package net.psforever.services.base
|
||||||
import akka.actor.Actor
|
import akka.actor.Actor
|
||||||
import net.psforever.services.Service
|
import net.psforever.services.Service
|
||||||
import net.psforever.services.base.bus.GenericEventBus
|
import net.psforever.services.base.bus.GenericEventBus
|
||||||
import net.psforever.services.base.envelope.{GenericMessageEnvelope, GenericResponseEnvelope}
|
import net.psforever.services.base.envelope.GenericMessageEnvelope
|
||||||
import org.log4s.Logger
|
import org.log4s.Logger
|
||||||
|
|
||||||
trait EventSystemStamp
|
/**
|
||||||
|
* A distinct tag associated with an event system.
|
||||||
|
* The stamp is intended to demonstrate that the input message has been interpreted into an output response
|
||||||
|
* through actual use of an event system
|
||||||
|
* and that the response has not been fabricated and is not fraudulent.
|
||||||
|
* While the word "stamp" more probably calls to mind the concept of a postage stamp,
|
||||||
|
* the purpose of this artifact is closer to that of the routing information
|
||||||
|
* stamped onto an envelope over the postage stamp area of a letter.
|
||||||
|
*/
|
||||||
|
trait EventSystemStamp {
|
||||||
|
/*
|
||||||
|
Example Classifiers: "foo", "foo.fizz", and "foo.buzz"
|
||||||
|
In general, Classifier channels will perform left-pattern matching.
|
||||||
|
"foo" will publish to "foo", "foo.fizz", and "foo.buzz"
|
||||||
|
To isolate "foo", one must distinguish it with a right-pattern.
|
||||||
|
"foo" is appended as "foo.bar" and no longer publishes to "foo.fizz.bar" or to "foo.buzz.bar"
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Take an input channel and produce the publishing output channel.
|
||||||
|
* @param channel publishing channel
|
||||||
|
* @return appended publishing channel
|
||||||
|
*/
|
||||||
|
def routing(channel: String): String = s"/$channel/out"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic opt-in event response relay system.
|
||||||
|
* Remembers "subscribers" (`ActorRef`) to "channels" (`String`);
|
||||||
|
* accepts "messages" (`GenericMessageEnvelope`) and interprets the message as a response (`GenericResponseEnvelope`);
|
||||||
|
* and, dispatches the response to all subscribers associated with the channel provided in the message.
|
||||||
|
* @param stamp distinct tag associated with an event system
|
||||||
|
*/
|
||||||
abstract class GenericEventService(stamp: EventSystemStamp)
|
abstract class GenericEventService(stamp: EventSystemStamp)
|
||||||
extends Actor {
|
extends Actor {
|
||||||
protected lazy val log: Logger = org.log4s.getLogger(getClass.getSimpleName)
|
protected lazy val log: Logger = org.log4s.getLogger(getClass.getSimpleName)
|
||||||
|
|
||||||
protected val eventBus: GenericEventBus = new GenericEventBus
|
protected val eventBus: GenericEventBus = new GenericEventBus
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add subscription handling.
|
||||||
|
*/
|
||||||
private def commonJoinBehavior: Receive = {
|
private def commonJoinBehavior: Receive = {
|
||||||
case Service.Join(channel, true) =>
|
case Service.Join(channel, true) =>
|
||||||
val path = formatChannel(channel)
|
val path = stamp.routing(channel)
|
||||||
val who = sender()
|
val who = sender()
|
||||||
eventBus.subscribe(who, path)
|
eventBus.subscribe(who, path)
|
||||||
who ! Service.JoinConfirmation(self, channel)
|
who ! Service.JoinConfirmation(self, channel)
|
||||||
|
|
||||||
case Service.Join(channel, _) =>
|
case Service.Join(channel, _) =>
|
||||||
val path = formatChannel(channel)
|
val path = stamp.routing(channel)
|
||||||
val who = sender()
|
val who = sender()
|
||||||
eventBus.subscribe(who, path)
|
eventBus.subscribe(who, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove subscription handling.
|
||||||
|
*/
|
||||||
private def commonLeaveBehavior: Receive = {
|
private def commonLeaveBehavior: Receive = {
|
||||||
case Service.Leave(None) =>
|
case Service.LeaveAll =>
|
||||||
eventBus.unsubscribe(sender())
|
eventBus.unsubscribe(sender())
|
||||||
|
|
||||||
case Service.Leave(Some(channel)) =>
|
case Service.Leave(channel) =>
|
||||||
val path = formatChannel(channel)
|
val path = stamp.routing(channel)
|
||||||
eventBus.unsubscribe(sender(), path)
|
eventBus.unsubscribe(sender(), path)
|
||||||
|
|
||||||
case Service.LeaveAll() =>
|
|
||||||
eventBus.unsubscribe(sender())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept and handle designated messages.
|
||||||
|
*/
|
||||||
protected def commonBehavior: Receive = {
|
protected def commonBehavior: Receive = {
|
||||||
case msg: GenericMessageEnvelope =>
|
case msg: GenericMessageEnvelope =>
|
||||||
handleMessage(msg)
|
handleMessage(msg)
|
||||||
|
|
@ -53,13 +89,12 @@ abstract class GenericEventService(stamp: EventSystemStamp)
|
||||||
log.warn(s"Unhandled message $msg from ${sender()}")
|
log.warn(s"Unhandled message $msg from ${sender()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def handleMessage(msg: GenericMessageEnvelope): Unit = {
|
/**
|
||||||
eventBus.publish(composeResponseEnvelope(msg))
|
* Handle designated messages.
|
||||||
|
* Interpret the input message as an output response and publish that response.
|
||||||
|
* @param event event system message
|
||||||
|
*/
|
||||||
|
protected def handleMessage(event: GenericMessageEnvelope): Unit = {
|
||||||
|
eventBus.publish(event.response(stamp))
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def composeResponseEnvelope(msg: GenericMessageEnvelope): GenericResponseEnvelope = {
|
|
||||||
msg.response(stamp, formatChannel)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected def formatChannel(channel: String): String = s"/$channel"
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,21 +18,36 @@ import scala.concurrent.duration.DurationInt
|
||||||
Adapted from the rating limiting code in PSForever fork https://github.com/Pinapse/giant with permission
|
Adapted from the rating limiting code in PSForever fork https://github.com/Pinapse/giant with permission
|
||||||
*/
|
*/
|
||||||
|
|
||||||
trait CachedGenericEventMessageEnvelope
|
/**
|
||||||
|
* A framework for flagged messages to be cached
|
||||||
|
* based on designation of a target globally unique identifier.
|
||||||
|
* Suggestion: this identifier be associated with the source.
|
||||||
|
*/
|
||||||
|
trait CachedGenericEventEnvelope
|
||||||
extends MessageTransformationBehavior {
|
extends MessageTransformationBehavior {
|
||||||
|
/** cache designator */
|
||||||
def guid: PlanetSideGUID
|
def guid: PlanetSideGUID
|
||||||
}
|
}
|
||||||
|
|
||||||
final case class CachedEnvelope(
|
final case class CachedEnvelope(
|
||||||
guid: PlanetSideGUID,
|
guid: PlanetSideGUID,
|
||||||
originalChannel: String,
|
originalChannel: String,
|
||||||
override val filter: PlanetSideGUID,
|
filter: PlanetSideGUID,
|
||||||
override val msg: EventMessage
|
msg: EventMessage
|
||||||
) extends CachedGenericEventMessageEnvelope {
|
) extends CachedGenericEventEnvelope {
|
||||||
assert(guid != Service.defaultPlayerGUID, "can not cache message under default GUID")
|
assert(guid != Service.defaultPlayerGUID, "can not cache message under default GUID")
|
||||||
}
|
}
|
||||||
|
|
||||||
object CachedEnvelope {
|
object CachedEnvelope {
|
||||||
|
/**
|
||||||
|
* If the filter is specified, but not the cache target, treat the filter like the cache target.
|
||||||
|
* Treat invalid values as a reason to downgrade from a cached message envelope to a traditional message envelope.
|
||||||
|
* Warn of this downgrade.e
|
||||||
|
* @param channel set of subscribers on an event system bus the envelope should reach
|
||||||
|
* @param filter specific subscriber endpoint to be excluded (the subscriber should filter themselves)
|
||||||
|
* @param msg input payload transported by this envelope
|
||||||
|
* @return either a `CacheEnvelope` or a `MessageEnvelope`, depending on cache-readiness of the `filter` value
|
||||||
|
*/
|
||||||
def apply(channel: String, filter: PlanetSideGUID, msg: EventMessage): GenericMessageEnvelope = {
|
def apply(channel: String, filter: PlanetSideGUID, msg: EventMessage): GenericMessageEnvelope = {
|
||||||
if (filter == Service.defaultPlayerGUID) {
|
if (filter == Service.defaultPlayerGUID) {
|
||||||
org.log4s.getLogger("CachedEnvelope").warn("(1) cached message envelope downgraded to normal message envelope")
|
org.log4s.getLogger("CachedEnvelope").warn("(1) cached message envelope downgraded to normal message envelope")
|
||||||
|
|
@ -42,20 +57,37 @@ object CachedEnvelope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the cache target is specified, but is not valid,
|
||||||
|
* downgrade from a cached message envelope to a traditional message envelope.
|
||||||
|
* Warn of this downgrade.
|
||||||
|
* If valid, the filter becomes its defaulted value in the corresponding message
|
||||||
|
* @param guid cache designator
|
||||||
|
* @param channel set of subscribers on an event system bus the envelope should reach
|
||||||
|
* @param msg input payload transported by this envelope
|
||||||
|
* @return either a `CacheEnvelope` or a `MessageEnvelope`, depending on cache-readiness of the target
|
||||||
|
*/
|
||||||
def apply(guid: PlanetSideGUID, channel: String, msg: EventMessage): GenericMessageEnvelope = {
|
def apply(guid: PlanetSideGUID, channel: String, msg: EventMessage): GenericMessageEnvelope = {
|
||||||
if (guid == Service.defaultPlayerGUID) {
|
if (guid == Service.defaultPlayerGUID) {
|
||||||
org.log4s.getLogger("CachedEnvelope").warn("(2) cached message envelope downgraded to normal message envelope")
|
org.log4s.getLogger("CachedEnvelope").warn("(2) cached message envelope downgraded to normal message envelope")
|
||||||
MessageEnvelope(channel, guid, msg)
|
MessageEnvelope(channel, guid, msg)
|
||||||
} else {
|
} else {
|
||||||
CachedEnvelope(guid, channel, guid, msg)
|
CachedEnvelope(guid, channel, Service.defaultPlayerGUID, msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An internal message for the purpose of forcing cached messages to be flushed.
|
||||||
|
* All of it's fields default to harmless values because it is not intended to be processed by an event system
|
||||||
|
* but it must maintain the trappings of a message envelope to be processed.
|
||||||
|
* @see `NoMessage`
|
||||||
|
* @see `NoResponseEnvelope`
|
||||||
|
*/
|
||||||
private case object FlushCachedMessages extends GenericMessageEnvelope {
|
private case object FlushCachedMessages extends GenericMessageEnvelope {
|
||||||
def originalChannel: String = ""
|
def originalChannel: String = ""
|
||||||
def msg: EventMessage = NoMessage
|
def msg: EventMessage = NoMessage
|
||||||
def response(stamp: EventSystemStamp, sendToChannel: String => String): GenericResponseEnvelope = NoResponseEnvelope
|
def response(stamp: EventSystemStamp): GenericResponseEnvelope = NoResponseEnvelope
|
||||||
def channel: String = ""
|
def channel: String = ""
|
||||||
def filter: PlanetSideGUID = Service.defaultPlayerGUID
|
def filter: PlanetSideGUID = Service.defaultPlayerGUID
|
||||||
}
|
}
|
||||||
|
|
@ -65,7 +97,7 @@ abstract class GenericEventServiceWithCacheAndSupport
|
||||||
stamp: EventSystemStamp,
|
stamp: EventSystemStamp,
|
||||||
eventSupportServices: List[EventServiceSupport]
|
eventSupportServices: List[EventServiceSupport]
|
||||||
) extends GenericEventServiceWithSupport(stamp, eventSupportServices) {
|
) extends GenericEventServiceWithSupport(stamp, eventSupportServices) {
|
||||||
private val flushCacheWait: Long = 50 //milliseconds
|
private val flushCacheWait: Long = 50L //milliseconds
|
||||||
private var hasCachedMessages: Boolean = false
|
private var hasCachedMessages: Boolean = false
|
||||||
private var nextTimeToFlushCache: Long = 0L
|
private var nextTimeToFlushCache: Long = 0L
|
||||||
private var emergencyFlush: Cancellable = Default.Cancellable
|
private var emergencyFlush: Cancellable = Default.Cancellable
|
||||||
|
|
@ -78,6 +110,12 @@ abstract class GenericEventServiceWithCacheAndSupport
|
||||||
super.postStop()
|
super.postStop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If there were previously no messages in the cache,
|
||||||
|
* prepare to flush the cache after the intended interval passes,
|
||||||
|
* whether the passing of that interval is detected by a new incoming message and the cache gets flushed naturally,
|
||||||
|
* or if a safety timer expires and the cache is flushed in precaution.
|
||||||
|
*/
|
||||||
private def tryRetimeFlushCache(): Unit = {
|
private def tryRetimeFlushCache(): Unit = {
|
||||||
if (!hasCachedMessages) {
|
if (!hasCachedMessages) {
|
||||||
hasCachedMessages = true
|
hasCachedMessages = true
|
||||||
|
|
@ -86,7 +124,14 @@ abstract class GenericEventServiceWithCacheAndSupport
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def pushToCache(event: CachedGenericEventMessageEnvelope): Unit = {
|
/**
|
||||||
|
* Add messages to the cache based on their channel, then their type, then their cache target identifier.
|
||||||
|
* Messages that arrive with the same cache profile as a previous message,
|
||||||
|
* but before that previous message was dispatched,
|
||||||
|
* will overwrite the previous message without fanfare or warning.
|
||||||
|
* @param event event system message
|
||||||
|
*/
|
||||||
|
private def pushToCache(event: CachedGenericEventEnvelope): Unit = {
|
||||||
val eventClassName = event.msg.getClass.getName
|
val eventClassName = event.msg.getClass.getName
|
||||||
val updateBranch = cache
|
val updateBranch = cache
|
||||||
.getOrElseUpdate(event.channel, new ConcurrentHashMap[String, CMap[PlanetSideGUID, GenericMessageEnvelope]]().asScala)
|
.getOrElseUpdate(event.channel, new ConcurrentHashMap[String, CMap[PlanetSideGUID, GenericMessageEnvelope]]().asScala)
|
||||||
|
|
@ -95,12 +140,22 @@ abstract class GenericEventServiceWithCacheAndSupport
|
||||||
tryRetimeFlushCache()
|
tryRetimeFlushCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
private def tryFlushCache(): Unit = {
|
/**
|
||||||
if (hasCachedMessages && nextTimeToFlushCache < System.currentTimeMillis()) {
|
* If the cache has messages and the current time exceeds the anticipated flush time,
|
||||||
|
* flush the cache messages to the normal event system bus.
|
||||||
|
*/
|
||||||
|
private def tryFlushCache(): Boolean = {
|
||||||
|
val willFLush = hasCachedMessages && nextTimeToFlushCache < System.currentTimeMillis()
|
||||||
|
if (willFLush) {
|
||||||
flushCache()
|
flushCache()
|
||||||
}
|
}
|
||||||
|
willFLush
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush the cache messages to the normal event system bus.
|
||||||
|
* Clear old messages then reset all flags that would force the messages to be flushed.
|
||||||
|
*/
|
||||||
private def flushCache(): Unit = {
|
private def flushCache(): Unit = {
|
||||||
cache.foreachEntry { (_, map) =>
|
cache.foreachEntry { (_, map) =>
|
||||||
map.foreachEntry { (_, map) =>
|
map.foreachEntry { (_, map) =>
|
||||||
|
|
@ -115,15 +170,24 @@ abstract class GenericEventServiceWithCacheAndSupport
|
||||||
emergencyFlush = Default.Cancellable
|
emergencyFlush = Default.Cancellable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the cache will be flushed after this message, flush the cache and then pass the message for processing.
|
||||||
|
* If the cache will not be flushed, add the message to the cache.
|
||||||
|
* In any case, procedure should always be ready to flush the cache when a message is received.
|
||||||
|
* If there is a support actor involved with a cached message, it is resolved when the message is flushed.
|
||||||
|
* @param event event system message that may be cached
|
||||||
|
*/
|
||||||
override protected def handleMessage(event: GenericMessageEnvelope): Unit = {
|
override protected def handleMessage(event: GenericMessageEnvelope): Unit = {
|
||||||
event match {
|
event match {
|
||||||
case envelope: CachedGenericEventMessageEnvelope =>
|
|
||||||
pushToCache(envelope)
|
|
||||||
case FlushCachedMessages =>
|
case FlushCachedMessages =>
|
||||||
tryFlushCache()
|
flushCache()
|
||||||
|
case _: CachedGenericEventEnvelope if tryFlushCache() =>
|
||||||
|
super.handleMessage(event)
|
||||||
|
case envelope: CachedGenericEventEnvelope =>
|
||||||
|
pushToCache(envelope)
|
||||||
case _ =>
|
case _ =>
|
||||||
|
tryFlushCache()
|
||||||
super.handleMessage(event)
|
super.handleMessage(event)
|
||||||
}
|
}
|
||||||
tryFlushCache()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,18 @@ import net.psforever.types.PlanetSideGUID
|
||||||
|
|
||||||
import scala.annotation.unused
|
import scala.annotation.unused
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A message when there should be none, but a message is required to be defined anyway.
|
||||||
|
* @see `GenericSupportEnvelopeOnly`
|
||||||
|
*/
|
||||||
case object NoMessage extends SelfRespondingEvent
|
case object NoMessage extends SelfRespondingEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A response when there should be none, but a response is required to be defined anyway.
|
||||||
|
* @see `GenericSupportEnvelopeOnly`
|
||||||
|
* @see `NoReply`
|
||||||
|
* @see `Undelivered`
|
||||||
|
*/
|
||||||
case object NoResponseEnvelope extends GenericResponseEnvelope {
|
case object NoResponseEnvelope extends GenericResponseEnvelope {
|
||||||
def reply: EventResponse = NoReply
|
def reply: EventResponse = NoReply
|
||||||
def stamp: EventSystemStamp = Undelivered
|
def stamp: EventSystemStamp = Undelivered
|
||||||
|
|
@ -18,20 +28,39 @@ case object NoResponseEnvelope extends GenericResponseEnvelope {
|
||||||
def filter: PlanetSideGUID = Service.defaultPlayerGUID
|
def filter: PlanetSideGUID = Service.defaultPlayerGUID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A framework for how support actors are to be submitted to an event system.
|
||||||
|
* The bare bones are a label by which the support actor is identified for message routing,
|
||||||
|
* and a function that constructs the support actor within the context of the event system.
|
||||||
|
* @see `ActorContext`
|
||||||
|
*/
|
||||||
trait EventServiceSupport {
|
trait EventServiceSupport {
|
||||||
def label: String
|
def label: String
|
||||||
def constructor(@unused context: ActorContext): ActorRef
|
def constructor(@unused context: ActorContext): ActorRef
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A framework for communicating messages to support actors within an event system.
|
||||||
|
* The bare bones are a label by which the support actor is identified for message routing,
|
||||||
|
* and the message payload for the support actor to process.
|
||||||
|
* @see `ActorContext`
|
||||||
|
*/
|
||||||
trait GenericMessageToSupport {
|
trait GenericMessageToSupport {
|
||||||
def supportLabel: String
|
def supportLabel: String
|
||||||
def supportMessage: Any
|
def supportMessage: Any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An envelope framework for communicating messages to support actors within an event system
|
||||||
|
* and also interacting with the event system directly.
|
||||||
|
*/
|
||||||
trait GenericSupportEnvelope
|
trait GenericSupportEnvelope
|
||||||
extends GenericMessageToSupport
|
extends GenericMessageToSupport
|
||||||
with MessageTransformationBehavior
|
with MessageTransformationBehavior
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An envelope framework for communicating messages to support actors within an event system only.
|
||||||
|
*/
|
||||||
trait GenericSupportEnvelopeOnly
|
trait GenericSupportEnvelopeOnly
|
||||||
extends GenericMessageToSupport
|
extends GenericMessageToSupport
|
||||||
with GenericMessageEnvelope {
|
with GenericMessageEnvelope {
|
||||||
|
|
@ -40,9 +69,17 @@ trait GenericSupportEnvelopeOnly
|
||||||
def filter: PlanetSideGUID = Service.defaultPlayerGUID
|
def filter: PlanetSideGUID = Service.defaultPlayerGUID
|
||||||
def msg: EventMessage = NoMessage
|
def msg: EventMessage = NoMessage
|
||||||
|
|
||||||
def response(@unused stamp: EventSystemStamp, @unused sendToChannel: String => String): GenericResponseEnvelope = NoResponseEnvelope
|
def response(@unused stamp: EventSystemStamp): GenericResponseEnvelope = NoResponseEnvelope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advanced opt-in event response relay system.
|
||||||
|
* Includes a system of specialized child actors that serve specific repeatable operations
|
||||||
|
* whose behaviors are intentionally synchronized on this event pipeline
|
||||||
|
* or whose collective operational overhead can be streamlined by reliance on a singular pipeline.
|
||||||
|
* @param stamp distinct tag associated with an event system
|
||||||
|
* @param eventSupportServices list of support actors to initialize
|
||||||
|
*/
|
||||||
abstract class GenericEventServiceWithSupport
|
abstract class GenericEventServiceWithSupport
|
||||||
(
|
(
|
||||||
stamp: EventSystemStamp,
|
stamp: EventSystemStamp,
|
||||||
|
|
@ -54,6 +91,12 @@ abstract class GenericEventServiceWithSupport
|
||||||
.map { supportService => (supportService.label, supportService.constructor(context)) }
|
.map { supportService => (supportService.label, supportService.constructor(context)) }
|
||||||
.toMap[String, ActorRef]
|
.toMap[String, ActorRef]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the label assigned to a support actor
|
||||||
|
* to locate that support actor on this event system
|
||||||
|
* and forward to it a payload message.
|
||||||
|
* @param msg event system message carrying an additional message for a support actor
|
||||||
|
*/
|
||||||
private def forwardToSupport(msg: GenericMessageToSupport): Unit = {
|
private def forwardToSupport(msg: GenericMessageToSupport): Unit = {
|
||||||
supportServices
|
supportServices
|
||||||
.get(msg.supportLabel)
|
.get(msg.supportLabel)
|
||||||
|
|
@ -66,15 +109,20 @@ abstract class GenericEventServiceWithSupport
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the message involves the support actor subsystem, forward the message to it for additional processing.
|
||||||
|
* Afterwards, or if not, merely call up to previously-established message handling if warranted.
|
||||||
|
* @param event event system message that may be carrying an additional message for a support actor
|
||||||
|
*/
|
||||||
override protected def handleMessage(event: GenericMessageEnvelope): Unit = {
|
override protected def handleMessage(event: GenericMessageEnvelope): Unit = {
|
||||||
event match {
|
event match {
|
||||||
case msg: GenericSupportEnvelopeOnly =>
|
case msg: GenericSupportEnvelopeOnly =>
|
||||||
forwardToSupport(msg)
|
forwardToSupport(msg)
|
||||||
case msg: GenericSupportEnvelope =>
|
case msg: GenericSupportEnvelope =>
|
||||||
forwardToSupport(msg)
|
forwardToSupport(msg)
|
||||||
eventBus.publish(composeResponseEnvelope(event))
|
super.handleMessage(event)
|
||||||
case event =>
|
case event =>
|
||||||
eventBus.publish(composeResponseEnvelope(event))
|
super.handleMessage(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,16 @@ import akka.event.{ActorEventBus, SubchannelClassification}
|
||||||
import akka.util.Subclassification
|
import akka.util.Subclassification
|
||||||
import net.psforever.services.base.envelope.GenericResponseEnvelope
|
import net.psforever.services.base.envelope.GenericResponseEnvelope
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maintain a list of endpoints that have subscribed to a specific perspective
|
||||||
|
* internally called a `Classifier` and externally referred to as a "channel".
|
||||||
|
* When an `Event` - externally referred colloquially as a "message" - is received,
|
||||||
|
* to be published,
|
||||||
|
* match the "channel" to a list of known `Classifier` endpoints.
|
||||||
|
* Each of these `Subscribers` should receive the message.
|
||||||
|
* @see `GenericResponseEnvelope`
|
||||||
|
* @see `ActorEventBus.Subscriber`
|
||||||
|
*/
|
||||||
class GenericEventBus
|
class GenericEventBus
|
||||||
extends ActorEventBus with SubchannelClassification {
|
extends ActorEventBus with SubchannelClassification {
|
||||||
type Event = GenericResponseEnvelope
|
type Event = GenericResponseEnvelope
|
||||||
|
|
@ -12,6 +22,11 @@ class GenericEventBus
|
||||||
|
|
||||||
protected def classify(event: Event): Classifier = event.channel
|
protected def classify(event: Event): Classifier = event.channel
|
||||||
|
|
||||||
|
/*
|
||||||
|
Example Classifiers: "foo", "foo.fizz", and "foo.buzz"
|
||||||
|
In general, Classifier channels will perform left-pattern matching.
|
||||||
|
"foo" will publish to "foo", "foo.fizz", and "foo.buzz"
|
||||||
|
*/
|
||||||
protected def subclassification: Subclassification[String] =
|
protected def subclassification: Subclassification[String] =
|
||||||
new Subclassification[Classifier] {
|
new Subclassification[Classifier] {
|
||||||
def isEqual(x: Classifier, y: Classifier): Boolean = x == y
|
def isEqual(x: Classifier, y: Classifier): Boolean = x == y
|
||||||
|
|
@ -19,15 +34,11 @@ class GenericEventBus
|
||||||
def isSubclass(x: Classifier, y: Classifier): Boolean = x.startsWith(y)
|
def isSubclass(x: Classifier, y: Classifier): Boolean = x.startsWith(y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def publish(event: Event): Unit = {
|
||||||
|
super[SubchannelClassification].publish(event)
|
||||||
|
}
|
||||||
|
|
||||||
protected def publish(event: Event, subscriber: Subscriber): Unit = {
|
protected def publish(event: Event, subscriber: Subscriber): Unit = {
|
||||||
subscriber ! event
|
subscriber ! event
|
||||||
}
|
}
|
||||||
|
|
||||||
override def publish(event: Event): Unit = {
|
|
||||||
truePublish(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
def truePublish(event: Event): Unit = {
|
|
||||||
super[SubchannelClassification].publish(event)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,13 @@ package net.psforever.services.base.envelope
|
||||||
|
|
||||||
import net.psforever.types.PlanetSideGUID
|
import net.psforever.types.PlanetSideGUID
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base of all envelope classes.
|
||||||
|
* Defines a channel and a filter, both of which server the purpose of routing the message to its destination.
|
||||||
|
*/
|
||||||
trait AllEnvelopes {
|
trait AllEnvelopes {
|
||||||
|
/** set of subscribers on an event system bus the envelope should reach */
|
||||||
def channel: String
|
def channel: String
|
||||||
|
/** specific subscriber endpoint to be excluded (the subscriber should filter themselves) */
|
||||||
def filter: PlanetSideGUID
|
def filter: PlanetSideGUID
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,23 @@ import net.psforever.types.PlanetSideGUID
|
||||||
|
|
||||||
trait GenericMessageEnvelope
|
trait GenericMessageEnvelope
|
||||||
extends AllEnvelopes {
|
extends AllEnvelopes {
|
||||||
|
/** channel information provided with the message */
|
||||||
def originalChannel: String
|
def originalChannel: String
|
||||||
|
/** input payload transported by this envelope */
|
||||||
def msg: EventMessage
|
def msg: EventMessage
|
||||||
def response(stamp: EventSystemStamp, sendToChannel: String => String): GenericResponseEnvelope
|
/** method that counts as "processing" the envelope by an event system;
|
||||||
|
* the event system supplies their stamp and converts the message envelope into a response envelope;
|
||||||
|
* the input message is converted into a response message */
|
||||||
|
def response(stamp: EventSystemStamp): GenericResponseEnvelope
|
||||||
}
|
}
|
||||||
|
|
||||||
object GenericMessageEnvelope {
|
object GenericMessageEnvelope {
|
||||||
|
/**
|
||||||
|
* The `unapply`ed data from a message envelope resembles the data from includes the filter and the channel information.
|
||||||
|
* The original channel information.
|
||||||
|
* @param obj response envelope
|
||||||
|
* @return a tuple containing the channel, filter, and reply message
|
||||||
|
*/
|
||||||
def unapply(obj: GenericMessageEnvelope): Option[(String, PlanetSideGUID, EventMessage)] = {
|
def unapply(obj: GenericMessageEnvelope): Option[(String, PlanetSideGUID, EventMessage)] = {
|
||||||
Some((obj.originalChannel, obj.filter, obj.msg))
|
Some((obj.originalChannel, obj.filter, obj.msg))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,37 @@ import net.psforever.services.base.EventSystemStamp
|
||||||
import net.psforever.services.base.message.{EventMessage, EventResponse}
|
import net.psforever.services.base.message.{EventMessage, EventResponse}
|
||||||
import net.psforever.types.PlanetSideGUID
|
import net.psforever.types.PlanetSideGUID
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The framework of an event system envelope that is treated as the processed complement of a message envelope.
|
||||||
|
*/
|
||||||
trait GenericResponseEnvelope
|
trait GenericResponseEnvelope
|
||||||
extends AllEnvelopes {
|
extends AllEnvelopes {
|
||||||
|
/** result of converting the message envelope input payload into output payload */
|
||||||
def reply: EventResponse
|
def reply: EventResponse
|
||||||
|
/** marker indicating the routing through which the original message was processed */
|
||||||
def stamp: EventSystemStamp
|
def stamp: EventSystemStamp
|
||||||
}
|
}
|
||||||
|
|
||||||
object GenericResponseEnvelope {
|
object GenericResponseEnvelope {
|
||||||
def apply(stamp: EventSystemStamp, channel: String, filter: PlanetSideGUID, msg: EventMessage): GenericResponseEnvelope ={
|
/**
|
||||||
|
* Fake a response envelope, as if processed by a specific event system.
|
||||||
|
* @param stamp marker indicating the routing through which the original message was processed
|
||||||
|
* @param channel set of subscribers on an event system bus the envelope should reach
|
||||||
|
* @param filter a specific subscriber endpoint to be excluded
|
||||||
|
* @param msg input payload transported by this envelope
|
||||||
|
* @return a faked but typically acceptable response envelope
|
||||||
|
*/
|
||||||
|
def apply(stamp: EventSystemStamp, channel: String, filter: PlanetSideGUID, msg: EventMessage): GenericResponseEnvelope = {
|
||||||
val envelope = MessageEnvelope(channel, filter, msg)
|
val envelope = MessageEnvelope(channel, filter, msg)
|
||||||
envelope.response(stamp, s => s)
|
envelope.response(stamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
def unapply(obj: GenericResponseEnvelope): Option[(String, PlanetSideGUID, EventResponse)] =
|
/**
|
||||||
|
* The `unapply`ed data from a response envelope resembles the data from includes the filter and the channel information.
|
||||||
|
* @param obj response envelope
|
||||||
|
* @return a tuple containing the channel, filter, and reply message
|
||||||
|
*/
|
||||||
|
def unapply(obj: GenericResponseEnvelope): Option[(String, PlanetSideGUID, EventResponse)] = {
|
||||||
Some((obj.channel, obj.filter, obj.reply))
|
Some((obj.channel, obj.filter, obj.reply))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,29 @@ import net.psforever.services.base.EventSystemStamp
|
||||||
import net.psforever.services.base.message.{EventMessage, EventResponse}
|
import net.psforever.services.base.message.{EventMessage, EventResponse}
|
||||||
import net.psforever.types.PlanetSideGUID
|
import net.psforever.types.PlanetSideGUID
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A response when there should be none, but a reply is required to be defined anyway.
|
||||||
|
*/
|
||||||
case object NoReply extends EventResponse
|
case object NoReply extends EventResponse
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A stamp that represents not having been processed by an event system.
|
||||||
|
* Should never been given out to an event system.
|
||||||
|
*/
|
||||||
case object Undelivered extends EventSystemStamp
|
case object Undelivered extends EventSystemStamp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mechanics of a proper event system envelope.
|
||||||
|
* The envelope has two forms: the input message envelope and the output response envelope.
|
||||||
|
* The event system uses its stamp to mark that the message form converts into the response form when being processed.
|
||||||
|
* In terms of proper metaphor,
|
||||||
|
* it makes no sense for a post office take a letter out of its original envelope,
|
||||||
|
* write a completely different envelope,
|
||||||
|
* write a completely different letter,
|
||||||
|
* package the new letter in the new envelope,
|
||||||
|
* and deliver it is place of the originals.
|
||||||
|
* That would be considered fraud.
|
||||||
|
*/
|
||||||
trait MessageTransformationBehavior
|
trait MessageTransformationBehavior
|
||||||
extends GenericMessageEnvelope
|
extends GenericMessageEnvelope
|
||||||
with GenericResponseEnvelope {
|
with GenericResponseEnvelope {
|
||||||
|
|
@ -16,22 +35,24 @@ trait MessageTransformationBehavior
|
||||||
private var outputChannel: String = originalChannel
|
private var outputChannel: String = originalChannel
|
||||||
private var outputReply: EventResponse = NoReply
|
private var outputReply: EventResponse = NoReply
|
||||||
|
|
||||||
// satisfies GenericMessageEnvelope2 (and GenericResponseEnvelope2)
|
// satisfies GenericMessageEnvelope (and GenericResponseEnvelope)
|
||||||
def channel: String = outputChannel
|
def channel: String = outputChannel
|
||||||
|
|
||||||
// satisfies GenericMessageEnvelope2
|
// satisfies GenericMessageEnvelope
|
||||||
def response(stamp: EventSystemStamp, sendToChannel: String => String): GenericResponseEnvelope = {
|
def response(stamp: EventSystemStamp): GenericResponseEnvelope = {
|
||||||
outputStamp = stamp
|
outputStamp = stamp
|
||||||
outputChannel = sendToChannel(originalChannel)
|
outputChannel = stamp.routing(originalChannel)
|
||||||
outputReply = msg.response()
|
outputReply = msg.response()
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
// satisfies GenericResponseEnvelope2
|
// satisfies GenericResponseEnvelope
|
||||||
def stamp: EventSystemStamp = outputStamp
|
def stamp: EventSystemStamp = outputStamp
|
||||||
|
|
||||||
def reply: EventResponse = outputReply
|
def reply: EventResponse = outputReply
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* A proper event system envelope.
|
||||||
|
*/
|
||||||
case class MessageEnvelope(originalChannel: String, filter: PlanetSideGUID, msg: EventMessage)
|
case class MessageEnvelope(originalChannel: String, filter: PlanetSideGUID, msg: EventMessage)
|
||||||
extends MessageTransformationBehavior
|
extends MessageTransformationBehavior
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,15 @@ package net.psforever.services.galaxy
|
||||||
|
|
||||||
import net.psforever.services.base.{EventSystemStamp, GenericEventService}
|
import net.psforever.services.base.{EventSystemStamp, GenericEventService}
|
||||||
|
|
||||||
case object GalaxyStamp extends EventSystemStamp
|
case object GalaxyStamp extends EventSystemStamp {
|
||||||
|
override def routing(channel: String): String = {
|
||||||
class GalaxyService
|
|
||||||
extends GenericEventService(stamp = GalaxyStamp) {
|
|
||||||
override protected def formatChannel(channel: String): String = {
|
|
||||||
if (channel.trim.isEmpty) {
|
if (channel.trim.isEmpty) {
|
||||||
"/all"
|
"/out"
|
||||||
} else {
|
} else {
|
||||||
s"/$channel"
|
s"/$channel/out"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class GalaxyService
|
||||||
|
extends GenericEventService(stamp = GalaxyStamp)
|
||||||
|
|
|
||||||
|
|
@ -176,20 +176,20 @@ class SquadService extends Actor {
|
||||||
|
|
||||||
def receive: Receive = {
|
def receive: Receive = {
|
||||||
//subscribe to a faction's channel - necessary to receive updates about listed squads
|
//subscribe to a faction's channel - necessary to receive updates about listed squads
|
||||||
case Service.Join(faction) if SquadService.FactionWordSalad.indexOf(faction) > -1 =>
|
case Service.Join(faction, _) if SquadService.FactionWordSalad.indexOf(faction) > -1 =>
|
||||||
JoinByFaction(faction, sender())
|
JoinByFaction(faction, sender())
|
||||||
|
|
||||||
//subscribe to the player's personal channel - necessary for future and previous squad information
|
//subscribe to the player's personal channel - necessary for future and previous squad information
|
||||||
case Service.Join(char_id) =>
|
case Service.Join(char_id, _) =>
|
||||||
JoinByCharacterId(char_id, sender())
|
JoinByCharacterId(char_id, sender())
|
||||||
|
|
||||||
case Service.Leave(Some(faction)) if SquadService.FactionWordSalad.indexOf(faction) > -1 =>
|
case Service.Leave(faction) if SquadService.FactionWordSalad.indexOf(faction) > -1 =>
|
||||||
LeaveByFaction(faction, sender())
|
LeaveByFaction(faction, sender())
|
||||||
|
|
||||||
case Service.Leave(Some(char_id)) =>
|
case Service.Leave(char_id) =>
|
||||||
LeaveByCharacterId(char_id, sender())
|
LeaveByCharacterId(char_id, sender())
|
||||||
|
|
||||||
case Service.Leave(None) | Service.LeaveAll() =>
|
case Service.LeaveAll =>
|
||||||
LeaveInGeneral(sender())
|
LeaveInGeneral(sender())
|
||||||
|
|
||||||
case Terminated(actorRef) =>
|
case Terminated(actorRef) =>
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ class OrbitalShuttlePadControlTest extends FreedContextActorTest {
|
||||||
"startup and create the shuttle" in {
|
"startup and create the shuttle" in {
|
||||||
assert(building.Amenities.size == 9)
|
assert(building.Amenities.size == 9)
|
||||||
assert(vehicles.isEmpty)
|
assert(vehicles.isEmpty)
|
||||||
pad.Actor ! Service.Startup()
|
pad.Actor ! Service.Startup
|
||||||
expectNoMessage(max = 5 seconds)
|
expectNoMessage(max = 5 seconds)
|
||||||
assert(building.Amenities.size == 10)
|
assert(building.Amenities.size == 10)
|
||||||
assert(vehicles.size == 1)
|
assert(vehicles.size == 1)
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ class ResourceSiloControlStartupTest extends ActorTest {
|
||||||
|
|
||||||
"Resource silo" should {
|
"Resource silo" should {
|
||||||
"startup properly" in {
|
"startup properly" in {
|
||||||
obj.Actor ! Service.Startup()
|
obj.Actor ! Service.Startup
|
||||||
expectNoMessage(max = 1000 milliseconds)
|
expectNoMessage(max = 1000 milliseconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -113,7 +113,7 @@ class ResourceSiloControlStartupMessageNoneTest extends ActorTest {
|
||||||
"report if it has no NTU on startup" in {
|
"report if it has no NTU on startup" in {
|
||||||
obj.NtuCapacitor = 0
|
obj.NtuCapacitor = 0
|
||||||
assert(obj.NtuCapacitor == 0)
|
assert(obj.NtuCapacitor == 0)
|
||||||
obj.Actor ! Service.Startup()
|
obj.Actor ! Service.Startup
|
||||||
val ownerMsg = buildingEvents.receiveOne(200 milliseconds)
|
val ownerMsg = buildingEvents.receiveOne(200 milliseconds)
|
||||||
assert(ownerMsg match {
|
assert(ownerMsg match {
|
||||||
case BuildingActor.NtuDepleted() => true
|
case BuildingActor.NtuDepleted() => true
|
||||||
|
|
@ -139,7 +139,7 @@ class ResourceSiloControlStartupMessageSomeTest extends ActorTest {
|
||||||
"report if it has any NTU on startup" in {
|
"report if it has any NTU on startup" in {
|
||||||
obj.NtuCapacitor = 1
|
obj.NtuCapacitor = 1
|
||||||
assert(obj.NtuCapacitor == 1)
|
assert(obj.NtuCapacitor == 1)
|
||||||
obj.Actor ! Service.Startup()
|
obj.Actor ! Service.Startup
|
||||||
val ownerMsg = buildingEvents.receiveOne(200 milliseconds)
|
val ownerMsg = buildingEvents.receiveOne(200 milliseconds)
|
||||||
assert(ownerMsg match {
|
assert(ownerMsg match {
|
||||||
case BuildingActor.SuppliedWithNtu() => true
|
case BuildingActor.SuppliedWithNtu() => true
|
||||||
|
|
@ -197,7 +197,7 @@ class ResourceSiloControlUseTest extends FreedContextActorTest {
|
||||||
ant.DeploymentState = DriveState.Deployed
|
ant.DeploymentState = DriveState.Deployed
|
||||||
building.Amenities = silo
|
building.Amenities = silo
|
||||||
silo.Actor = system.actorOf(Props(classOf[ResourceSiloControl], silo), "test-silo")
|
silo.Actor = system.actorOf(Props(classOf[ResourceSiloControl], silo), "test-silo")
|
||||||
silo.Actor ! Service.Startup()
|
silo.Actor ! Service.Startup
|
||||||
|
|
||||||
"Resource silo" should {
|
"Resource silo" should {
|
||||||
"respond when being used" in {
|
"respond when being used" in {
|
||||||
|
|
@ -225,7 +225,7 @@ class ResourceSiloControlNtuWarningTest extends ActorTest {
|
||||||
|
|
||||||
val zoneEvents: TestProbe = TestProbe("zone-events")
|
val zoneEvents: TestProbe = TestProbe("zone-events")
|
||||||
zone.AvatarEvents = zoneEvents.ref
|
zone.AvatarEvents = zoneEvents.ref
|
||||||
obj.Actor ! Service.Startup()
|
obj.Actor ! Service.Startup
|
||||||
obj.Actor ! ResourceSilo.UpdateChargeLevel(-obj.NtuCapacitor)
|
obj.Actor ! ResourceSilo.UpdateChargeLevel(-obj.NtuCapacitor)
|
||||||
zoneEvents.receiveN(3, 500.milliseconds) //events from setup
|
zoneEvents.receiveN(3, 500.milliseconds) //events from setup
|
||||||
|
|
||||||
|
|
@ -257,7 +257,7 @@ class ResourceSiloControlUpdate1Test extends ActorTest {
|
||||||
val buildingEvents: TestProbe = TestProbe("building-events")
|
val buildingEvents: TestProbe = TestProbe("building-events")
|
||||||
zone.AvatarEvents = zoneEvents.ref
|
zone.AvatarEvents = zoneEvents.ref
|
||||||
bldg.Actor = buildingEvents.ref
|
bldg.Actor = buildingEvents.ref
|
||||||
obj.Actor ! Service.Startup()
|
obj.Actor ! Service.Startup
|
||||||
buildingEvents.receiveOne(500 milliseconds) //message caused by "startup"
|
buildingEvents.receiveOne(500 milliseconds) //message caused by "startup"
|
||||||
obj.Actor ! ResourceSilo.UpdateChargeLevel(-obj.NtuCapacitor)
|
obj.Actor ! ResourceSilo.UpdateChargeLevel(-obj.NtuCapacitor)
|
||||||
zoneEvents.receiveN(3, 500.milliseconds) //events from setup
|
zoneEvents.receiveN(3, 500.milliseconds) //events from setup
|
||||||
|
|
@ -301,7 +301,7 @@ class ResourceSiloControlUpdate2Test extends ActorTest {
|
||||||
val buildingEvents: TestProbe = TestProbe("building-events")
|
val buildingEvents: TestProbe = TestProbe("building-events")
|
||||||
zone.AvatarEvents = zoneEvents.ref
|
zone.AvatarEvents = zoneEvents.ref
|
||||||
bldg.Actor = buildingEvents.ref
|
bldg.Actor = buildingEvents.ref
|
||||||
obj.Actor ! Service.Startup()
|
obj.Actor ! Service.Startup
|
||||||
buildingEvents.receiveOne(500 milliseconds) //message caused by "startup"
|
buildingEvents.receiveOne(500 milliseconds) //message caused by "startup"
|
||||||
obj.Actor ! ResourceSilo.UpdateChargeLevel(-obj.NtuCapacitor + 100)
|
obj.Actor ! ResourceSilo.UpdateChargeLevel(-obj.NtuCapacitor + 100)
|
||||||
zoneEvents.receiveN(3, 500.milliseconds) //events from setup
|
zoneEvents.receiveN(3, 500.milliseconds) //events from setup
|
||||||
|
|
@ -345,7 +345,7 @@ class ResourceSiloControlNoUpdateTest extends ActorTest {
|
||||||
val buildingEvents: TestProbe = TestProbe("building-events")
|
val buildingEvents: TestProbe = TestProbe("building-events")
|
||||||
zone.AvatarEvents = zoneEvents.ref
|
zone.AvatarEvents = zoneEvents.ref
|
||||||
bldg.Actor = buildingEvents.ref
|
bldg.Actor = buildingEvents.ref
|
||||||
obj.Actor ! Service.Startup()
|
obj.Actor ! Service.Startup
|
||||||
obj.NtuCapacitor = 0
|
obj.NtuCapacitor = 0
|
||||||
|
|
||||||
"Resource silo" should {
|
"Resource silo" should {
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,7 @@ class ProximityTerminalControlStartTest extends ActorTest {
|
||||||
|
|
||||||
avatar.GUID = PlanetSideGUID(1)
|
avatar.GUID = PlanetSideGUID(1)
|
||||||
terminal.GUID = PlanetSideGUID(2)
|
terminal.GUID = PlanetSideGUID(2)
|
||||||
terminal.Actor ! Service.Startup()
|
terminal.Actor ! Service.Startup
|
||||||
expectNoMessage(500 milliseconds) //spacer
|
expectNoMessage(500 milliseconds) //spacer
|
||||||
val probe1 = new TestProbe(system, "local-events")
|
val probe1 = new TestProbe(system, "local-events")
|
||||||
val probe2 = new TestProbe(system, "target-callback")
|
val probe2 = new TestProbe(system, "target-callback")
|
||||||
|
|
@ -258,7 +258,7 @@ class ProximityTerminalControlTwoUsersTest extends ActorTest {
|
||||||
avatar.GUID = PlanetSideGUID(1)
|
avatar.GUID = PlanetSideGUID(1)
|
||||||
avatar2.GUID = PlanetSideGUID(2)
|
avatar2.GUID = PlanetSideGUID(2)
|
||||||
terminal.GUID = PlanetSideGUID(3)
|
terminal.GUID = PlanetSideGUID(3)
|
||||||
terminal.Actor ! Service.Startup()
|
terminal.Actor ! Service.Startup
|
||||||
expectNoMessage(500 milliseconds) //spacer
|
expectNoMessage(500 milliseconds) //spacer
|
||||||
val probe1 = new TestProbe(system, "local-events")
|
val probe1 = new TestProbe(system, "local-events")
|
||||||
val probe2 = new TestProbe(system, "target-callback-1")
|
val probe2 = new TestProbe(system, "target-callback-1")
|
||||||
|
|
@ -317,7 +317,7 @@ class ProximityTerminalControlStopTest extends ActorTest {
|
||||||
|
|
||||||
avatar.GUID = PlanetSideGUID(1)
|
avatar.GUID = PlanetSideGUID(1)
|
||||||
terminal.GUID = PlanetSideGUID(2)
|
terminal.GUID = PlanetSideGUID(2)
|
||||||
terminal.Actor ! Service.Startup()
|
terminal.Actor ! Service.Startup
|
||||||
expectNoMessage(500 milliseconds) //spacer
|
expectNoMessage(500 milliseconds) //spacer
|
||||||
val probe1 = new TestProbe(system, "local-events")
|
val probe1 = new TestProbe(system, "local-events")
|
||||||
val probe2 = new TestProbe(system, "target-callback")
|
val probe2 = new TestProbe(system, "target-callback")
|
||||||
|
|
@ -395,7 +395,7 @@ class ProximityTerminalControlNotStopTest extends ActorTest {
|
||||||
avatar.GUID = PlanetSideGUID(1)
|
avatar.GUID = PlanetSideGUID(1)
|
||||||
avatar2.GUID = PlanetSideGUID(2)
|
avatar2.GUID = PlanetSideGUID(2)
|
||||||
terminal.GUID = PlanetSideGUID(3)
|
terminal.GUID = PlanetSideGUID(3)
|
||||||
terminal.Actor ! Service.Startup()
|
terminal.Actor ! Service.Startup
|
||||||
expectNoMessage(500 milliseconds) //spacer
|
expectNoMessage(500 milliseconds) //spacer
|
||||||
val probe1 = new TestProbe(system, "local-events")
|
val probe1 = new TestProbe(system, "local-events")
|
||||||
val probe2 = new TestProbe(system, "target-callback-1")
|
val probe2 = new TestProbe(system, "target-callback-1")
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import akka.testkit.TestProbe
|
||||||
import base.ActorTest
|
import base.ActorTest
|
||||||
import net.psforever.services.Service
|
import net.psforever.services.Service
|
||||||
import net.psforever.services.base.message.EventMessage
|
import net.psforever.services.base.message.EventMessage
|
||||||
import net.psforever.services.base.{CachedGenericEventMessageEnvelope, EventServiceSupport, GenericEventServiceWithCacheAndSupport, GenericSupportEnvelope}
|
import net.psforever.services.base.{CachedGenericEventEnvelope, EventServiceSupport, GenericEventServiceWithCacheAndSupport, GenericSupportEnvelope}
|
||||||
import net.psforever.types.PlanetSideGUID
|
import net.psforever.types.PlanetSideGUID
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
@ -20,7 +20,7 @@ object EventServiceCacheSupportTest {
|
||||||
originalChannel: String,
|
originalChannel: String,
|
||||||
override val msg: EventMessage,
|
override val msg: EventMessage,
|
||||||
supportMessage: Any
|
supportMessage: Any
|
||||||
) extends CachedGenericEventMessageEnvelope with GenericSupportEnvelope {
|
) extends CachedGenericEventEnvelope with GenericSupportEnvelope {
|
||||||
assert(guid != Service.defaultPlayerGUID, "can not cache message under default GUID")
|
assert(guid != Service.defaultPlayerGUID, "can not cache message under default GUID")
|
||||||
def filter: PlanetSideGUID = Service.defaultPlayerGUID
|
def filter: PlanetSideGUID = Service.defaultPlayerGUID
|
||||||
def supportLabel: String = "supportActor"
|
def supportLabel: String = "supportActor"
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import akka.testkit.TestProbe
|
||||||
import base.ActorTest
|
import base.ActorTest
|
||||||
import net.psforever.services.Service
|
import net.psforever.services.Service
|
||||||
import net.psforever.services.base.GenericEventService
|
import net.psforever.services.base.GenericEventService
|
||||||
import net.psforever.services.base.envelope.GenericResponseEnvelope
|
import net.psforever.services.base.envelope.{GenericResponseEnvelope, MessageEnvelope}
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
|
@ -160,7 +160,7 @@ class EventServiceTest7 extends ActorTest {
|
||||||
case _ => assert(false, "(7) message expected but not received")
|
case _ => assert(false, "(7) message expected but not received")
|
||||||
}
|
}
|
||||||
|
|
||||||
events.tell(Service.Leave(), probe.ref)
|
events.tell(Service.LeaveAll, probe.ref)
|
||||||
events ! MessageEnvelope("test", Service.defaultPlayerGUID, TestMessage(5))
|
events ! MessageEnvelope("test", Service.defaultPlayerGUID, TestMessage(5))
|
||||||
events ! MessageEnvelope("anotherTest", Service.defaultPlayerGUID, TestMessage(6))
|
events ! MessageEnvelope("anotherTest", Service.defaultPlayerGUID, TestMessage(6))
|
||||||
probe.expectNoMessage(250 milliseconds)
|
probe.expectNoMessage(250 milliseconds)
|
||||||
|
|
@ -190,7 +190,7 @@ class EventServiceTest8 extends ActorTest {
|
||||||
case _ => assert(false, "(7) message expected but not received")
|
case _ => assert(false, "(7) message expected but not received")
|
||||||
}
|
}
|
||||||
|
|
||||||
events.tell(Service.LeaveAll(), probe.ref)
|
events.tell(Service.LeaveAll, probe.ref)
|
||||||
events ! MessageEnvelope("test", Service.defaultPlayerGUID, TestMessage(5))
|
events ! MessageEnvelope("test", Service.defaultPlayerGUID, TestMessage(5))
|
||||||
events ! MessageEnvelope("anotherTest", Service.defaultPlayerGUID, TestMessage(6))
|
events ! MessageEnvelope("anotherTest", Service.defaultPlayerGUID, TestMessage(6))
|
||||||
probe.expectNoMessage(250 milliseconds)
|
probe.expectNoMessage(250 milliseconds)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue