introducing generic event message services; implementing GalaxyService using the generic framework

This commit is contained in:
Fate-JH 2026-01-20 18:24:13 -05:00
parent 83482a7207
commit 406e89ea50
33 changed files with 309 additions and 178 deletions

View file

@ -3,9 +3,10 @@ package net.psforever.actors.session.normal
import akka.actor.{ActorContext, ActorRef, typed}
import net.psforever.actors.session.AvatarActor
import net.psforever.actors.session.support.{GalaxyHandlerFunctions, SessionGalaxyHandlers, SessionData}
import net.psforever.actors.session.support.{GalaxyHandlerFunctions, SessionData, SessionGalaxyHandlers}
import net.psforever.packet.game.{BroadcastWarpgateUpdateMessage, FriendsResponse, HotSpotUpdateMessage, ZoneInfoMessage, ZonePopulationUpdateMessage, HotSpotInfo => PacketHotSpotInfo}
import net.psforever.services.galaxy.{GalaxyAction, GalaxyResponse, GalaxyServiceMessage}
import net.psforever.services.base.EventResponse
import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage}
import net.psforever.types.{MemberAction, PlanetSideEmpire}
object GalaxyHandlerLogic {
@ -32,9 +33,9 @@ class GalaxyHandlerLogic(val ops: SessionGalaxyHandlers, implicit val context: A
/* response handlers */
def handle(reply: GalaxyResponse.Response): Unit = {
def handle(reply: EventResponse): Unit = {
reply match {
case GalaxyResponse.HotSpotUpdate(zone_index, priority, hot_spot_info) =>
case GalaxyAction.HotSpotUpdate(zone_index, priority, hot_spot_info) =>
sendResponse(
HotSpotUpdateMessage(
zone_index,
@ -43,7 +44,7 @@ class GalaxyHandlerLogic(val ops: SessionGalaxyHandlers, implicit val context: A
)
)
case GalaxyResponse.MapUpdate(msg) =>
case GalaxyAction.MapUpdate(msg) =>
sendResponse(msg)
import net.psforever.actors.zone.ZoneActor
import net.psforever.zones.Zones
@ -53,7 +54,7 @@ class GalaxyHandlerLogic(val ops: SessionGalaxyHandlers, implicit val context: A
case None =>
}
case GalaxyResponse.UpdateBroadcastPrivileges(zoneId, gateMapId, fromFactions, toFactions) =>
case GalaxyAction.UpdateBroadcastPrivileges(zoneId, gateMapId, fromFactions, toFactions) =>
val faction = player.Faction
val from = fromFactions.contains(faction)
val to = toFactions.contains(faction)
@ -63,16 +64,16 @@ class GalaxyHandlerLogic(val ops: SessionGalaxyHandlers, implicit val context: A
sendResponse(BroadcastWarpgateUpdateMessage(zoneId, gateMapId, faction))
}
case GalaxyResponse.FlagMapUpdate(msg) =>
case GalaxyAction.FlagMapUpdate(msg) =>
sendResponse(msg)
case GalaxyResponse.TransferPassenger(temp_channel, vehicle, _, manifest) =>
case GalaxyAction.TransferPassenger(_, temp_channel, vehicle, _, manifest) =>
sessionLogic.zoning.handleTransferPassenger(temp_channel, vehicle, manifest)
case GalaxyResponse.LockedZoneUpdate(zone, time) =>
case GalaxyAction.LockedZoneUpdate(zone, time) =>
sendResponse(ZoneInfoMessage(zone.Number, empire_status=false, lock_time=time))
case GalaxyResponse.UnlockedZoneUpdate(zone) =>
case GalaxyAction.UnlockedZoneUpdate(zone) =>
sendResponse(ZoneInfoMessage(zone.Number, empire_status=true, lock_time=0L))
val popBO = 0
val pop = zone.LivePlayers.distinctBy(_.CharId)
@ -81,10 +82,10 @@ class GalaxyHandlerLogic(val ops: SessionGalaxyHandlers, implicit val context: A
val popVS = pop.count(_.Faction == PlanetSideEmpire.VS)
sendResponse(ZonePopulationUpdateMessage(zone.Number, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO))
case GalaxyResponse.LogStatusChange(name) if avatar.people.friend.exists(_.name.equals(name)) =>
case GalaxyAction.LogStatusChange(name) if avatar.people.friend.exists(_.name.equals(name)) =>
avatarActor ! AvatarActor.MemberListRequest(MemberAction.UpdateFriend, name)
case GalaxyResponse.SendResponse(msg) =>
case GalaxyAction.SendResponse(msg) =>
sendResponse(msg)
case _ => ()

View file

@ -3,16 +3,18 @@ package net.psforever.actors.session.support
import akka.actor.{ActorContext, ActorRef, typed}
import net.psforever.packet.game.FriendsResponse
import net.psforever.services.base.EventResponse
import scala.annotation.unused
//
import net.psforever.actors.session.AvatarActor
import net.psforever.services.galaxy.GalaxyResponse
trait GalaxyHandlerFunctions extends CommonSessionInterfacingFunctionality {
def ops: SessionGalaxyHandlers
def handleUpdateIgnoredPlayers(pkt: FriendsResponse): Unit
def handle(reply: GalaxyResponse.Response): Unit
def handle(@unused reply: EventResponse): Unit
}
class SessionGalaxyHandlers(

View file

@ -602,7 +602,7 @@ class ZoningOperations(
sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list
spawn.initializeFriendsAndIgnoredLists()
//the following subscriptions last until character switch/logout
galaxyService ! Service.Join("galaxy") //for galaxy-wide messages
galaxyService ! Service.Join("") //for galaxy-wide messages
galaxyService ! Service.Join(s"${avatar.faction}") //for hotspots, etc.
sessionLogic.squadService ! Service.Join(s"${avatar.faction}") //channel will be player.Faction
sessionLogic.squadService ! Service.Join(s"${avatar.id}") //channel will be player.CharId (in order to work with packets)
@ -1470,10 +1470,7 @@ class ZoningOperations(
case Some(manifest) =>
val toChannel = manifest.file
val topLevel = interstellarFerryTopLevelGUID.getOrElse(vehicle.GUID)
galaxyService ! GalaxyServiceMessage(
toChannel,
GalaxyAction.TransferPassenger(player_guid, toChannel, vehicle, topLevel, manifest)
)
galaxyService ! GalaxyServiceMessage(toChannel, GalaxyAction.TransferPassenger(player_guid, toChannel, vehicle, topLevel, manifest))
vehicle.CargoHolds.values
.collect {
case hold if hold.isOccupied =>

View file

@ -5,6 +5,8 @@ import akka.actor.{Actor, ActorRef, Cancellable, Props}
import net.psforever.objects.Default
import net.psforever.types.{PlanetSideEmpire, Vector3}
import net.psforever.services.ServiceManager
import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage}
import scala.collection.mutable.ListBuffer
import scala.concurrent.duration._
@ -303,12 +305,11 @@ class ZoneHotSpotProjector(zone: Zone, hotspots: ListBuffer[HotSpotInfo], blanki
val zoneNumber = zone.Number
val hotSpotInfoList = hotSpotInfos.toList
affectedFactions.foreach(faction =>
galaxy ! Zone.HotSpot.Update(
faction,
zoneNumber,
1,
ZoneHotSpotProjector.SpecificHotSpotInfo(faction, hotSpotInfoList)
)
galaxy ! GalaxyServiceMessage(faction.toString, GalaxyAction.HotSpotUpdate(
zoneNumber,
1,
ZoneHotSpotProjector.SpecificHotSpotInfo(faction, hotSpotInfoList)
))
)
}
}

View file

@ -12,7 +12,7 @@ import net.psforever.objects.Default
import net.psforever.objects.serverobject.structures.{Building, WarpGate}
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.ChatMsg
import net.psforever.services.galaxy.{GalaxyAction, GalaxyResponse, GalaxyServiceMessage, GalaxyServiceResponse}
import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage, GalaxyServiceResponse}
import net.psforever.types.ChatMessageType
import net.psforever.util.Config
import net.psforever.zones.Zones
@ -557,20 +557,14 @@ class CavernRotationService(
val (lockedZones, unlockedZones) = managedZones.partition(_.locked)
//borrow GalaxyService response structure, but send to the specific endpoint math.max(0, monitor.start + monitor.duration - curr)
unlockedZones.foreach { monitor =>
sendToSession ! GalaxyServiceResponse("", GalaxyResponse.UnlockedZoneUpdate(monitor.zone))
sendToSession ! GalaxyServiceResponse("", GalaxyAction.UnlockedZoneUpdate(monitor.zone))
}
val sortedLocked = lockedZones.sortBy(z => z.start)
sortedLocked.take(2).foreach { monitor =>
sendToSession ! GalaxyServiceResponse(
"",
GalaxyResponse.LockedZoneUpdate(monitor.zone, math.max(0, monitor.start + monitor.duration - curr))
)
sendToSession ! GalaxyServiceResponse("", GalaxyAction.LockedZoneUpdate(monitor.zone, math.max(0, monitor.start + monitor.duration - curr)))
}
sortedLocked.takeRight(2).foreach { monitor =>
sendToSession ! GalaxyServiceResponse(
"",
GalaxyResponse.LockedZoneUpdate(monitor.zone, 0L)
)
sendToSession ! GalaxyServiceResponse("", GalaxyAction.LockedZoneUpdate(monitor.zone, 0L))
}
}
@ -654,8 +648,8 @@ class CavernRotationService(
val unlocking = managedZones(nextToUnlock)
val lockingZone = locking.zone
val unlockingZone = unlocking.zone
val fullHoursBetweenRotationsAsHours = timeToCompleteAllRotationsHours.hours
val fullHoursBetweenRotationsAsMillis = fullHoursBetweenRotationsAsHours.toMillis
//val fullHoursBetweenRotationsAsHours = timeToCompleteAllRotationsHours.hours
//val fullHoursBetweenRotationsAsMillis = fullHoursBetweenRotationsAsHours.toMillis
val hoursBetweenRotationsAsHours = timeBetweenRotationsHours.hours
val prevToLock = nextToLock
nextToLock = (nextToLock + 1) % managedZones.size
@ -729,7 +723,7 @@ class CavernRotationService(
advanceTimeBy: FiniteDuration,
galaxyService: ActorRef
) : Unit = {
val curr = System.currentTimeMillis()
//val curr = System.currentTimeMillis()
val advanceByTimeAsMillis = advanceTimeBy.toMillis
managedZones.foreach { zone =>
zone.start = zone.start - advanceByTimeAsMillis

View file

@ -5,8 +5,8 @@ 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.services.base.support.{SimilarityComparator, SupportActor, SupportActorCaseConversions}
import net.psforever.types.Vector3
import net.psforever.services.support.{SimilarityComparator, SupportActor, SupportActorCaseConversions}
import scala.concurrent.Future
import scala.concurrent.duration._

View file

@ -1,8 +1,6 @@
// Copyright (c) 2017 PSForever
package net.psforever.services
import akka.event.{ActorEventBus, SubchannelClassification}
import akka.util.Subclassification
import net.psforever.types.PlanetSideGUID
object Service {
@ -14,24 +12,3 @@ object Service {
final case class Leave(channel: Option[String] = None)
final case class LeaveAll()
}
trait GenericEventBusMsg {
def channel: String
}
class GenericEventBus[A <: GenericEventBusMsg] extends ActorEventBus with SubchannelClassification {
type Event = A
type Classifier = String
protected def classify(event: Event): Classifier = event.channel
protected def subclassification =
new Subclassification[Classifier] {
def isEqual(x: Classifier, y: Classifier) = x == y
def isSubclass(x: Classifier, y: Classifier) = x.startsWith(y)
}
protected def publish(event: Event, subscriber: Subscriber): Unit = {
subscriber ! event
}
}

View file

@ -7,7 +7,8 @@ import net.psforever.packet.game.ObjectCreateMessage
import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectCreateMessageParent, PlacementData}
import net.psforever.types.PlanetSideGUID
import net.psforever.services.avatar.support.{CorpseRemovalActor, DroppedItemRemover}
import net.psforever.services.{GenericEventBus, RemoverActor, Service}
import net.psforever.services.base.GenericEventBus
import net.psforever.services.{RemoverActor, Service}
class AvatarService(zone: Zone) extends Actor {
private val undertaker: ActorRef = context.actorOf(Props[CorpseRemovalActor](), s"${zone.id}-corpse-removal-agent")

View file

@ -13,6 +13,7 @@ import net.psforever.objects.zones.Zone
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.ImplantAction
import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectCreateMessageParent}
import net.psforever.services.base.{EventMessage, EventResponse}
import net.psforever.types.{ExoSuitType, ExperienceType, PlanetSideEmpire, PlanetSideGUID, TransactionType, Vector3}
import scala.concurrent.duration.FiniteDuration
@ -25,7 +26,9 @@ object AvatarServiceMessage {
}
object AvatarAction {
sealed trait Action
sealed trait Action extends EventMessage {
def response(): EventResponse = null
}
final case class ArmorChanged(player_guid: PlanetSideGUID, suit: ExoSuitType.Value, subtype: Int) extends Action
final case class AvatarImplant(player_guid: PlanetSideGUID, action: ImplantAction.Value, implantSlot: Int, status: Int) extends Action

View file

@ -13,7 +13,7 @@ import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.objectcreate.ConstructorData
import net.psforever.packet.game.{ImplantAction, ObjectCreateMessage}
import net.psforever.types.{ExoSuitType, ExperienceType, PlanetSideEmpire, PlanetSideGUID, TransactionType, Vector3}
import net.psforever.services.GenericEventBusMsg
import net.psforever.services.base.{EventResponse, GenericEventBusMsg}
final case class AvatarServiceResponse(
channel: String,
@ -22,7 +22,7 @@ final case class AvatarServiceResponse(
) extends GenericEventBusMsg
object AvatarResponse {
sealed trait Response
sealed trait Response extends EventResponse
final case class ArmorChanged(suit: ExoSuitType.Value, subtype: Int) extends Response
final case class AvatarImplant(action: ImplantAction.Value, implantSlot: Int, status: Int) extends Response

View file

@ -0,0 +1,16 @@
// Copyright (c) 2026 PSForever
package net.psforever.services.base
trait EventExchange
trait EventResponse extends EventExchange
trait EventMessage extends EventExchange {
def response(): EventResponse
}
trait SelfResponseMessage
extends EventMessage
with EventResponse {
def response(): EventResponse = this
}

View file

@ -0,0 +1,27 @@
// Copyright (c) 2017 PSForever
package net.psforever.services.base
import akka.event.{ActorEventBus, SubchannelClassification}
import akka.util.Subclassification
trait GenericEventBusMsg {
def channel: String
}
class GenericEventBus[A <: GenericEventBusMsg] extends ActorEventBus with SubchannelClassification {
type Event = A
type Classifier = String
protected def classify(event: Event): Classifier = event.channel
protected def subclassification: Subclassification[String] =
new Subclassification[Classifier] {
def isEqual(x: Classifier, y: Classifier): Boolean = x == y
def isSubclass(x: Classifier, y: Classifier): Boolean = x.startsWith(y)
}
protected def publish(event: Event, subscriber: Subscriber): Unit = {
subscriber ! event
}
}

View file

@ -0,0 +1,72 @@
// Copyright (c) 2026 PSForever
package net.psforever.services.base
import akka.actor.Actor
import net.psforever.services.Service
import net.psforever.types.PlanetSideGUID
import org.log4s.Logger
trait GenericResponseEnvelope
extends GenericEventBusMsg {
def exclude: PlanetSideGUID
def reply: EventResponse
}
trait GenericMessageEnvelope {
def channel: String
def exclude: PlanetSideGUID
def msg: EventMessage
def response(outChannel: String): GenericResponseEnvelope
}
abstract class GenericEventService[OUT <: GenericResponseEnvelope](busName: String)
extends Actor {
protected lazy val log: Logger = org.log4s.getLogger(getClass.getSimpleName)
protected val eventBus = new GenericEventBus[OUT]
def commonJoinBehavior: Receive = {
case Service.Join(channel) =>
val path = formatChannelOnBusName(channel, busName)
val who = sender()
eventBus.subscribe(who, path)
}
def commonLeaveBehavior: Receive = {
case Service.Leave(None) =>
eventBus.unsubscribe(sender())
case Service.Leave(Some(channel)) =>
val path = formatChannelOnBusName(channel, busName)
eventBus.unsubscribe(sender(), path)
case Service.LeaveAll() =>
eventBus.unsubscribe(sender())
}
def receive: Receive =
commonJoinBehavior.orElse(commonLeaveBehavior)
.orElse {
case msg: GenericMessageEnvelope =>
compose(msg)
case msg => ()
log.warn(s"Unhandled message $msg from ${sender()}")
}
protected def compose(msg: GenericMessageEnvelope): Unit = {
eventBus.publish(msg.response(formatChannelOnBusName(msg.channel, busName)).asInstanceOf[OUT])
}
def formatChannelOnBusName: (String, String) => String = GenericEventService.BusOnChannelFormat
}
object GenericEventService {
final def BusOnChannelFormat(channel: String, busName: String): String = {
if (channel.trim.isEmpty) {
s"/$busName"
} else {
s"/$channel/$busName"
}
}
}

View file

@ -0,0 +1,51 @@
// Copyright (c) 2026 PSForever
package net.psforever.services.base
import akka.actor.{ActorContext, ActorRef}
import scala.annotation.unused
trait EventServiceSupport {
def label: String
def constructor(@unused context: ActorContext): ActorRef
}
trait GenericMessageToSupportEnvelope
extends GenericMessageEnvelope {
def toSupport: String
def response(outChannel: String): GenericResponseEnvelope = null
}
abstract class GenericEventServiceWithSupport[OUT <: GenericResponseEnvelope](
busName: String,
eventSupportServices: List[EventServiceSupport]
)
extends GenericEventService[OUT](busName) {
private val supportServices: Map[String, ActorRef] =
eventSupportServices
.map { supportService => (supportService.label, supportService.constructor(context)) }
.toMap[String, ActorRef]
private def supportReceive: Receive = {
case msg: GenericMessageToSupportEnvelope =>
forwardToSupport(msg)
}
override def receive: Receive = supportReceive.orElse(super.receive)
private def forwardToSupport(msg: GenericMessageToSupportEnvelope): Unit = {
supportServices
.get(msg.toSupport)
.map { support =>
support.forward(msg)
msg
}
.getOrElse {
log.error(s"support service ${msg.toSupport} was not found - check message routing or service params")
}
if (msg.response(outChannel = "") != null) {
compose(msg)
}
}
}

View file

@ -1,5 +1,5 @@
// Copyright (c) 2017 PSForever
package net.psforever.services.support
package net.psforever.services.base.support
abstract class SimilarityComparator[A <: SupportActor.Entry] {

View file

@ -1,5 +1,5 @@
// Copyright (c) 2017 PSForever
package net.psforever.services.support
package net.psforever.services.base.support
import akka.actor.Actor
import net.psforever.objects.PlanetSideGameObject

View file

@ -1,5 +1,5 @@
// Copyright (c) 2017 PSForever
package net.psforever.services.support
package net.psforever.services.base.support
import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.zones.Zone

View file

@ -6,6 +6,7 @@ import akka.actor.typed.{ActorRef, Behavior}
import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors}
import net.psforever.objects.{Session, SessionSource}
import net.psforever.packet.game.ChatMsg
import net.psforever.services.base.{EventMessage, EventResponse}
import net.psforever.types.{ChatMessageType, PlanetSideEmpire}
object ChatService {
@ -17,14 +18,16 @@ object ChatService {
new ChatService(context)
}
sealed trait Command
sealed trait Command extends EventMessage {
def response(): EventResponse = null
}
final case class JoinChannel(actor: ActorRef[MessageResponse], sessionSource: SessionSource, channel: ChatChannel) extends Command
final case class LeaveChannel(actor: ActorRef[MessageResponse], channel: ChatChannel) extends Command
final case class LeaveAllChannels(actor: ActorRef[MessageResponse]) extends Command
final case class Message(session: Session, message: ChatMsg, channel: ChatChannel) extends Command
final case class MessageResponse(session: Session, message: ChatMsg, channel: ChatChannel)
final case class MessageResponse(session: Session, message: ChatMsg, channel: ChatChannel) extends EventResponse
}
class ChatService(context: ActorContext[ChatService.Command]) extends AbstractBehavior[ChatService.Command](context) {

View file

@ -2,26 +2,25 @@
package net.psforever.services.galaxy
import akka.actor.Actor
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.BuildingInfoUpdateMessage
import net.psforever.services.{GenericEventBus, Service}
import net.psforever.services.base.GenericEventBus
import net.psforever.services.Service
class GalaxyService extends Actor {
private[this] val log = org.log4s.getLogger
val GalaxyEvents = new GenericEventBus[GalaxyServiceResponse]
private def correctChannelName(channel: String): String = {
if (channel.isEmpty) {
s"/Galaxy"
} else {
s"/$channel/Galaxy"
}
}
def receive: Receive = {
case Service.Join(faction) if "TRNCVS".containsSlice(faction) =>
val path = s"/$faction/Galaxy"
GalaxyEvents.subscribe(sender(), path)
case Service.Join("galaxy") =>
val path = s"/Galaxy"
GalaxyEvents.subscribe(sender(), path)
case Service.Join(channel) =>
val path = s"/$channel/Galaxy"
val path = correctChannelName(channel)
GalaxyEvents.subscribe(sender(), path)
case Service.Leave(None) =>
@ -34,72 +33,8 @@ class GalaxyService extends Actor {
case Service.LeaveAll() =>
GalaxyEvents.unsubscribe(sender())
case GalaxyServiceMessage(forChannel, action) =>
action match {
case GalaxyAction.MapUpdate(msg: BuildingInfoUpdateMessage) =>
GalaxyEvents.publish(
GalaxyServiceResponse(s"/Galaxy", GalaxyResponse.MapUpdate(msg))
)
case GalaxyAction.UpdateBroadcastPrivileges(zoneId, gateMapId, fromFactions, toFactions) =>
GalaxyEvents.publish(
GalaxyServiceResponse(
s"/$forChannel/Galaxy",
GalaxyResponse.UpdateBroadcastPrivileges(zoneId, gateMapId, fromFactions, toFactions)
)
)
case GalaxyAction.FlagMapUpdate(msg) =>
GalaxyEvents.publish(
GalaxyServiceResponse(s"/Galaxy", GalaxyResponse.FlagMapUpdate(msg))
)
case GalaxyAction.TransferPassenger(_, temp_channel, vehicle, vehicle_to_delete, manifest) =>
GalaxyEvents.publish(
GalaxyServiceResponse(
s"/$forChannel/Galaxy",
GalaxyResponse.TransferPassenger(temp_channel, vehicle, vehicle_to_delete, manifest)
)
)
case GalaxyAction.LockedZoneUpdate(zone, time) =>
GalaxyEvents.publish(
GalaxyServiceResponse(
s"/Galaxy",
GalaxyResponse.LockedZoneUpdate(zone, time)
)
)
case GalaxyAction.UnlockedZoneUpdate(zone) =>
GalaxyEvents.publish(
GalaxyServiceResponse(
s"/Galaxy",
GalaxyResponse.UnlockedZoneUpdate(zone)
)
)
case GalaxyAction.LogStatusChange(name) =>
GalaxyEvents.publish(
GalaxyServiceResponse(
s"/Galaxy",
GalaxyResponse.LogStatusChange(name)
)
)
case GalaxyAction.SendResponse(msg) =>
GalaxyEvents.publish(
GalaxyServiceResponse(
s"/Galaxy",
GalaxyResponse.SendResponse(msg)
)
)
case _ => ;
}
case Zone.HotSpot.Update(faction, zone_num, priority, info) =>
GalaxyEvents.publish(
GalaxyServiceResponse(s"/$faction/Galaxy", GalaxyResponse.HotSpotUpdate(zone_num, priority, info))
)
case msg @ GalaxyServiceMessage(forChannel, _) =>
GalaxyEvents.publish(msg.response(correctChannelName(forChannel)))
case msg =>
log.warn(s"Unhandled message $msg from ${sender()}")

View file

@ -3,22 +3,43 @@ package net.psforever.services.galaxy
import net.psforever.objects.Vehicle
import net.psforever.objects.vehicles.VehicleManifest
import net.psforever.objects.zones.Zone
import net.psforever.objects.zones.{HotSpotInfo, Zone}
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.{BuildingInfoUpdateMessage, CaptureFlagUpdateMessage}
import net.psforever.services.Service
import net.psforever.services.base.{EventMessage, EventResponse, GenericMessageEnvelope, SelfResponseMessage}
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
final case class GalaxyServiceMessage(forChannel: String, actionMessage: GalaxyAction.Action)
final case class GalaxyServiceMessage(channel: String, msg: EventMessage)
extends GenericMessageEnvelope {
def exclude: PlanetSideGUID = Service.defaultPlayerGUID
def response(outChannel: String): GalaxyServiceResponse = {
GalaxyServiceResponse(outChannel, msg.response())
}
}
object GalaxyServiceMessage {
def apply(actionMessage: GalaxyAction.Action): GalaxyServiceMessage = GalaxyServiceMessage("", actionMessage)
}
object GalaxyAction {
trait Action
trait Action extends EventMessage
trait Response extends EventResponse
final case class HotSpotUpdate(zone_id: Int, priority: Int, host_spot_info: List[HotSpotInfo])
extends Action
with Response
with SelfResponseMessage
final case class MapUpdate(msg: BuildingInfoUpdateMessage)
extends Action
with Response
with SelfResponseMessage
final case class MapUpdate(msg: BuildingInfoUpdateMessage) extends Action
final case class FlagMapUpdate(msg: CaptureFlagUpdateMessage) extends Action
with Response
with SelfResponseMessage
final case class TransferPassenger(
player_guid: PlanetSideGUID,
@ -27,6 +48,8 @@ object GalaxyAction {
vehicle_to_delete: PlanetSideGUID,
manifest: VehicleManifest
) extends Action
with Response
with SelfResponseMessage
final case class UpdateBroadcastPrivileges(
zoneId: Int,
@ -34,12 +57,22 @@ object GalaxyAction {
fromFactions: Set[PlanetSideEmpire.Value],
toFactions: Set[PlanetSideEmpire.Value]
) extends Action
with Response
with SelfResponseMessage
final case class LockedZoneUpdate(zone: Zone, timeUntilUnlock: Long) extends Action
with Response
with SelfResponseMessage
final case class UnlockedZoneUpdate(zone: Zone) extends Action
with Response
with SelfResponseMessage
final case class LogStatusChange(name: String) extends Action
with Response
with SelfResponseMessage
final case class SendResponse(msg: PlanetSideGamePacket) extends Action
with Response
with SelfResponseMessage
}

View file

@ -7,13 +7,16 @@ import net.psforever.objects.zones.{HotSpotInfo, Zone}
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.{BuildingInfoUpdateMessage, CaptureFlagUpdateMessage}
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
import net.psforever.services.GenericEventBusMsg
import net.psforever.services.Service
import net.psforever.services.base.{EventResponse, GenericResponseEnvelope}
final case class GalaxyServiceResponse(channel: String, replyMessage: GalaxyResponse.Response)
extends GenericEventBusMsg
final case class GalaxyServiceResponse(channel: String, reply: EventResponse)
extends GenericResponseEnvelope {
def exclude: PlanetSideGUID = Service.defaultPlayerGUID
}
object GalaxyResponse {
trait Response
trait Response extends EventResponse
final case class HotSpotUpdate(zone_id: Int, priority: Int, host_spot_info: List[HotSpotInfo]) extends Response
final case class MapUpdate(msg: BuildingInfoUpdateMessage) extends Response

View file

@ -4,8 +4,8 @@ package net.psforever.services.hart
import akka.actor.{Actor, ActorRef, Cancellable}
import net.psforever.objects.Default
import net.psforever.objects.zones.Zone
import net.psforever.services.base.{EventResponse, GenericEventBus, GenericEventBusMsg}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.services.{GenericEventBus, GenericEventBusMsg}
import net.psforever.types.{HartSequence, PlanetSideGUID}
import scala.concurrent.duration._
@ -20,7 +20,7 @@ import scala.concurrent.ExecutionContext.Implicits.global
*/
class HartTimer(zone: Zone) extends Actor {
/** since the system is zone-locked, caching this value is fine */
val zoneId = zone.id
val zoneId: String = zone.id
/** all of the paired HART facility amenities and the shuttle housed in that facility (in that order) */
var padAndShuttlePairs: List[(PlanetSideGUID, PlanetSideGUID)] = List()
@ -45,8 +45,8 @@ class HartTimer(zone: Zone) extends Actor {
/** a message bus to which all associated orbital shuttle pads are subscribed */
val padEvents = new GenericEventBus[HartTimer.Command]
/** cache common messages */
val shuttleDockedInThisZone = HartTimer.ShuttleDocked(zoneId)
val shuttleFreeFromDockInThisZone = HartTimer.ShuttleFreeFromDock(zoneId)
val shuttleDockedInThisZone: HartTimer.ShuttleDocked = HartTimer.ShuttleDocked(zoneId)
val shuttleFreeFromDockInThisZone: HartTimer.ShuttleFreeFromDock = HartTimer.ShuttleFreeFromDock(zoneId)
/** the behaviors common to both the inert and active operations of the hart */
val commonBehavior: Receive = {
@ -259,7 +259,7 @@ object HartTimer {
* to relay instructions back to the individual facility amenity portions of this HART system.
* The channel is blank because it does not need special designation.
*/
trait Command extends GenericEventBusMsg { def channel: String = "" }
trait Command extends EventResponse with GenericEventBusMsg { def channel: String = "" }
/**
* Forbid entry through the boartding gantry doors.
*/

View file

@ -8,8 +8,9 @@ import net.psforever.packet.game.{ObjectCreateMessage, TriggeredEffect, Triggere
import net.psforever.services.local.support.CaptureFlagManager
import net.psforever.types.PlanetSideGUID
import net.psforever.services.local.support._
import net.psforever.services.{GenericEventBus, Service}
import net.psforever.services.support.SupportActor
import net.psforever.services.Service
import net.psforever.services.base.GenericEventBus
import net.psforever.services.base.support.SupportActor
class LocalService(zone: Zone) extends Actor {
private val doorCloser = context.actorOf(

View file

@ -15,6 +15,7 @@ import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.GenericObjectActionEnum.GenericObjectActionEnum
import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
import net.psforever.packet.game.{ChatMsg, DeployableInfo, DeploymentAction, GenericAction, HackState7, TriggeredSound}
import net.psforever.services.base.{EventMessage, EventResponse}
import net.psforever.services.hart.HartTimer.OrbitalShuttleEvent
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
@ -25,7 +26,9 @@ object LocalServiceMessage {
}
object LocalAction {
trait Action
trait Action extends EventMessage {
def response(): EventResponse = null
}
final case class DeployItem(item: Deployable) extends Action
final case class DeployableMapIcon(

View file

@ -12,7 +12,7 @@ import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game._
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
import net.psforever.services.GenericEventBusMsg
import net.psforever.services.base.{EventResponse, GenericEventBusMsg}
import net.psforever.services.hart.HartTimer.OrbitalShuttleEvent
final case class LocalServiceResponse(
@ -22,7 +22,7 @@ final case class LocalServiceResponse(
) extends GenericEventBusMsg
object LocalResponse {
trait Response
trait Response extends EventResponse
final case class DeployableMapIcon(action: DeploymentAction.Value, deployInfo: DeployableInfo) extends Response
final case class DeployableUIFor(obj: DeployedItem.Value) extends Response

View file

@ -4,7 +4,9 @@ import akka.actor.Actor
import net.psforever.packet.game.{GamePropertyTarget, PropertyOverrideMessage}
import net.psforever.packet.game.PropertyOverrideMessage.GamePropertyScope
import net.psforever.packet.game.objectcreate.ObjectClass
import net.psforever.services.base.{EventMessage, EventResponse}
import net.psforever.zones.Zones
import scala.collection.mutable.ListBuffer
class PropertyOverrideManager extends Actor {
@ -87,5 +89,7 @@ class PropertyOverrideManager extends Actor {
}
object PropertyOverrideManager {
final case object GetOverridesMessage
final case object GetOverridesMessage extends EventMessage {
def response(): EventResponse = null
}
}

View file

@ -5,6 +5,7 @@ import net.psforever.objects.Player
import net.psforever.objects.avatar.Certification
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.{WaypointEventAction, WaypointInfo, SquadAction => PacketSquadAction}
import net.psforever.services.base.{EventMessage, EventResponse}
import net.psforever.types.{PlanetSideGUID, SquadRequestType, SquadWaypoint, Vector3}
final case class SquadServiceMessage(tplayer: Player, zone: Zone, actionMessage: Any)
@ -15,7 +16,9 @@ object SquadServiceMessage {
}
object SquadAction {
sealed trait Action
sealed trait Action extends EventMessage {
def response(): EventResponse = null
}
final case class InitSquadList() extends Action
final case class InitCharId() extends Action

View file

@ -6,10 +6,10 @@ import net.psforever.objects.avatar.Certification
import net.psforever.objects.teamwork.Squad
import net.psforever.packet.game.{SquadDetail, SquadInfo, WaypointEventAction, WaypointInfo}
import net.psforever.types.{ChatMessageType, PlanetSideGUID, SquadResponseType, SquadWaypoint}
import net.psforever.services.GenericEventBusMsg
import net.psforever.services.base.{EventResponse, GenericEventBusMsg}
final case class SquadServiceResponse(channel: String, exclude: Iterable[Long], response: SquadResponse.Response)
extends GenericEventBusMsg
extends EventResponse with GenericEventBusMsg
object SquadServiceResponse {
def apply(toChannel: String, response: SquadResponse.Response): SquadServiceResponse =

View file

@ -2,11 +2,11 @@
package net.psforever.services.teamwork
import akka.actor.ActorRef
import scala.collection.mutable
import scala.collection.mutable
import net.psforever.objects.teamwork.{Squad, SquadFeatures}
import net.psforever.packet.game.SquadDetail
import net.psforever.services.GenericEventBus
import net.psforever.services.base.GenericEventBus
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
class SquadSubscriptionEntity {

View file

@ -6,9 +6,10 @@ import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.ObjectCreateMessage
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
import net.psforever.services.base.GenericEventBus
import net.psforever.services.vehicle.support.TurretUpgrader
import net.psforever.types.DriveState
import net.psforever.services.{GenericEventBus, Service}
import net.psforever.services.Service
class VehicleService(zone: Zone) extends Actor {
private val turretUpgrade: ActorRef = context.actorOf(Props[TurretUpgrader](), s"${zone.id}-turret-upgrade-agent")

View file

@ -7,6 +7,7 @@ import net.psforever.objects.inventory.InventoryItem
import net.psforever.objects.zones.Zone
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.objectcreate.ConstructorData
import net.psforever.services.base.{EventMessage, EventResponse}
import net.psforever.types.{BailType, DriveState, PlanetSideGUID, Vector3}
final case class VehicleServiceMessage(forChannel: String, actionMessage: VehicleAction.Action)
@ -21,7 +22,9 @@ object VehicleServiceMessage {
}
object VehicleAction {
trait Action
trait Action extends EventMessage {
def response(): EventResponse = null
}
final case class ChangeAmmo(
player_guid: PlanetSideGUID,

View file

@ -11,7 +11,7 @@ import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.objectcreate.ConstructorData
import net.psforever.packet.game.ObjectCreateMessage
import net.psforever.types.{BailType, DriveState, PlanetSideGUID, Vector3}
import net.psforever.services.GenericEventBusMsg
import net.psforever.services.base.{EventResponse, GenericEventBusMsg}
final case class VehicleServiceResponse(
channel: String,
@ -20,7 +20,7 @@ final case class VehicleServiceResponse(
) extends GenericEventBusMsg
object VehicleResponse {
trait Response
trait Response extends EventResponse
final case class ChangeAmmo(
weapon_guid: PlanetSideGUID,

View file

@ -10,7 +10,7 @@ import net.psforever.objects.serverobject.turret.{FacilityTurret, TurretUpgrade,
import net.psforever.objects.vehicles.MountedWeapons
import net.psforever.objects.zones.Zone
import net.psforever.types.PlanetSideGUID
import net.psforever.services.support.{SimilarityComparator, SupportActor, SupportActorCaseConversions}
import net.psforever.services.base.support.{SimilarityComparator, SupportActor, SupportActorCaseConversions}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.concurrent.Future