modified ChatActor to be replaced by a function-data logic pair, and modified ChatService to be able to accommodate the new chat channel; chat packet handling moved from general operations to the new chat operations

This commit is contained in:
Fate-JH 2024-05-04 09:26:38 -04:00
parent b5b72e5b7b
commit 8e7be33a15
21 changed files with 1791 additions and 114 deletions

View file

@ -10,6 +10,7 @@ import net.psforever.actors.session.spectator.{SpectatorMode => SessionSpectator
import net.psforever.actors.zone.ZoneActor
import net.psforever.objects.sourcing.PlayerSource
import net.psforever.objects.zones.ZoneInfo
import net.psforever.services.chat.{DefaultChannel, SquadChannel}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.types.ChatMessageType.CMT_QUIT
import org.log4s.Logger
@ -33,7 +34,7 @@ import net.psforever.packet.game.objectcreate.DrawnSlot
import net.psforever.packet.game.{ChatMsg, CreateShortcutMessage, DeadState, RequestDestroyMessage, Shortcut}
import net.psforever.services.{CavernRotationService, InterstellarClusterService}
import net.psforever.services.chat.ChatService
import net.psforever.services.chat.ChatService.ChatChannel
import net.psforever.services.chat.ChatChannel
import net.psforever.types.ChatMessageType.{CMT_GMOPEN, UNK_227, UNK_229}
import net.psforever.types.{ChatMessageType, Cosmetic, ExperienceType, ImplantType, PlanetSideEmpire, PlanetSideGUID, Vector3}
import net.psforever.util.{Config, PointOfInterest}
@ -385,7 +386,7 @@ object ChatActor {
sendTo ! ChatService.Message(
session,
ChatMsg(UNK_227, wideContents=true, "", content.mkString(" "), None),
ChatChannel.Default()
DefaultChannel
)
true
}
@ -688,7 +689,7 @@ class ChatActor(
case ListingResponse(ChatService.ChatServiceKey.Listing(listings)) =>
chatService = Some(listings.head)
channels ++= List(ChatChannel.Default())
channels ++= List(DefaultChannel)
postStartBehaviour()
case SetSession(newSession) =>
@ -704,7 +705,7 @@ class ChatActor(
def postStartBehaviour(): Behavior[Command] = {
(session, chatService, cluster) match {
case (Some(_session), Some(_chatService), Some(_cluster)) if _session.player != null =>
_chatService ! ChatService.JoinChannel(chatServiceAdapter, _session, ChatChannel.Default())
_chatService ! ChatService.JoinChannel(chatServiceAdapter, null, DefaultChannel)
logic = NormalMode.init(_chatService, _cluster)
buffer.unstashAll(active(_session, _chatService, _cluster))
case _ =>
@ -724,7 +725,7 @@ class ChatActor(
active(newSession, chatService, cluster)
case JoinChannel(channel) =>
chatService ! ChatService.JoinChannel(chatServiceAdapter, session, channel)
chatService ! ChatService.JoinChannel(chatServiceAdapter, null, channel)
channels ++= List(channel)
Behaviors.same
@ -1057,7 +1058,7 @@ class ChatActor(
// SH prefix are tactical voice macros only sent to squad
if (contents.startsWith("SH")) {
channels.foreach {
case _/*channel*/: ChatChannel.Squad =>
case _/*channel*/: SquadChannel =>
commandSendToRecipient(session, message, chatService)
case _ => ()
}
@ -1082,7 +1083,7 @@ class ChatActor(
def commandSquad(session: Session, message: ChatMsg, chatService: ActorRef[ChatService.Command]): Unit = {
channels.foreach {
case _/*channel*/: ChatChannel.Squad =>
case _/*channel*/: SquadChannel =>
commandSendToRecipient(session, message, chatService)
case _ => ()
}
@ -1415,7 +1416,7 @@ class ChatActor(
chatService ! ChatService.Message(
session,
message,
ChatChannel.Default()
DefaultChannel
)
}
@ -1423,7 +1424,7 @@ class ChatActor(
chatService ! ChatService.Message(
session,
message.copy(recipient = session.player.Name),
ChatChannel.Default()
DefaultChannel
)
}

View file

@ -113,7 +113,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
middlewareActor ! MiddlewareActor.Send(KeepAliveMessage())
case SessionActor.SetMode(newMode) =>
logic.switchFrom(data.session)
if (mode != newMode) {
logic.switchFrom(data.session)
}
mode = newMode
logic = mode.setup(data)
logic.switchTo(data.session)

View file

@ -0,0 +1,234 @@
// Copyright (c) 2024 PSForever
package net.psforever.actors.session.normal
import akka.actor.ActorContext
import net.psforever.actors.session.support.{ChatFunctions, ChatOperations, SessionData}
import net.psforever.objects.Session
import net.psforever.packet.game.{ChatMsg, SetChatFilterMessage}
import net.psforever.services.chat.DefaultChannel
import net.psforever.types.ChatMessageType
import net.psforever.util.Config
object ChatLogic {
def apply(ops: ChatOperations): ChatLogic = {
new ChatLogic(ops, ops.context)
}
}
class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) extends ChatFunctions {
def sessionLogic: SessionData = ops.sessionLogic
def handleChatMsg(session: Session, message: ChatMsg): Unit = {
import net.psforever.types.ChatMessageType._
val gmCommandAllowed =
session.account.gm || Config.app.development.unprivilegedGmCommands.contains(message.messageType)
(message.messageType, message.recipient.trim, message.contents.trim) match {
/** Messages starting with ! are custom chat commands */
case (_, _, contents) if contents.startsWith("!") &&
customCommandMessages(message, session) => ()
case (CMT_FLY, recipient, contents) if gmCommandAllowed =>
ops.commandFly(contents, recipient)
case (CMT_ANONYMOUS, _, _) =>
// ?
case (CMT_TOGGLE_GM, _, _) =>
// ?
case (CMT_CULLWATERMARK, _, contents) =>
ops.commandWatermark(contents)
case (CMT_SPEED, _, contents) if gmCommandAllowed =>
ops.commandSpeed(message, contents)
case (CMT_TOGGLESPECTATORMODE, _, contents) if gmCommandAllowed =>
ops.commandToggleSpectatorMode(session, contents)
case (CMT_RECALL, _, _) =>
ops.commandRecall(session)
case (CMT_INSTANTACTION, _, _) =>
ops.commandInstantAction(session)
case (CMT_QUIT, _, _) =>
ops.commandQuit(session)
case (CMT_SUICIDE, _, _) =>
ops.commandSuicide(session)
case (CMT_DESTROY, _, contents) if contents.matches("\\d+") =>
ops.commandDestroy(session, message, contents)
case (CMT_SETBASERESOURCES, _, contents) if gmCommandAllowed =>
ops.commandSetBaseResources(session, contents)
case (CMT_ZONELOCK, _, contents) if gmCommandAllowed =>
ops.commandZoneLock(contents)
case (U_CMT_ZONEROTATE, _, _) if gmCommandAllowed =>
ops.commandZoneRotate()
case (CMT_CAPTUREBASE, _, contents) if gmCommandAllowed =>
ops.commandCaptureBase(session, message, contents)
case (CMT_GMBROADCAST | CMT_GMBROADCAST_NC | CMT_GMBROADCAST_VS | CMT_GMBROADCAST_TR, _, _)
if gmCommandAllowed =>
ops.commandSendToRecipient(session, message, DefaultChannel)
case (CMT_GMTELL, _, _) if gmCommandAllowed =>
ops.commandSend(session, message, DefaultChannel)
case (CMT_GMBROADCASTPOPUP, _, _) if gmCommandAllowed =>
ops.commandSendToRecipient(session, message, DefaultChannel)
case (CMT_OPEN, _, _) if !session.player.silenced =>
ops.commandSendToRecipient(session, message, DefaultChannel)
case (CMT_VOICE, _, contents) =>
ops.commandVoice(session, message, contents, DefaultChannel)
case (CMT_TELL, _, _) if !session.player.silenced =>
ops.commandTellOrIgnore(session, message, DefaultChannel)
case (CMT_BROADCAST, _, _) if !session.player.silenced =>
ops.commandSendToRecipient(session, message, DefaultChannel)
case (CMT_PLATOON, _, _) if !session.player.silenced =>
ops.commandSendToRecipient(session, message, DefaultChannel)
case (CMT_COMMAND, _, _) if gmCommandAllowed =>
ops.commandSendToRecipient(session, message, DefaultChannel)
case (CMT_NOTE, _, _) =>
ops.commandSend(session, message, DefaultChannel)
case (CMT_SILENCE, _, _) if gmCommandAllowed =>
ops.commandSend(session, message, DefaultChannel)
case (CMT_SQUAD, _, _) =>
ops.commandSquad(session, message, DefaultChannel) //todo SquadChannel, but what is the guid
case (CMT_WHO | CMT_WHO_CSR | CMT_WHO_CR | CMT_WHO_PLATOONLEADERS | CMT_WHO_SQUADLEADERS | CMT_WHO_TEAMS, _, _) =>
ops.commandWho(session)
case (CMT_ZONE, _, contents) if gmCommandAllowed =>
ops.commandZone(message, contents)
case (CMT_WARP, _, contents) if gmCommandAllowed =>
ops.commandWarp(session, message, contents)
case (CMT_SETBATTLERANK, _, contents) if gmCommandAllowed =>
ops.commandSetBattleRank(session, message, contents)
case (CMT_SETCOMMANDRANK, _, contents) if gmCommandAllowed =>
ops.commandSetCommandRank(session, message, contents)
case (CMT_ADDBATTLEEXPERIENCE, _, contents) if gmCommandAllowed =>
ops.commandAddBattleExperience(message, contents)
case (CMT_ADDCOMMANDEXPERIENCE, _, contents) if gmCommandAllowed =>
ops.commandAddCommandExperience(message, contents)
case (CMT_TOGGLE_HAT, _, contents) =>
ops.commandToggleHat(session, message, contents)
case (CMT_HIDE_HELMET | CMT_TOGGLE_SHADES | CMT_TOGGLE_EARPIECE, _, contents) =>
ops.commandToggleCosmetics(session, message, contents)
case (CMT_ADDCERTIFICATION, _, contents) if gmCommandAllowed =>
ops.commandAddCertification(session, message, contents)
case (CMT_KICK, _, contents) if gmCommandAllowed =>
ops.commandKick(session, message, contents)
case _ =>
log.warn(s"Unhandled chat message $message")
}
}
def handleChatFilter(pkt: SetChatFilterMessage): Unit = {
val SetChatFilterMessage(_, _, _) = pkt
}
def handleIncomingMessage(session: Session, message: ChatMsg, fromSession: Session): Unit = {
import ChatMessageType._
message.messageType match {
case CMT_BROADCAST | CMT_SQUAD | CMT_PLATOON | CMT_COMMAND | CMT_NOTE =>
ops.commandIncomingSendAllIfOnline(session, message)
case CMT_OPEN =>
ops.commandIncomingSendToLocalIfOnline(session, fromSession, message)
case CMT_TELL | U_CMT_TELLFROM |
CMT_GMOPEN | CMT_GMBROADCAST | CMT_GMBROADCAST_NC | CMT_GMBROADCAST_TR | CMT_GMBROADCAST_VS |
CMT_GMBROADCASTPOPUP | CMT_GMTELL | U_CMT_GMTELLFROM | UNK_45 | UNK_71 | UNK_227 | UNK_229 =>
ops.commandIncomingSend(message)
case CMT_VOICE =>
ops.commandIncomingVoice(session, fromSession, message)
case CMT_SILENCE =>
ops.commandIncomingSilence(session, message)
case _ =>
log.warn(s"Unexpected messageType $message")
}
}
private def customCommandMessages(
message: ChatMsg,
session: Session
): Boolean = {
val contents = message.contents
if (contents.startsWith("!")) {
val (command, params) = ops.cliTokenization(contents.drop(1)) match {
case a :: b => (a, b)
case _ => ("", Seq(""))
}
val gmBangCommandAllowed = session.account.gm || Config.app.development.unprivilegedGmBangCommands.contains(command)
//try gm commands
val tryGmCommandResult = if (gmBangCommandAllowed) {
command match {
case "whitetext" => Some(ops.customCommandWhitetext(session, params))
case "list" => Some(ops.customCommandList(session, params, message))
case "ntu" => Some(ops.customCommandNtu(session, params))
case "zonerotate" => Some(ops.customCommandZonerotate(params))
case "nearby" => Some(ops.customCommandNearby(session))
case _ => None
}
} else {
None
}
//try commands for all players if not caught as a gm command
val result = tryGmCommandResult match {
case None =>
command match {
case "loc" => ops.customCommandLoc(session, message)
case "suicide" => ops.customCommandSuicide(session)
case "grenade" => ops.customCommandGrenade(session, log)
case "macro" => ops.customCommandMacro(session, params)
case "progress" => ops.customCommandProgress(session, params)
case _ => false
}
case Some(out) =>
out
}
if (!result) {
// command was not handled
sendResponse(
ChatMsg(
ChatMessageType.CMT_GMOPEN, // CMT_GMTELL
message.wideContents,
"Server",
s"Unknown command !$command",
message.note
)
)
}
result
} else {
false // not a handled command
}
}
}

View file

@ -2,11 +2,11 @@
package net.psforever.actors.session.normal
import akka.actor.typed.scaladsl.adapter._
import akka.actor.{ActorContext, Cancellable, typed}
import net.psforever.actors.session.{AvatarActor, ChatActor, SessionActor}
import akka.actor.{ActorContext, typed}
import net.psforever.actors.session.{AvatarActor, SessionActor}
import net.psforever.actors.session.support.{GeneralFunctions, GeneralOperations, SessionData}
import net.psforever.login.WorldSession.{CallBackForTask, ContainableMoveItem, DropEquipmentFromInventory, PickUpEquipmentFromGround, RemoveOldEquipmentFromInventory}
import net.psforever.objects.{Account, BoomerDeployable, BoomerTrigger, ConstructionItem, Default, Deployables, GlobalDefinitions, Kit, LivePlayerList, PlanetSideGameObject, Player, SensorDeployable, ShieldGeneratorDeployable, SpecialEmp, TelepadDeployable, Tool, TrapDeployable, TurretDeployable, Vehicle}
import net.psforever.objects.{Account, BoomerDeployable, BoomerTrigger, ConstructionItem, Deployables, GlobalDefinitions, Kit, LivePlayerList, PlanetSideGameObject, Player, SensorDeployable, ShieldGeneratorDeployable, SpecialEmp, TelepadDeployable, Tool, TrapDeployable, TurretDeployable, Vehicle}
import net.psforever.objects.avatar.{Avatar, PlayerControl, SpecialCarry}
import net.psforever.objects.ballistics.Projectile
import net.psforever.objects.ce.{Deployable, DeployedItem, TelepadLike}
@ -39,13 +39,12 @@ import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.objects.zones.{Zone, ZoneProjectile, Zoning}
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.objectcreate.ObjectClass
import net.psforever.packet.game.{ActionCancelMessage, ActionResultMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BindStatus, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestAction, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, ItemTransactionMessage, LootItemMessage, MoveItemMessage, ObjectDeleteMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, SetChatFilterMessage, ShiftState, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostKill, VoiceHostRequest, ZipLineMessage}
import net.psforever.packet.game.{ActionCancelMessage, ActionResultMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BindStatus, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestAction, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, ItemTransactionMessage, LootItemMessage, MoveItemMessage, ObjectDeleteMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, ShiftState, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostKill, VoiceHostRequest, ZipLineMessage}
import net.psforever.services.RemoverActor
import net.psforever.services.account.{AccountPersistenceService, RetrieveAccountData}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.services.local.support.CaptureFlagManager
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import net.psforever.types.{CapacitorStateType, ChatMessageType, Cosmetic, DriveState, ExoSuitType, ImplantType, PlanetSideEmpire, PlanetSideGUID, SpawnGroup, TransactionType, Vector3}
import net.psforever.util.Config
@ -62,8 +61,6 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor
private val chatActor: typed.ActorRef[ChatActor.Command] = ops.chatActor
def handleConnectToWorldRequest(pkt: ConnectToWorldRequestMessage): Unit = {
val ConnectToWorldRequestMessage(_, token, majorVersion, minorVersion, revision, buildDate, _, _) = pkt
log.trace(
@ -192,14 +189,6 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
player.zoneInteractions()
}
def handleChat(pkt: ChatMsg): Unit = {
chatActor ! ChatActor.Message(pkt)
}
def handleChatFilter(pkt: SetChatFilterMessage): Unit = {
val SetChatFilterMessage(_, _, _) = pkt
}
def handleVoiceHostRequest(pkt: VoiceHostRequest): Unit = {
log.debug(s"$pkt")
sendResponse(VoiceHostKill())

View file

@ -3,10 +3,9 @@ package net.psforever.actors.session.normal
import akka.actor.Actor.Receive
import akka.actor.ActorRef
import net.psforever.actors.session.ChatActor
import net.psforever.actors.session.support.{GeneralFunctions, LocalHandlerFunctions, MountHandlerFunctions, SquadHandlerFunctions, TerminalHandlerFunctions, VehicleFunctions, VehicleHandlerFunctions, WeaponAndProjectileFunctions}
import net.psforever.objects.Session
import net.psforever.actors.session.support.{ChatFunctions, GeneralFunctions, LocalHandlerFunctions, MountHandlerFunctions, SquadHandlerFunctions, TerminalHandlerFunctions, VehicleFunctions, VehicleHandlerFunctions, WeaponAndProjectileFunctions}
import net.psforever.packet.game.UplinkRequest
import net.psforever.services.chat.ChatService
//
import net.psforever.actors.session.{AvatarActor, SessionActor}
import net.psforever.actors.session.support.{ModeLogic, PlayerMode, SessionData, ZoningOperations}
@ -34,6 +33,7 @@ import net.psforever.util.Config
class NormalModeLogic(data: SessionData) extends ModeLogic {
val avatarResponse: AvatarHandlerLogic = AvatarHandlerLogic(data.avatarResponse)
val chat: ChatFunctions = ChatLogic(data.chat)
val galaxy: GalaxyHandlerLogic = GalaxyHandlerLogic(data.galaxyResponseHandlers)
val general: GeneralFunctions = GeneralLogic(data.general)
val local: LocalHandlerFunctions = LocalHandlerLogic(data.localResponse)
@ -44,11 +44,6 @@ class NormalModeLogic(data: SessionData) extends ModeLogic {
val vehicles: VehicleFunctions = VehicleLogic(data.vehicles)
val vehicleResponse: VehicleHandlerFunctions = VehicleHandlerLogic(data.vehicleResponseOperations)
override def switchTo(session: Session): Unit = {
data.general.chatActor ! ChatActor.SetMode("normal")
super.switchTo(session)
}
def parse(sender: ActorRef): Receive = {
/* really common messages (very frequently, every life) */
case packet: PlanetSideGamePacket =>
@ -75,6 +70,9 @@ class NormalModeLogic(data: SessionData) extends ModeLogic {
case VehicleServiceResponse(toChannel, guid, reply) =>
vehicleResponse.handle(toChannel, guid, reply)
case ChatService.MessageResponse(fromSession, message, _) =>
chat.handleIncomingMessage(data.session, message, fromSession)
case SessionActor.SendResponse(packet) =>
data.sendResponse(packet)
@ -315,10 +313,10 @@ class NormalModeLogic(data: SessionData) extends ModeLogic {
data.zoning.spawn.handleSpawnRequest(packet)
case packet: ChatMsg =>
general.handleChat(packet)
chat.handleChatMsg(data.session, packet)
case packet: SetChatFilterMessage =>
general.handleChatFilter(packet)
chat.handleChatFilter(packet)
case packet: VoiceHostRequest =>
general.handleVoiceHostRequest(packet)

View file

@ -3,12 +3,12 @@ package net.psforever.actors.session.normal
import akka.actor.{ActorContext, ActorRef, typed}
import net.psforever.actors.session.support.SessionSquadHandlers.SquadUIElement
import net.psforever.actors.session.{AvatarActor, ChatActor}
import net.psforever.actors.session.AvatarActor
import net.psforever.actors.session.support.{SessionData, SessionSquadHandlers, SquadHandlerFunctions}
import net.psforever.objects.{Default, LivePlayerList}
import net.psforever.objects.avatar.Avatar
import net.psforever.packet.game.{CharacterKnowledgeInfo, CharacterKnowledgeMessage, ChatMsg, MemberEvent, PlanetsideAttributeMessage, ReplicationStreamMessage, SquadAction, SquadDefinitionActionMessage, SquadDetailDefinitionUpdateMessage, SquadListing, SquadMemberEvent, SquadMembershipRequest, SquadMembershipResponse, SquadState, SquadStateInfo, SquadWaypointEvent, SquadWaypointRequest, WaypointEvent, WaypointEventAction}
import net.psforever.services.chat.ChatService
import net.psforever.services.chat.SquadChannel
import net.psforever.services.teamwork.{SquadResponse, SquadServiceMessage, SquadAction => SquadServiceAction}
import net.psforever.types.{ChatMessageType, PlanetSideGUID, SquadListDecoration, SquadResponseType, WaypointSubtype}
@ -23,8 +23,6 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor
private val chatActor: typed.ActorRef[ChatActor.Command] = ops.chatActor
private val squadService: ActorRef = ops.squadService
private var waypointCooldown: Long = 0L
@ -194,8 +192,8 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
sendResponse(SquadDefinitionActionMessage(squad.GUID, 0, SquadAction.Unknown(18)))
squadService ! SquadServiceMessage(player, continent, SquadServiceAction.ReloadDecoration())
ops.updateSquadRef = ref
ops. updateSquad = ops.PeriodicUpdatesWhenEnrolledInSquad
chatActor ! ChatActor.JoinChannel(ChatService.ChatChannel.Squad(squad.GUID))
ops.updateSquad = ops.PeriodicUpdatesWhenEnrolledInSquad
sessionLogic.chat.JoinChannel(SquadChannel(squad.GUID))
case _ =>
//other player is joining our squad
//load each member's entry
@ -245,7 +243,7 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
ops.squad_supplement_id = 0
ops.squadUpdateCounter = 0
ops.updateSquad = ops.NoSquadUpdates
chatActor ! ChatActor.LeaveChannel(ChatService.ChatChannel.Squad(squad.GUID))
sessionLogic.chat.LeaveChannel(SquadChannel(squad.GUID))
case _ =>
//remove each member's entry
ops.GiveSquadColorsToMembers(

View file

@ -0,0 +1,143 @@
// Copyright (c) 2024 PSForever
package net.psforever.actors.session.spectator
import akka.actor.ActorContext
import net.psforever.actors.session.SessionActor
import net.psforever.actors.session.normal.NormalMode
import net.psforever.actors.session.support.{ChatFunctions, ChatOperations, SessionData}
import net.psforever.objects.Session
import net.psforever.packet.game.{ChatMsg, SetChatFilterMessage}
import net.psforever.services.chat.SpectatorChannel
import net.psforever.types.ChatMessageType
import scala.collection.Seq
object ChatLogic {
def apply(ops: ChatOperations): ChatLogic = {
new ChatLogic(ops, ops.context)
}
}
class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) extends ChatFunctions {
def sessionLogic: SessionData = ops.sessionLogic
def handleChatMsg(session: Session, message: ChatMsg): Unit = {
import ChatMessageType._
(message.messageType, message.recipient.trim, message.contents.trim) match {
/** Messages starting with ! are custom chat commands */
case (_, _, contents) if contents.startsWith("!") &&
customCommandMessages(message, session) => ()
case (CMT_FLY, recipient, contents) =>
ops.commandFly(contents, recipient)
case (CMT_ANONYMOUS, _, _) =>
// ?
case (CMT_TOGGLE_GM, _, _) =>
// ?
case (CMT_CULLWATERMARK, _, contents) =>
ops.commandWatermark(contents)
case (CMT_SPEED, _, contents) =>
ops.commandSpeed(message, contents)
case (CMT_TOGGLESPECTATORMODE, _, contents) =>
commandToggleSpectatorMode(contents)
case (CMT_RECALL, _, _) =>
commandToggleSpectatorMode(contents = "off")
case (CMT_QUIT, _, _) =>
ops.commandQuit(session)
case (CMT_SUICIDE, _, _) =>
commandToggleSpectatorMode(contents = "off")
case (CMT_OPEN, _, _) =>
ops.commandSendToRecipient(session, message, SpectatorChannel)
case (CMT_VOICE, _, contents) =>
ops.commandVoice(session, message, contents, SpectatorChannel)
case (CMT_TELL, _, _) =>
ops.commandTellOrIgnore(session, message, SpectatorChannel)
case (CMT_BROADCAST, _, _) =>
ops.commandSendToRecipient(session, message, SpectatorChannel)
case (CMT_PLATOON, _, _) =>
ops.commandSendToRecipient(session, message, SpectatorChannel)
case (CMT_GMTELL, _, _) =>
ops.commandSend(session, message, SpectatorChannel)
case (CMT_NOTE, _, _) =>
ops.commandSend(session, message, SpectatorChannel)
case (CMT_WHO | CMT_WHO_CSR | CMT_WHO_CR | CMT_WHO_PLATOONLEADERS | CMT_WHO_SQUADLEADERS | CMT_WHO_TEAMS, _, _) =>
ops.commandWho(session)
case (CMT_ZONE, _, contents) =>
ops.commandZone(message, contents)
case (CMT_WARP, _, contents) =>
ops.commandWarp(session, message, contents)
case _ => ()
}
}
def handleChatFilter(pkt: SetChatFilterMessage): Unit = {
val SetChatFilterMessage(_, _, _) = pkt
}
def handleIncomingMessage(session: Session, message: ChatMsg, fromSession: Session): Unit = {
import ChatMessageType._
message.messageType match {
case CMT_BROADCAST | CMT_SQUAD | CMT_PLATOON | CMT_COMMAND | CMT_NOTE =>
ops.commandIncomingSendAllIfOnline(session, message)
case CMT_OPEN =>
ops.commandIncomingSendToLocalIfOnline(session, fromSession, message)
case CMT_TELL | U_CMT_TELLFROM |
CMT_GMOPEN | CMT_GMBROADCAST | CMT_GMBROADCAST_NC | CMT_GMBROADCAST_TR | CMT_GMBROADCAST_VS |
CMT_GMBROADCASTPOPUP | CMT_GMTELL | U_CMT_GMTELLFROM | UNK_45 | UNK_71 | UNK_227 | UNK_229 =>
ops.commandIncomingSend(message)
case _ => ()
}
}
private def customCommandMessages(
message: ChatMsg,
session: Session
): Boolean = {
val contents = message.contents
if (contents.startsWith("!")) {
val (command, params) = ops.cliTokenization(contents.drop(1)) match {
case a :: b => (a, b)
case _ => ("", Seq(""))
}
command match {
case "list" => ops.customCommandList(session, params, message)
case "nearby" => ops.customCommandNearby(session)
case "loc" => ops.customCommandLoc(session, message)
case "macro" => ops.customCommandMacro(session, params)
case _ => false
}
} else {
false
}
}
private def commandToggleSpectatorMode(contents: String): Unit = {
contents.toLowerCase() match {
case "off" | "of" =>
context.self ! SessionActor.SetMode(NormalMode)
case _ => ()
}
}
}

View file

@ -2,7 +2,7 @@
package net.psforever.actors.session.spectator
import akka.actor.{ActorContext, typed}
import net.psforever.actors.session.{AvatarActor, ChatActor}
import net.psforever.actors.session.AvatarActor
import net.psforever.actors.session.support.{GeneralFunctions, GeneralOperations, SessionData}
import net.psforever.login.WorldSession.RemoveOldEquipmentFromInventory
import net.psforever.objects.{Account, BoomerDeployable, BoomerTrigger, GlobalDefinitions, LivePlayerList, PlanetSideGameObject, Player, TelepadDeployable, Tool, Vehicle}
@ -21,7 +21,7 @@ import net.psforever.objects.vehicles.Utility.InternalTelepad
import net.psforever.objects.vital.{VehicleDismountActivity, VehicleMountActivity}
import net.psforever.objects.zones.{Zone, ZoneProjectile}
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ChatMsg, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDeleteMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, SetChatFilterMessage, ShiftState, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostKill, VoiceHostRequest, ZipLineMessage}
import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ChatMsg, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDeleteMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, PlayerStateShiftMessage, RequestDestroyMessage, ShiftState, TargetInfo, TargetingImplantRequest, TargetingInfoMessage, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostKill, VoiceHostRequest, ZipLineMessage}
import net.psforever.services.RemoverActor
import net.psforever.services.account.AccountPersistenceService
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
@ -40,8 +40,6 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor
private val chatActor: typed.ActorRef[ChatActor.Command] = ops.chatActor
private var customImplants = SpectatorModeLogic.SpectatorImplants.map(_.get)
def handleConnectToWorldRequest(pkt: ConnectToWorldRequestMessage): Unit = { /* intentionally blank */ }
@ -85,12 +83,6 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
}
}
def handleChat(pkt: ChatMsg): Unit = {
chatActor ! ChatActor.Message(pkt)
}
def handleChatFilter(pkt: SetChatFilterMessage): Unit = { /* intentionally blank */ }
def handleVoiceHostRequest(pkt: VoiceHostRequest): Unit = {
log.debug(s"$pkt")
sendResponse(VoiceHostKill())

View file

@ -3,8 +3,7 @@ package net.psforever.actors.session.spectator
import akka.actor.Actor.Receive
import akka.actor.ActorRef
import net.psforever.actors.session.ChatActor
import net.psforever.actors.session.support.{AvatarHandlerFunctions, GalaxyHandlerFunctions, GeneralFunctions, LocalHandlerFunctions, MountHandlerFunctions, SquadHandlerFunctions, TerminalHandlerFunctions, VehicleFunctions, VehicleHandlerFunctions, WeaponAndProjectileFunctions}
import net.psforever.actors.session.support.{AvatarHandlerFunctions, ChatFunctions, GalaxyHandlerFunctions, GeneralFunctions, LocalHandlerFunctions, MountHandlerFunctions, SquadHandlerFunctions, TerminalHandlerFunctions, VehicleFunctions, VehicleHandlerFunctions, WeaponAndProjectileFunctions}
import net.psforever.actors.zone.ZoneActor
import net.psforever.objects.avatar.{BattleRank, CommandRank, DeployableToolbox, FirstTimeEvents, Implant, ProgressDecoration, Shortcut => AvatarShortcut}
import net.psforever.objects.serverobject.ServerObject
@ -13,6 +12,7 @@ import net.psforever.packet.PlanetSidePacket
import net.psforever.packet.game.{ObjectCreateDetailedMessage, ObjectDeleteMessage}
import net.psforever.packet.game.objectcreate.{ObjectClass, ObjectCreateMessageParent, RibbonBars}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.chat.{ChatService, SpectatorChannel}
import net.psforever.services.teamwork.{SquadAction, SquadServiceMessage}
import net.psforever.types.{CapacitorStateType, ChatMessageType, ExoSuitType, MeritCommendation, SquadRequestType}
//
@ -41,6 +41,7 @@ import net.psforever.services.vehicle.VehicleServiceResponse
class SpectatorModeLogic(data: SessionData) extends ModeLogic {
val avatarResponse: AvatarHandlerFunctions = AvatarHandlerLogic(data.avatarResponse)
val chat: ChatFunctions = ChatLogic(data.chat)
val galaxy: GalaxyHandlerFunctions = GalaxyHandlerLogic(data.galaxyResponseHandlers)
val general: GeneralFunctions = GeneralLogic(data.general)
val local: LocalHandlerFunctions = LocalHandlerLogic(data.localResponse)
@ -115,9 +116,12 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic {
val originalEvent = player.History.headOption
player.ClearHistory()
player.LogActivity(originalEvent)
player.spectator = true
//
data.general.chatActor ! ChatActor.SetMode("spectator")
player.spectator = true
if (player.silenced) {
data.chat.commandIncomingSilence(session, ChatMsg(ChatMessageType.CMT_SILENCE, "player 0"))
}
data.chat.JoinChannel(SpectatorChannel)
val newPlayer = SpectatorModeLogic.spectatorCharacter(player)
val cud = new SimpleItem(GlobalDefinitions.command_detonater)
cud.GUID = player.avatar.locker.GUID
@ -145,6 +149,7 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic {
val zoning = data.zoning
val sendResponse: PlanetSidePacket => Unit = data.sendResponse
//
data.chat.LeaveChannel(SpectatorChannel)
player.spectator = false
sendResponse(ObjectDeleteMessage(player.avatar.locker.GUID, 0)) //free up the slot (from cud)
sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, "off"))
@ -178,6 +183,9 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic {
case VehicleServiceResponse(toChannel, guid, reply) =>
vehicleResponse.handle(toChannel, guid, reply)
case ChatService.MessageResponse(fromSession, message, _) =>
chat.handleIncomingMessage(data.session, message, fromSession)
case SessionActor.SendResponse(packet) =>
data.sendResponse(packet)
@ -409,10 +417,10 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic {
data.zoning.spawn.handleSpawnRequest(packet)
case packet: ChatMsg =>
general.handleChat(packet)
chat.handleChatMsg(data.session, packet)
case packet: SetChatFilterMessage =>
general.handleChatFilter(packet)
chat.handleChatFilter(packet)
case packet: VoiceHostRequest =>
general.handleVoiceHostRequest(packet)

View file

@ -3,12 +3,12 @@ package net.psforever.actors.session.spectator
import akka.actor.{ActorContext, typed}
import net.psforever.actors.session.support.SessionSquadHandlers.SquadUIElement
import net.psforever.actors.session.{AvatarActor, ChatActor}
import net.psforever.actors.session.AvatarActor
import net.psforever.actors.session.support.{SessionData, SessionSquadHandlers, SquadHandlerFunctions}
import net.psforever.objects.{Default, LivePlayerList}
import net.psforever.objects.avatar.Avatar
import net.psforever.packet.game.{CharacterKnowledgeInfo, CharacterKnowledgeMessage, PlanetsideAttributeMessage, ReplicationStreamMessage, SquadAction, SquadDefinitionActionMessage, SquadDetailDefinitionUpdateMessage, SquadListing, SquadMemberEvent, SquadMembershipRequest, SquadMembershipResponse, SquadState, SquadStateInfo, SquadWaypointEvent, SquadWaypointRequest, WaypointEventAction}
import net.psforever.services.chat.ChatService
import net.psforever.services.chat.SquadChannel
import net.psforever.services.teamwork.SquadResponse
import net.psforever.types.{PlanetSideGUID, SquadListDecoration, SquadResponseType}
@ -23,10 +23,6 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor
private val chatActor: typed.ActorRef[ChatActor.Command] = ops.chatActor
//private val squadService: ActorRef = ops.squadService
/* packet */
def handleSquadDefinitionAction(pkt: SquadDefinitionActionMessage): Unit = { /* intentionally blank */ }
@ -122,7 +118,7 @@ class SquadHandlerLogic(val ops: SessionSquadHandlers, implicit val context: Act
ops.squad_supplement_id = 0
ops.squadUpdateCounter = 0
ops.updateSquad = ops.NoSquadUpdates
chatActor ! ChatActor.LeaveChannel(ChatService.ChatChannel.Squad(squad.GUID))
sessionLogic.chat.LeaveChannel(SquadChannel(squad.GUID))
case _ =>
//remove each member's entry
ops.GiveSquadColorsToMembers(

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,7 @@ import scala.collection.mutable
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
//
import net.psforever.actors.session.{AvatarActor, ChatActor, SessionActor}
import net.psforever.actors.session.{AvatarActor, SessionActor}
import net.psforever.login.WorldSession._
import net.psforever.objects._
import net.psforever.objects.avatar._
@ -25,7 +25,7 @@ import net.psforever.objects.vehicles._
import net.psforever.objects.vital._
import net.psforever.objects.zones._
import net.psforever.packet._
import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, ChangeShortcutBankMessage, CharacterRequestMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DisplayedAwardMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, SetChatFilterMessage, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UseItemMessage, ZipLineMessage}
import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, ChangeShortcutBankMessage, CharacterRequestMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DisplayedAwardMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UseItemMessage, ZipLineMessage}
import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
import net.psforever.packet.game.objectcreate._
import net.psforever.packet.game._
@ -48,10 +48,6 @@ trait GeneralFunctions extends CommonSessionInterfacingFunctionality {
def handlePlayerStateUpstream(pkt: PlayerStateMessageUpstream): Unit
def handleChat(pkt: ChatMsg): Unit
def handleChatFilter(pkt: SetChatFilterMessage): Unit
def handleVoiceHostRequest(pkt: VoiceHostRequest): Unit
def handleVoiceHostInfo(pkt: VoiceHostInfo): Unit
@ -148,7 +144,6 @@ trait GeneralFunctions extends CommonSessionInterfacingFunctionality {
class GeneralOperations(
val sessionLogic: SessionData,
val avatarActor: typed.ActorRef[AvatarActor.Command],
val chatActor: typed.ActorRef[ChatActor.Command],
implicit val context: ActorContext
) extends CommonSessionInterfacingFunctionality {
private[session] var progressBarValue: Option[Float] = None

View file

@ -7,6 +7,7 @@ import net.psforever.objects.Session
trait ModeLogic {
def avatarResponse: AvatarHandlerFunctions
def chat: ChatFunctions
def galaxy: GalaxyHandlerFunctions
def general: GeneralFunctions
def local: LocalHandlerFunctions

View file

@ -8,7 +8,7 @@ import net.psforever.objects.zones.exp
import scala.collection.mutable
//
import net.psforever.actors.session.{AvatarActor, ChatActor}
import net.psforever.actors.session.AvatarActor
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
import net.psforever.packet.game._
import net.psforever.services.avatar.AvatarResponse
@ -24,7 +24,6 @@ trait AvatarHandlerFunctions extends CommonSessionInterfacingFunctionality {
class SessionAvatarHandlers(
val sessionLogic: SessionData,
val avatarActor: typed.ActorRef[AvatarActor.Command],
chatActor: typed.ActorRef[ChatActor.Command],
implicit val context: ActorContext
) extends CommonSessionInterfacingFunctionality {
//TODO player characters only exist within a certain range of GUIDs for a given zone; this is overkill

View file

@ -4,12 +4,14 @@ package net.psforever.actors.session.support
import akka.actor.typed.receptionist.Receptionist
import akka.actor.typed.scaladsl.adapter._
import akka.actor.{ActorContext, ActorRef, typed}
import net.psforever.services.chat.ChatService
import scala.collection.mutable
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
//
import net.psforever.actors.net.MiddlewareActor
import net.psforever.actors.session.{AvatarActor, ChatActor}
import net.psforever.actors.session.AvatarActor
import net.psforever.actors.zone.ZoneActor
import net.psforever.objects._
import net.psforever.objects.avatar._
@ -60,7 +62,7 @@ object SessionData {
class SessionData(
val middlewareActor: typed.ActorRef[MiddlewareActor.Command],
implicit val context: ActorContext
) {
) extends SessionSource {
/**
* Hardwire an implicit `sender` to be the same as `context.self` of the `SessionActor` actor class
* for which this support class was initialized.
@ -74,7 +76,6 @@ class SessionData(
private[this] implicit val sender: ActorRef = context.self
private val avatarActor: typed.ActorRef[AvatarActor.Command] = context.spawnAnonymous(AvatarActor(context.self))
private val chatActor: typed.ActorRef[ChatActor.Command] = context.spawnAnonymous(ChatActor(context.self, avatarActor))
private[session] val log = org.log4s.getLogger
private[session] var theSession: Session = Session()
@ -83,6 +84,7 @@ class SessionData(
private[session] var galaxyService: ActorRef = Default.Actor
private[session] var squadService: ActorRef = Default.Actor
private[session] var cluster: typed.ActorRef[ICS.Command] = Default.typed.Actor
private[session] var chatService: typed.ActorRef[ChatService.Command] = Default.typed.Actor
private[session] var connectionState: Int = 25
private[session] var persistFunc: () => Unit = noPersistence
private[session] var persist: () => Unit = updatePersistenceOnly
@ -92,13 +94,13 @@ class SessionData(
private var contextSafeEntity: PlanetSideGUID = PlanetSideGUID(0)
val general: GeneralOperations =
new GeneralOperations(sessionLogic=this, avatarActor, chatActor, context)
new GeneralOperations(sessionLogic=this, avatarActor, context)
val shooting: WeaponAndProjectileOperations =
new WeaponAndProjectileOperations(sessionLogic=this, avatarActor, chatActor, context)
new WeaponAndProjectileOperations(sessionLogic=this, avatarActor, context)
val vehicles: VehicleOperations =
new VehicleOperations(sessionLogic=this, avatarActor, context)
val avatarResponse: SessionAvatarHandlers =
new SessionAvatarHandlers(sessionLogic=this, avatarActor, chatActor, context)
new SessionAvatarHandlers(sessionLogic=this, avatarActor, context)
val localResponse: SessionLocalHandlers =
new SessionLocalHandlers(sessionLogic=this, context)
val mountResponse: SessionMountHandlers =
@ -109,16 +111,19 @@ class SessionData(
private var galaxyResponseOpt: Option[SessionGalaxyHandlers] = None
private var squadResponseOpt: Option[SessionSquadHandlers] = None
private var zoningOpt: Option[ZoningOperations] = None
private var chatOpt: Option[ChatOperations] = None
def vehicleResponseOperations: SessionVehicleHandlers = vehicleResponseOpt.orNull
def galaxyResponseHandlers: SessionGalaxyHandlers = galaxyResponseOpt.orNull
def squad: SessionSquadHandlers = squadResponseOpt.orNull
def zoning: ZoningOperations = zoningOpt.orNull
def chat: ChatOperations = chatOpt.orNull
ServiceManager.serviceManager ! Lookup("accountIntermediary")
ServiceManager.serviceManager ! Lookup("accountPersistence")
ServiceManager.serviceManager ! Lookup("galaxy")
ServiceManager.serviceManager ! Lookup("squad")
ServiceManager.receptionist ! Receptionist.Find(ICS.InterstellarClusterServiceKey, context.self)
ServiceManager.receptionist ! Receptionist.Find(ChatService.ChatServiceKey, context.self)
/**
* updated when an upstream packet arrives;
@ -129,7 +134,6 @@ class SessionData(
def session: Session = theSession
def session_=(session: Session): Unit = {
chatActor ! ChatActor.SetSession(session)
avatarActor ! AvatarActor.SetSession(session)
theSession = session
}
@ -164,6 +168,11 @@ class SessionData(
case ICS.InterstellarClusterServiceKey.Listing(listings) =>
cluster = listings.head
buildDependentOperationsForZoning(galaxyService, cluster)
buildDependentOperationsForChat(chatService, cluster)
true
case ChatService.ChatServiceKey.Listing(listings) =>
chatService = listings.head
buildDependentOperationsForChat(chatService, cluster)
true
case _ =>
@ -186,7 +195,13 @@ class SessionData(
def buildDependentOperationsForSquad(squadActor: ActorRef): Unit = {
if (squadResponseOpt.isEmpty && squadActor != Default.Actor) {
squadResponseOpt = Some(new SessionSquadHandlers(sessionLogic=this, avatarActor, chatActor, squadActor, context))
squadResponseOpt = Some(new SessionSquadHandlers(sessionLogic=this, avatarActor, squadActor, context))
}
}
def buildDependentOperationsForChat(chatService: typed.ActorRef[ChatService.Command], clusterActor: typed.ActorRef[ICS.Command]): Unit = {
if (chatOpt.isEmpty && chatService != Default.typed.Actor && clusterActor != Default.typed.Actor) {
chatOpt = Some(new ChatOperations(sessionLogic=this, avatarActor, chatService, clusterActor, context))
}
}
@ -196,7 +211,8 @@ class SessionData(
vehicleResponseOpt.nonEmpty &&
galaxyResponseOpt.nonEmpty &&
squadResponseOpt.nonEmpty &&
zoningOpt.nonEmpty
zoningOpt.nonEmpty &&
chatOpt.nonEmpty
}
/* support functions */
@ -363,11 +379,11 @@ class SessionData(
terminals.actionsToCancel()
if (session.flying) {
session = session.copy(flying = false)
chatActor ! ChatActor.Message(ChatMsg(ChatMessageType.CMT_FLY, wideContents=false, "", "off", None))
chat.commandFly(contents = "off", recipient = "")
}
if (session.speed > 1) {
session = session.copy(speed = 1)
chatActor ! ChatActor.Message(ChatMsg(ChatMessageType.CMT_SPEED, wideContents=false, "", "1.000", None))
chat.commandSpeed(ChatMsg(ChatMessageType.CMT_SPEED, "1.000"), contents = "1.000")
}
}
@ -542,7 +558,6 @@ class SessionData(
def stop(): Unit = {
context.stop(avatarActor)
context.stop(chatActor)
general.stop()
shooting.stop()
vehicles.stop()
@ -554,6 +569,7 @@ class SessionData(
galaxyResponseOpt.foreach(_.stop())
squadResponseOpt.foreach(_.stop())
zoningOpt.foreach(_.stop())
chatOpt.foreach(_.stop())
continent.AvatarEvents ! Service.Leave()
continent.LocalEvents ! Service.Leave()
continent.VehicleEvents ! Service.Leave()

View file

@ -4,7 +4,7 @@ package net.psforever.actors.session.support
import akka.actor.{ActorContext, ActorRef, typed}
import scala.collection.mutable
//
import net.psforever.actors.session.{AvatarActor, ChatActor}
import net.psforever.actors.session.AvatarActor
import net.psforever.objects.teamwork.Squad
import net.psforever.objects.{Default, Player}
import net.psforever.packet.game._
@ -39,7 +39,6 @@ object SessionSquadHandlers {
class SessionSquadHandlers(
val sessionLogic: SessionData,
val avatarActor: typed.ActorRef[AvatarActor.Command],
val chatActor: typed.ActorRef[ChatActor.Command],
val squadService: ActorRef,
implicit val context: ActorContext
) extends CommonSessionInterfacingFunctionality {

View file

@ -9,7 +9,7 @@ import net.psforever.objects.zones.exp.ToDatabase
import scala.collection.mutable
import scala.concurrent.duration._
//
import net.psforever.actors.session.{AvatarActor, ChatActor}
import net.psforever.actors.session.AvatarActor
import net.psforever.objects.avatar.scoring.EquipmentStat
import net.psforever.objects.ballistics.Projectile
import net.psforever.objects.equipment.EquipmentSize
@ -63,7 +63,6 @@ trait WeaponAndProjectileFunctions extends CommonSessionInterfacingFunctionality
class WeaponAndProjectileOperations(
val sessionLogic: SessionData,
val avatarActor: typed.ActorRef[AvatarActor.Command],
val chatActor: typed.ActorRef[ChatActor.Command],
implicit val context: ActorContext
) extends CommonSessionInterfacingFunctionality {
var shooting: mutable.Set[PlanetSideGUID] = mutable.Set.empty //ChangeFireStateMessage_Start

View file

@ -6,6 +6,7 @@ import akka.actor.typed.scaladsl.adapter._
import akka.actor.{ActorContext, ActorRef, Cancellable, typed}
import akka.pattern.ask
import akka.util.Timeout
import net.psforever.actors.session.spectator.SpectatorMode
import net.psforever.login.WorldSession
import net.psforever.objects.avatar.BattleRank
import net.psforever.objects.avatar.scoring.{CampaignStatistics, ScoreCard, SessionStatistics}
@ -17,6 +18,7 @@ import net.psforever.objects.serverobject.turret.auto.AutomatedTurret
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource}
import net.psforever.objects.vital.{InGameHistory, IncarnationActivity, ReconstructionActivity, SpawningActivity}
import net.psforever.packet.game.{CampaignStatistic, ChangeFireStateMessage_Start, MailMessage, ObjectDetectedMessage, SessionStatistic}
import net.psforever.services.chat.DefaultChannel
import scala.collection.mutable
import scala.concurrent.duration._
@ -2164,6 +2166,7 @@ class ZoningOperations(
*/
def avatarLoginResponse(avatar: Avatar): Unit = {
session = session.copy(avatar = avatar)
sessionLogic.chat.JoinChannel(DefaultChannel)
Deployables.InitializeDeployableQuantities(avatar)
cluster ! ICS.FilterZones(_ => true, context.self)
}
@ -3070,6 +3073,9 @@ class ZoningOperations(
}
})
}
if (!setAvatar && tplayer.spectator) {
context.self ! SessionActor.SetMode(SpectatorMode) //should reload spectator status
}
}
upstreamMessageCount = 0
setAvatar = true

View file

@ -15,3 +15,7 @@ case class Session(
speed: Float = 1.0f,
flying: Boolean = false
)
trait SessionSource {
def session: Session
}

View file

@ -0,0 +1,12 @@
// Copyright (c) 2024 PSForever
package net.psforever.services.chat
import net.psforever.types.PlanetSideGUID
trait ChatChannel
case object DefaultChannel extends ChatChannel
final case class SquadChannel(guid: PlanetSideGUID) extends ChatChannel
case object SpectatorChannel extends ChatChannel

View file

@ -4,9 +4,9 @@ package net.psforever.services.chat
import akka.actor.typed.receptionist.{Receptionist, ServiceKey}
import akka.actor.typed.{ActorRef, Behavior}
import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors}
import net.psforever.objects.Session
import net.psforever.objects.{Session, SessionSource}
import net.psforever.packet.game.ChatMsg
import net.psforever.types.{ChatMessageType, PlanetSideEmpire, PlanetSideGUID}
import net.psforever.types.{ChatMessageType, PlanetSideEmpire}
object ChatService {
val ChatServiceKey: ServiceKey[Command] = ServiceKey[ChatService.Command]("chatService")
@ -19,20 +19,12 @@ object ChatService {
sealed trait Command
final case class JoinChannel(actor: ActorRef[MessageResponse], session: Session, channel: ChatChannel) extends Command
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)
trait ChatChannel
object ChatChannel {
// one of the default channels that the player is always subscribed to (local, broadcast, command...)
final case class Default() extends ChatChannel
final case class Squad(guid: PlanetSideGUID) extends ChatChannel
}
}
class ChatService(context: ActorContext[ChatService.Command]) extends AbstractBehavior[ChatService.Command](context) {
@ -63,9 +55,10 @@ class ChatService(context: ActorContext[ChatService.Command]) extends AbstractBe
case Message(session, message, channel) =>
(channel, message.messageType) match {
case (ChatChannel.Squad(_), CMT_SQUAD) => ()
case (ChatChannel.Squad(_), CMT_VOICE) if message.contents.startsWith("SH") => ()
case (ChatChannel.Default(), messageType) if messageType != CMT_SQUAD => ()
case (SquadChannel(_), CMT_SQUAD) => ()
case (SquadChannel(_), CMT_VOICE) if message.contents.startsWith("SH") => ()
case (DefaultChannel, messageType) if messageType != CMT_SQUAD => ()
case (SpectatorChannel, messageType) if messageType != CMT_SQUAD => ()
case _ =>
log.error(s"invalid chat channel $channel for messageType ${message.messageType}")
return this
@ -78,8 +71,8 @@ class ChatService(context: ActorContext[ChatService.Command]) extends AbstractBe
val recipientName = message.recipient
val recipientNameLower = recipientName.toLowerCase()
(
subs.find(_.session.player.Name.toLowerCase().equals(playerNameLower)),
subs.find(_.session.player.Name.toLowerCase().equals(recipientNameLower))
subs.find(_.sessionSource.session.player.Name.toLowerCase().equals(playerNameLower)),
subs.find(_.sessionSource.session.player.Name.toLowerCase().equals(recipientNameLower))
) match {
case (Some(JoinChannel(sender, _, _)), Some(JoinChannel(receiver, _, _))) =>
val replyType = if (mtype == CMT_TELL) { U_CMT_TELLFROM } else { U_CMT_GMTELLFROM }
@ -122,14 +115,14 @@ class ChatService(context: ActorContext[ChatService.Command]) extends AbstractBe
case _ => (None, None, None)
}
val sender = subs.find(_.session.player.Name == session.player.Name)
val sender = subs.find(_.sessionSource.session.player.Name == session.player.Name)
(sender, name, time, error) match {
case (Some(sender), Some(name), Some(_), None) =>
val recipient = subs.find(_.session.player.Name == name)
val recipient = subs.find(_.sessionSource.session.player.Name == name)
recipient match {
case Some(recipient) =>
if (recipient.session.player.silenced) {
if (recipient.sessionSource.session.player.silenced) {
sender.actor ! MessageResponse(
session,
ChatMsg(UNK_229, wideContents = true, "", "@silence_disabled_ack", None),
@ -167,7 +160,7 @@ class ChatService(context: ActorContext[ChatService.Command]) extends AbstractBe
case CMT_NOTE =>
subs
.filter(_.session.player.Name == message.recipient)
.filter(_.sessionSource.session.player.Name == message.recipient)
.foreach(
_.actor ! MessageResponse(session, message.copy(recipient = session.player.Name), channel)
)
@ -175,23 +168,23 @@ class ChatService(context: ActorContext[ChatService.Command]) extends AbstractBe
// faction commands
case CMT_OPEN | CMT_PLATOON | CMT_COMMAND =>
subs
.filter(_.session.player.Faction == session.player.Faction)
.filter(_.sessionSource.session.player.Faction == session.player.Faction)
.foreach(
_.actor ! MessageResponse(session, message, channel)
)
case CMT_GMBROADCAST_NC =>
subs.filter(_.session.player.Faction == PlanetSideEmpire.NC).foreach {
subs.filter(_.sessionSource.session.player.Faction == PlanetSideEmpire.NC).foreach {
case JoinChannel(actor, _, _) => actor ! MessageResponse(session, message, channel)
}
case CMT_GMBROADCAST_TR =>
subs.filter(_.session.player.Faction == PlanetSideEmpire.TR).foreach {
subs.filter(_.sessionSource.session.player.Faction == PlanetSideEmpire.TR).foreach {
case JoinChannel(actor, _, _) => actor ! MessageResponse(session, message, channel)
}
case CMT_GMBROADCAST_VS =>
subs.filter(_.session.player.Faction == PlanetSideEmpire.VS).foreach {
subs.filter(_.sessionSource.session.player.Faction == PlanetSideEmpire.VS).foreach {
case JoinChannel(actor, _, _) => actor ! MessageResponse(session, message, channel)
}