changing the reporting visibility of the event bus broadcast channel and removing the need to maintain an 'original channel'

This commit is contained in:
Fate-JH 2026-03-05 14:53:32 -05:00
parent f05a0ab96a
commit dbd90e6eb1
20 changed files with 44 additions and 48 deletions

View file

@ -13,7 +13,7 @@ import net.psforever.services.base.message.ObjectDelete
import scala.concurrent.duration._ import scala.concurrent.duration._
final case class ReleaseEnvelope( final case class ReleaseEnvelope(
originalChannel: String, channel: String,
filter: PlanetSideGUID, filter: PlanetSideGUID,
msg: Release msg: Release
) )

View file

@ -14,7 +14,7 @@ import net.psforever.types.PlanetSideGUID
import scala.concurrent.duration._ import scala.concurrent.duration._
final case class PickupItemEnvelope( final case class PickupItemEnvelope(
originalChannel: String, channel: String,
filter: PlanetSideGUID, filter: PlanetSideGUID,
msg: PickupItem, msg: PickupItem,
zone: Zone zone: Zone
@ -33,7 +33,7 @@ object PickupItemEnvelope {
} }
final case class DropItemEnvelope( final case class DropItemEnvelope(
originalChannel: String, channel: String,
filter: PlanetSideGUID, filter: PlanetSideGUID,
msg: DropItem, msg: DropItem,
zone: Zone zone: Zone

View file

@ -18,11 +18,12 @@ import org.log4s.Logger
*/ */
trait EventSystemStamp { trait EventSystemStamp {
/* /*
Example Classifiers: "foo", "foo.fizz", and "foo.buzz" Example:
In general, Classifier channels will perform left-pattern matching. The channels are "foo", "foo.fizz", and "foo.buzz"
"foo" will publish to "foo", "foo.fizz", and "foo.buzz" In general, Classifier channels will perform left-pattern matching
To isolate "foo", one must distinguish it with a right-pattern. Publishing to channel "foo" will allocate Classifiers "foo", "foo.fizz", and "foo.buzz"
"foo" is appended as "foo.bar" and no longer publishes to "foo.fizz.bar" or to "foo.buzz.bar" To isolate "foo", one must distinguish it with a right-pattern such as "out"
Publishing to channel "foo.out" no longer publishes to "foo.fizz.out" or to "foo.buzz.out"
*/ */
/** /**
* Take an input channel and produce the publishing output channel. * Take an input channel and produce the publishing output channel.

View file

@ -31,7 +31,7 @@ trait CachedGenericEventEnvelope
final case class CachedEnvelope( final case class CachedEnvelope(
guid: PlanetSideGUID, guid: PlanetSideGUID,
originalChannel: String, channel: String,
filter: PlanetSideGUID, filter: PlanetSideGUID,
msg: EventMessage msg: EventMessage
) extends CachedGenericEventEnvelope { ) extends CachedGenericEventEnvelope {

View file

@ -20,19 +20,20 @@ class GenericEventBus
type Event = GenericResponseEnvelope type Event = GenericResponseEnvelope
type Classifier = String type Classifier = String
protected def classify(event: Event): Classifier = event.channel protected def classify(event: Event): Classifier = event.outChannel
/* /*
Example Classifiers: "foo", "foo.fizz", and "foo.buzz" Example:
In general, Classifier channels will perform left-pattern matching. The channels are "foo", "foo.fizz", and "foo.buzz"
"foo" will publish to "foo", "foo.fizz", and "foo.buzz" In general, Classifier channels will perform left-pattern matching
Publishing to channel "foo" will allocate Classifiers "foo", "foo.fizz", and "foo.buzz"
See `GenericResponseEnvelope.outChannel` to determine how this is applied
*/ */
protected def subclassification: Subclassification[String] = protected def subclassification: Subclassification[String] = new Subclassification[Classifier] {
new Subclassification[Classifier] { def isEqual(x: Classifier, y: Classifier): Boolean = x.equals(y)
def isEqual(x: Classifier, y: Classifier): Boolean = x == y
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 = { override def publish(event: Event): Unit = {
super[SubchannelClassification].publish(event) super[SubchannelClassification].publish(event)

View file

@ -8,8 +8,8 @@ import net.psforever.types.PlanetSideGUID
* Defines a channel and a filter, both of which server the purpose of routing the message to its destination. * 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 */ /** set of subscribers on an event system bus that the envelope should reach */
def channel: String def channel: String
/** specific subscriber endpoint to be excluded (the subscriber should filter themselves) */ /** specific subscriber endpoint to be excluded (the subscriber should filter themselves upon receipt) */
def filter: PlanetSideGUID def filter: PlanetSideGUID
} }

View file

@ -7,8 +7,6 @@ import net.psforever.types.PlanetSideGUID
trait GenericMessageEnvelope trait GenericMessageEnvelope
extends AllEnvelopes { extends AllEnvelopes {
/** channel information provided with the message */
def originalChannel: String
/** input payload transported by this envelope */ /** input payload transported by this envelope */
def msg: EventMessage def msg: EventMessage
/** method that counts as "processing" the envelope by an event system; /** method that counts as "processing" the envelope by an event system;
@ -25,6 +23,6 @@ object GenericMessageEnvelope {
* @return a tuple containing the channel, filter, and reply message * @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.channel, obj.filter, obj.msg))
} }
} }

View file

@ -14,6 +14,8 @@ trait GenericResponseEnvelope
def reply: EventResponse def reply: EventResponse
/** marker indicating the routing through which the original message was processed */ /** marker indicating the routing through which the original message was processed */
def stamp: EventSystemStamp def stamp: EventSystemStamp
/** channel information tailored to the event system */
def outChannel: String = stamp.routing(channel)
} }
object GenericResponseEnvelope { object GenericResponseEnvelope {

View file

@ -14,7 +14,9 @@ case object NoReply extends EventResponse
* A stamp that represents not having been processed by an event system. * A stamp that represents not having been processed by an event system.
* Should never been given out to an event system. * Should never been given out to an event system.
*/ */
case object Undelivered extends EventSystemStamp case object Undelivered extends EventSystemStamp {
override def routing(channel: String): String = ""
}
/** /**
* The mechanics of a proper event system envelope. * The mechanics of a proper event system envelope.
@ -32,16 +34,11 @@ trait MessageTransformationBehavior
extends GenericMessageEnvelope extends GenericMessageEnvelope
with GenericResponseEnvelope { with GenericResponseEnvelope {
private var outputStamp: EventSystemStamp = Undelivered private var outputStamp: EventSystemStamp = Undelivered
private var outputChannel: String = originalChannel
private var outputReply: EventResponse = NoReply private var outputReply: EventResponse = NoReply
// satisfies GenericMessageEnvelope (and GenericResponseEnvelope)
def channel: String = outputChannel
// satisfies GenericMessageEnvelope // satisfies GenericMessageEnvelope
def response(stamp: EventSystemStamp): GenericResponseEnvelope = { def response(stamp: EventSystemStamp): GenericResponseEnvelope = {
outputStamp = stamp outputStamp = stamp
outputChannel = stamp.routing(originalChannel)
outputReply = msg.response() outputReply = msg.response()
this this
} }
@ -54,5 +51,5 @@ trait MessageTransformationBehavior
/** /**
* A proper event system envelope. * A proper event system envelope.
*/ */
case class MessageEnvelope(originalChannel: String, filter: PlanetSideGUID, msg: EventMessage) case class MessageEnvelope(channel: String, filter: PlanetSideGUID, msg: EventMessage)
extends MessageTransformationBehavior extends MessageTransformationBehavior

View file

@ -9,7 +9,7 @@ case object GalaxyStamp extends EventSystemStamp {
if (channel.trim.isEmpty) { if (channel.trim.isEmpty) {
"/out" "/out"
} else { } else {
s"/$channel/out" super.routing(channel)
} }
} }
} }

View file

@ -15,7 +15,7 @@ import scala.annotation.tailrec
import scala.concurrent.duration._ import scala.concurrent.duration._
final case class DoorMessage( final case class DoorMessage(
originalChannel: String, channel: String,
msg: IsADoorMessage, msg: IsADoorMessage,
supportMessage: Any supportMessage: Any
) extends GenericSupportEnvelope { ) extends GenericSupportEnvelope {

View file

@ -18,7 +18,7 @@ import scala.annotation.tailrec
import scala.concurrent.duration._ import scala.concurrent.duration._
final case class HackEntityEnvelope( final case class HackEntityEnvelope(
originalChannel: String, channel: String,
filter: PlanetSideGUID, filter: PlanetSideGUID,
msg: IsAHackMessage, msg: IsAHackMessage,
supportMessage: Any supportMessage: Any

View file

@ -19,7 +19,6 @@ import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.packet.game.{InventoryStateMessage, RepairMessage} import net.psforever.packet.game.{InventoryStateMessage, RepairMessage}
import net.psforever.types._ import net.psforever.types._
import org.specs2.mutable.Specification import org.specs2.mutable.Specification
import net.psforever.services.avatar.AvatarAction
import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.services.base.envelope.MessageEnvelope
import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse}
import net.psforever.services.vehicle.VehicleAction import net.psforever.services.vehicle.VehicleAction

View file

@ -17,7 +17,6 @@ import net.psforever.objects.vehicles.control.VehicleControl
import net.psforever.objects.zones.{Zone, ZoneMap} import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.packet.game.{InventoryStateMessage, RepairMessage} import net.psforever.packet.game.{InventoryStateMessage, RepairMessage}
import net.psforever.types._ import net.psforever.types._
import net.psforever.services.avatar.AvatarAction
import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.services.base.envelope.MessageEnvelope
import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse}
import net.psforever.services.vehicle.VehicleAction import net.psforever.services.vehicle.VehicleAction

View file

@ -27,7 +27,7 @@ import net.psforever.packet.game._
import net.psforever.services.ServiceManager import net.psforever.services.ServiceManager
import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.services.base.envelope.MessageEnvelope
import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.services.vehicle.VehicleAction
import net.psforever.types._ import net.psforever.types._
import scala.concurrent.duration._ import scala.concurrent.duration._

View file

@ -14,7 +14,6 @@ import net.psforever.objects.{GlobalDefinitions, Player}
import net.psforever.types.{CharacterSex, CharacterVoice, PlanetSideEmpire, PlanetSideGUID} import net.psforever.types.{CharacterSex, CharacterVoice, PlanetSideEmpire, PlanetSideGUID}
import org.specs2.mutable.Specification import org.specs2.mutable.Specification
import net.psforever.services.Service import net.psforever.services.Service
import net.psforever.services.local.LocalService
import scala.concurrent.duration._ import scala.concurrent.duration._
import akka.actor.typed.scaladsl.adapter._ import akka.actor.typed.scaladsl.adapter._

View file

@ -56,7 +56,7 @@ class EnvelopeTest extends Specification {
val input = MessageEnvelope("test", TestFilter, TestMessage(5)) val input = MessageEnvelope("test", TestFilter, TestMessage(5))
val output = input.response(TestStamp) val output = input.response(TestStamp)
output match { output match {
case reply @ GenericResponseEnvelope("/test/out", TestFilter, TestMessage(5)) => case reply @ GenericResponseEnvelope("test", TestFilter, TestMessage(5)) =>
reply.stamp mustEqual TestStamp reply.stamp mustEqual TestStamp
case _ => case _ =>
ko ko
@ -67,7 +67,7 @@ class EnvelopeTest extends Specification {
val input = MessageEnvelope("test", TestFilter, TestInputMessage(5)) val input = MessageEnvelope("test", TestFilter, TestInputMessage(5))
val output = input.response(TestStamp) val output = input.response(TestStamp)
output match { output match {
case reply @ GenericResponseEnvelope("/test/out", TestFilter, TestOutputEvent(7)) => case reply @ GenericResponseEnvelope("test", TestFilter, TestOutputEvent(7)) =>
reply.stamp mustEqual TestStamp reply.stamp mustEqual TestStamp
case _ => case _ =>
ko ko
@ -79,7 +79,7 @@ class EnvelopeTest extends Specification {
"construct (quick)" in { "construct (quick)" in {
val input = GenericResponseEnvelope(TestStamp, "test", TestFilter, TestMessage(5)) val input = GenericResponseEnvelope(TestStamp, "test", TestFilter, TestMessage(5))
input match { input match {
case reply @ GenericResponseEnvelope("/test/out", TestFilter, TestMessage(5)) => case reply @ GenericResponseEnvelope("test", TestFilter, TestMessage(5)) =>
reply.stamp mustEqual TestStamp reply.stamp mustEqual TestStamp
case _ => case _ =>
ko ko

View file

@ -14,7 +14,7 @@ import scala.concurrent.duration._
object EventServiceCacheSupportTest { object EventServiceCacheSupportTest {
final case class CachedSupportTestEnvelope( final case class CachedSupportTestEnvelope(
guid: PlanetSideGUID, guid: PlanetSideGUID,
originalChannel: String, channel: String,
override val msg: EventMessage, override val msg: EventMessage,
supportMessage: Any supportMessage: Any
) extends CachedGenericEventEnvelope with GenericSupportEnvelope { ) extends CachedGenericEventEnvelope with GenericSupportEnvelope {

View file

@ -92,7 +92,7 @@ class EventServiceTestNotSubscribed extends ActorTest {
events ! MessageEnvelope("test", Service.defaultPlayerGUID, TestMessage(5)) events ! MessageEnvelope("test", Service.defaultPlayerGUID, TestMessage(5))
val reply = probe.receiveOne(100 milliseconds) val reply = probe.receiveOne(100 milliseconds)
reply match { reply match {
case GenericResponseEnvelope("/test/out", _, TestMessage(5)) => () case GenericResponseEnvelope("test", _, TestMessage(5)) => ()
case _ => assert(false, "(2) message expected but not received") case _ => assert(false, "(2) message expected but not received")
} }
missedProbe.expectNoMessage(100 milliseconds) missedProbe.expectNoMessage(100 milliseconds)
@ -127,11 +127,11 @@ class EventServiceTestLeave extends ActorTest {
events ! MessageEnvelope("anotherTest", Service.defaultPlayerGUID, TestMessage(5)) events ! MessageEnvelope("anotherTest", Service.defaultPlayerGUID, TestMessage(5))
val reply1 = probe.receiveN(2, 100 milliseconds) val reply1 = probe.receiveN(2, 100 milliseconds)
reply1.head match { reply1.head match {
case GenericResponseEnvelope("/test/out", _, _) => () case GenericResponseEnvelope("test", _, _) => ()
case _ => assert(false, "(3) message expected but not received") case _ => assert(false, "(3) message expected but not received")
} }
reply1(1) match { reply1(1) match {
case GenericResponseEnvelope("/anotherTest/out", _, _) => () case GenericResponseEnvelope("anotherTest", _, _) => ()
case _ => assert(false, "(4) message expected but not received") case _ => assert(false, "(4) message expected but not received")
} }
@ -140,7 +140,7 @@ class EventServiceTestLeave extends ActorTest {
events ! MessageEnvelope("anotherTest", Service.defaultPlayerGUID, TestMessage(6)) events ! MessageEnvelope("anotherTest", Service.defaultPlayerGUID, TestMessage(6))
val reply2 = probe.receiveOne(100 milliseconds) val reply2 = probe.receiveOne(100 milliseconds)
reply2 match { reply2 match {
case GenericResponseEnvelope("/test/out", _, TestMessage(5)) => () case GenericResponseEnvelope("test", _, TestMessage(5)) => ()
case _ => assert(false, "(5) message expected but not received") case _ => assert(false, "(5) message expected but not received")
} }
probe.expectNoMessage(250 milliseconds) probe.expectNoMessage(250 milliseconds)
@ -161,11 +161,11 @@ class EventServiceTestLeaveAll extends ActorTest {
events ! MessageEnvelope("anotherTest", Service.defaultPlayerGUID, TestMessage(6)) events ! MessageEnvelope("anotherTest", Service.defaultPlayerGUID, TestMessage(6))
val reply = probe.receiveN(2,100 milliseconds) val reply = probe.receiveN(2,100 milliseconds)
reply.head match { reply.head match {
case GenericResponseEnvelope("/test/out", _, TestMessage(5)) => () case GenericResponseEnvelope("test", _, TestMessage(5)) => ()
case _ => assert(false, "(6) message expected but not received") case _ => assert(false, "(6) message expected but not received")
} }
reply(1) match { reply(1) match {
case GenericResponseEnvelope("/anotherTest/out", _, TestMessage(6)) => () case GenericResponseEnvelope("anotherTest", _, TestMessage(6)) => ()
case _ => assert(false, "(7) message expected but not received") case _ => assert(false, "(7) message expected but not received")
} }

View file

@ -29,7 +29,7 @@ object EventServiceTestBase {
} }
final case class TestSupportEnvelope( final case class TestSupportEnvelope(
originalChannel: String, channel: String,
msg: SupportActorRepliesWith msg: SupportActorRepliesWith
) extends GenericSupportEnvelope { ) extends GenericSupportEnvelope {
def filter: PlanetSideGUID = Service.defaultPlayerGUID def filter: PlanetSideGUID = Service.defaultPlayerGUID