mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-03-06 22:10:26 +00:00
reorganized files and methods for session actor in preparation for custom spectator implementation
This commit is contained in:
parent
9319f7e7bd
commit
cab41ac0b8
16 changed files with 1816 additions and 1722 deletions
|
|
@ -1,38 +1,20 @@
|
|||
// Copyright (c) 2016, 2020 PSForever
|
||||
// Copyright (c) 2016, 2020, 2024 PSForever
|
||||
package net.psforever.actors.session
|
||||
|
||||
import akka.actor.typed.receptionist.Receptionist
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
import akka.actor.{Actor, MDCContextAware, typed}
|
||||
import akka.actor.{Actor, Cancellable, MDCContextAware, typed}
|
||||
import net.psforever.actors.session.support.NormalUser
|
||||
import org.joda.time.LocalDateTime
|
||||
import org.log4s.MDC
|
||||
import scala.collection.mutable
|
||||
//
|
||||
import net.psforever.actors.net.MiddlewareActor
|
||||
import net.psforever.actors.session.support.SessionData
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.avatar._
|
||||
import net.psforever.objects.definition._
|
||||
import net.psforever.objects.guid._
|
||||
import net.psforever.objects.serverobject.containable.Containable
|
||||
import net.psforever.objects.serverobject.deploy.Deployment
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.terminals._
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
import net.psforever.objects.zones._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.services.CavernRotationService.SendCavernRotationUpdates
|
||||
import net.psforever.services.ServiceManager.{Lookup, LookupResult}
|
||||
import net.psforever.services.account.{PlayerToken, ReceiveAccountData}
|
||||
import net.psforever.services.avatar.AvatarServiceResponse
|
||||
import net.psforever.services.galaxy.GalaxyServiceResponse
|
||||
import net.psforever.services.local.LocalServiceResponse
|
||||
import net.psforever.services.teamwork.SquadServiceResponse
|
||||
import net.psforever.services.vehicle.VehicleServiceResponse
|
||||
import net.psforever.services.{CavernRotationService, ServiceManager, InterstellarClusterService => ICS}
|
||||
import net.psforever.types._
|
||||
import net.psforever.util.Config
|
||||
import net.psforever.objects.{Default, Player}
|
||||
import net.psforever.objects.avatar.Avatar
|
||||
import net.psforever.objects.definition.BasicDefinition
|
||||
import net.psforever.packet.PlanetSidePacket
|
||||
import net.psforever.packet.game.{FriendsResponse, KeepAliveMessage}
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
object SessionActor {
|
||||
sealed trait Command
|
||||
|
|
@ -86,28 +68,6 @@ object SessionActor {
|
|||
final case object CharSaved extends Command
|
||||
|
||||
private[session] case object CharSavedMsg extends Command
|
||||
|
||||
/**
|
||||
* The message that progresses some form of user-driven activity with a certain eventual outcome
|
||||
* and potential feedback per cycle.
|
||||
* @param delta how much the progress value changes each tick, which will be treated as a percentage;
|
||||
* must be a positive value
|
||||
* @param completionAction a finalizing action performed once the progress reaches 100(%)
|
||||
* @param tickAction an action that is performed for each increase of progress
|
||||
* @param tickTime how long between each `tickAction` (ms);
|
||||
* defaults to 250 milliseconds
|
||||
*/
|
||||
private[session] final case class ProgressEvent(
|
||||
delta: Float,
|
||||
completionAction: () => Unit,
|
||||
tickAction: Float => Boolean,
|
||||
tickTime: Long = 250L
|
||||
)
|
||||
|
||||
private[session] final case class AvatarAwardMessageBundle(
|
||||
bundle: Iterable[Iterable[PlanetSideGamePacket]],
|
||||
delay: Long
|
||||
)
|
||||
}
|
||||
|
||||
class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], connectionId: String, sessionId: Long)
|
||||
|
|
@ -115,493 +75,46 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
with MDCContextAware {
|
||||
MDC("connectionId") = connectionId
|
||||
|
||||
private var clientKeepAlive: Cancellable = Default.Cancellable
|
||||
private[this] val buffer: mutable.ListBuffer[Any] = new mutable.ListBuffer[Any]()
|
||||
private[this] val sessionFuncs = new SessionData(middlewareActor, context)
|
||||
|
||||
ServiceManager.serviceManager ! Lookup("accountIntermediary")
|
||||
ServiceManager.serviceManager ! Lookup("accountPersistence")
|
||||
ServiceManager.serviceManager ! Lookup("galaxy")
|
||||
ServiceManager.serviceManager ! Lookup("squad")
|
||||
ServiceManager.receptionist ! Receptionist.Find(ICS.InterstellarClusterServiceKey, context.self)
|
||||
private[this] val logic = new NormalUser(middlewareActor, context)
|
||||
|
||||
override def postStop(): Unit = {
|
||||
//normally, the player avatar persists a minute or so after disconnect; we are subject to the SessionReaper
|
||||
//TODO put any temporary values back into the avatar
|
||||
sessionFuncs.stop()
|
||||
clientKeepAlive.cancel()
|
||||
logic.stop()
|
||||
}
|
||||
|
||||
def receive: Receive = startup
|
||||
|
||||
def startup: Receive = {
|
||||
case msg if !sessionFuncs.assignEventBus(msg) =>
|
||||
private def startup: Receive = {
|
||||
case msg if !logic.assignEventBus(msg) =>
|
||||
buffer.addOne(msg)
|
||||
case _ if sessionFuncs.whenAllEventBusesLoaded() =>
|
||||
case _ if logic.whenAllEventBusesLoaded() =>
|
||||
context.become(inTheGame)
|
||||
startHeartbeat()
|
||||
buffer.foreach { self.tell(_, self) } //we forget the original sender, shouldn't be doing callbacks at this point
|
||||
buffer.clear()
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
def inTheGame: Receive = {
|
||||
/* really common messages (very frequently, every life) */
|
||||
case packet: PlanetSideGamePacket =>
|
||||
handleGamePkt(packet)
|
||||
|
||||
case AvatarServiceResponse(toChannel, guid, reply) =>
|
||||
sessionFuncs.avatarResponse.handle(toChannel, guid, reply)
|
||||
|
||||
case GalaxyServiceResponse(_, reply) =>
|
||||
sessionFuncs.galaxyResponseHanders.handle(reply)
|
||||
|
||||
case LocalServiceResponse(toChannel, guid, reply) =>
|
||||
sessionFuncs.localResponse.handle(toChannel, guid, reply)
|
||||
|
||||
case Mountable.MountMessages(tplayer, reply) =>
|
||||
sessionFuncs.mountResponse.handle(tplayer, reply)
|
||||
|
||||
case SquadServiceResponse(_, excluded, response) =>
|
||||
sessionFuncs.squad.handle(response, excluded)
|
||||
|
||||
case Terminal.TerminalMessage(tplayer, msg, order) =>
|
||||
sessionFuncs.terminals.handle(tplayer, msg, order)
|
||||
|
||||
case VehicleServiceResponse(toChannel, guid, reply) =>
|
||||
sessionFuncs.vehicleResponseOperations.handle(toChannel, guid, reply)
|
||||
|
||||
case SessionActor.PokeClient() =>
|
||||
sessionFuncs.sendResponse(KeepAliveMessage())
|
||||
|
||||
case SessionActor.SendResponse(packet) =>
|
||||
sessionFuncs.sendResponse(packet)
|
||||
|
||||
case SessionActor.CharSaved =>
|
||||
sessionFuncs.renewCharSavedTimer(
|
||||
Config.app.game.savedMsg.interruptedByAction.fixed,
|
||||
Config.app.game.savedMsg.interruptedByAction.variable
|
||||
)
|
||||
|
||||
case SessionActor.CharSavedMsg =>
|
||||
sessionFuncs.displayCharSavedMsgThenRenewTimer(
|
||||
Config.app.game.savedMsg.renewal.fixed,
|
||||
Config.app.game.savedMsg.renewal.variable
|
||||
)
|
||||
|
||||
/* common messages (maybe once every respawn) */
|
||||
case ICS.SpawnPointResponse(response) =>
|
||||
sessionFuncs.zoning.handleSpawnPointResponse(response)
|
||||
|
||||
case SessionActor.NewPlayerLoaded(tplayer) =>
|
||||
sessionFuncs.zoning.spawn.handleNewPlayerLoaded(tplayer)
|
||||
|
||||
case SessionActor.PlayerLoaded(tplayer) =>
|
||||
sessionFuncs.zoning.spawn.handlePlayerLoaded(tplayer)
|
||||
|
||||
case Zone.Population.PlayerHasLeft(zone, None) =>
|
||||
log.debug(s"PlayerHasLeft: ${sessionFuncs.player.Name} does not have a body on ${zone.id}")
|
||||
|
||||
case Zone.Population.PlayerHasLeft(zone, Some(tplayer)) =>
|
||||
if (tplayer.isAlive) {
|
||||
log.info(s"${tplayer.Name} has left zone ${zone.id}")
|
||||
}
|
||||
|
||||
case Zone.Population.PlayerCanNotSpawn(zone, tplayer) =>
|
||||
log.warning(s"${tplayer.Name} can not spawn in zone ${zone.id}; why?")
|
||||
|
||||
case Zone.Population.PlayerAlreadySpawned(zone, tplayer) =>
|
||||
log.warning(s"${tplayer.Name} is already spawned on zone ${zone.id}; is this a clerical error?")
|
||||
|
||||
case Zone.Vehicle.CanNotSpawn(zone, vehicle, reason) =>
|
||||
log.warning(
|
||||
s"${sessionFuncs.player.Name}'s ${vehicle.Definition.Name} can not spawn in ${zone.id} because $reason"
|
||||
)
|
||||
|
||||
case Zone.Vehicle.CanNotDespawn(zone, vehicle, reason) =>
|
||||
log.warning(
|
||||
s"${sessionFuncs.player.Name}'s ${vehicle.Definition.Name} can not deconstruct in ${zone.id} because $reason"
|
||||
)
|
||||
|
||||
case ICS.ZoneResponse(Some(zone)) =>
|
||||
sessionFuncs.zoning.handleZoneResponse(zone)
|
||||
|
||||
/* uncommon messages (once a session) */
|
||||
case ICS.ZonesResponse(zones) =>
|
||||
sessionFuncs.zoning.handleZonesResponse(zones)
|
||||
|
||||
case SessionActor.SetAvatar(avatar) =>
|
||||
sessionFuncs.handleSetAvatar(avatar)
|
||||
|
||||
case PlayerToken.LoginInfo(name, Zone.Nowhere, _) =>
|
||||
sessionFuncs.zoning.spawn.handleLoginInfoNowhere(name, sender())
|
||||
|
||||
case PlayerToken.LoginInfo(name, inZone, optionalSavedData) =>
|
||||
sessionFuncs.zoning.spawn.handleLoginInfoSomewhere(name, inZone, optionalSavedData, sender())
|
||||
|
||||
case PlayerToken.RestoreInfo(playerName, inZone, pos) =>
|
||||
sessionFuncs.zoning.spawn.handleLoginInfoRestore(playerName, inZone, pos, sender())
|
||||
|
||||
case PlayerToken.CanNotLogin(playerName, reason) =>
|
||||
sessionFuncs.zoning.spawn.handleLoginCanNot(playerName, reason)
|
||||
|
||||
case ReceiveAccountData(account) =>
|
||||
sessionFuncs.handleReceiveAccountData(account)
|
||||
|
||||
case AvatarActor.AvatarResponse(avatar) =>
|
||||
sessionFuncs.handleAvatarResponse(avatar)
|
||||
|
||||
case AvatarActor.AvatarLoginResponse(avatar) =>
|
||||
sessionFuncs.zoning.spawn.avatarLoginResponse(avatar)
|
||||
|
||||
case SessionActor.SetCurrentAvatar(tplayer, max_attempts, attempt) =>
|
||||
sessionFuncs.zoning.spawn.ReadyToSetCurrentAvatar(tplayer, max_attempts, attempt)
|
||||
|
||||
case SessionActor.SetConnectionState(state) =>
|
||||
sessionFuncs.connectionState = state
|
||||
|
||||
case SessionActor.AvatarLoadingSync(state) =>
|
||||
sessionFuncs.zoning.spawn.handleAvatarLoadingSync(state)
|
||||
|
||||
/* uncommon messages (utility, or once in a while) */
|
||||
case SessionActor.AvatarAwardMessageBundle(pkts, delay) =>
|
||||
sessionFuncs.zoning.spawn.performAvatarAwardMessageDelivery(pkts, delay)
|
||||
|
||||
case CommonMessages.Progress(rate, finishedAction, stepAction) =>
|
||||
sessionFuncs.setupProgressChange(rate, finishedAction, stepAction)
|
||||
|
||||
case SessionActor.ProgressEvent(delta, finishedAction, stepAction, tick) =>
|
||||
sessionFuncs.handleProgressChange(delta, finishedAction, stepAction, tick)
|
||||
|
||||
case CavernRotationService.CavernRotationServiceKey.Listing(listings) =>
|
||||
listings.head ! SendCavernRotationUpdates(context.self)
|
||||
|
||||
case LookupResult("propertyOverrideManager", endpoint) =>
|
||||
sessionFuncs.zoning.propertyOverrideManagerLoadOverrides(endpoint)
|
||||
|
||||
case SessionActor.UpdateIgnoredPlayers(msg) =>
|
||||
sessionFuncs.handleUpdateIgnoredPlayers(msg)
|
||||
|
||||
case SessionActor.UseCooldownRenewed(definition, _) =>
|
||||
sessionFuncs.handleUseCooldownRenew(definition)
|
||||
|
||||
case Deployment.CanDeploy(obj, state) =>
|
||||
sessionFuncs.vehicles.handleCanDeploy(obj, state)
|
||||
|
||||
case Deployment.CanUndeploy(obj, state) =>
|
||||
sessionFuncs.vehicles.handleCanUndeploy(obj, state)
|
||||
|
||||
case Deployment.CanNotChangeDeployment(obj, state, reason) =>
|
||||
sessionFuncs.vehicles.handleCanNotChangeDeployment(obj, state, reason)
|
||||
|
||||
/* rare messages */
|
||||
case ProximityUnit.StopAction(term, _) =>
|
||||
sessionFuncs.terminals.LocalStopUsingProximityUnit(term)
|
||||
|
||||
case SessionActor.Suicide() =>
|
||||
sessionFuncs.suicide(sessionFuncs.player)
|
||||
|
||||
case SessionActor.Recall() =>
|
||||
sessionFuncs.zoning.handleRecall()
|
||||
|
||||
case SessionActor.InstantAction() =>
|
||||
sessionFuncs.zoning.handleInstantAction()
|
||||
|
||||
case SessionActor.Quit() =>
|
||||
sessionFuncs.zoning.handleQuit()
|
||||
|
||||
case ICS.DroppodLaunchDenial(errorCode, _) =>
|
||||
sessionFuncs.zoning.handleDroppodLaunchDenial(errorCode)
|
||||
|
||||
case ICS.DroppodLaunchConfirmation(zone, position) =>
|
||||
sessionFuncs.zoning.LoadZoneLaunchDroppod(zone, position)
|
||||
|
||||
case SessionActor.PlayerFailedToLoad(tplayer) =>
|
||||
sessionFuncs.failWithError(s"${tplayer.Name} failed to load anywhere")
|
||||
|
||||
/* csr only */
|
||||
case SessionActor.SetSpeed(speed) =>
|
||||
sessionFuncs.handleSetSpeed(speed)
|
||||
|
||||
case SessionActor.SetFlying(isFlying) =>
|
||||
sessionFuncs.handleSetFlying(isFlying)
|
||||
|
||||
case SessionActor.SetSpectator(isSpectator) =>
|
||||
sessionFuncs.handleSetSpectator(isSpectator)
|
||||
|
||||
case SessionActor.Kick(player, time) =>
|
||||
sessionFuncs.handleKick(player, time)
|
||||
|
||||
case SessionActor.SetZone(zoneId, position) =>
|
||||
sessionFuncs.zoning.handleSetZone(zoneId, position)
|
||||
|
||||
case SessionActor.SetPosition(position) =>
|
||||
sessionFuncs.zoning.spawn.handleSetPosition(position)
|
||||
|
||||
case SessionActor.SetSilenced(silenced) =>
|
||||
sessionFuncs.handleSilenced(silenced)
|
||||
|
||||
/* catch these messages */
|
||||
case _: ProximityUnit.Action => ;
|
||||
|
||||
case _: Zone.Vehicle.HasSpawned => ;
|
||||
|
||||
case _: Zone.Vehicle.HasDespawned => ;
|
||||
|
||||
case Zone.Deployable.IsDismissed(obj: TurretDeployable) => //only if target deployable was never fully introduced
|
||||
TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(sessionFuncs.continent.GUID, obj))
|
||||
|
||||
case Zone.Deployable.IsDismissed(obj) => //only if target deployable was never fully introduced
|
||||
TaskWorkflow.execute(GUIDTask.unregisterObject(sessionFuncs.continent.GUID, obj))
|
||||
|
||||
case msg: Containable.ItemPutInSlot =>
|
||||
log.debug(s"ItemPutInSlot: $msg")
|
||||
|
||||
case msg: Containable.CanNotPutItemInSlot =>
|
||||
log.debug(s"CanNotPutItemInSlot: $msg")
|
||||
|
||||
case default =>
|
||||
log.warning(s"Invalid packet class received: $default from ${sender()}")
|
||||
private def startHeartbeat(): Unit = {
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
clientKeepAlive.cancel()
|
||||
clientKeepAlive = context.system.scheduler.scheduleWithFixedDelay(
|
||||
initialDelay = 0.seconds,
|
||||
delay = 500.milliseconds,
|
||||
context.self,
|
||||
SessionActor.PokeClient()
|
||||
)
|
||||
}
|
||||
|
||||
private def handleGamePkt: PlanetSideGamePacket => Unit = {
|
||||
case packet: ConnectToWorldRequestMessage =>
|
||||
sessionFuncs.handleConnectToWorldRequest(packet)
|
||||
private def inTheGame: Receive = {
|
||||
/* used for the game's heartbeat*/
|
||||
case SessionActor.PokeClient() =>
|
||||
middlewareActor ! MiddlewareActor.Send(KeepAliveMessage())
|
||||
|
||||
case packet: MountVehicleCargoMsg =>
|
||||
sessionFuncs.vehicles.handleMountVehicleCargo(packet)
|
||||
|
||||
case packet: DismountVehicleCargoMsg =>
|
||||
sessionFuncs.vehicles.handleDismountVehicleCargo(packet)
|
||||
|
||||
case packet: CharacterCreateRequestMessage =>
|
||||
sessionFuncs.handleCharacterCreateRequest(packet)
|
||||
|
||||
case packet: CharacterRequestMessage =>
|
||||
sessionFuncs.handleCharacterRequest(packet)
|
||||
|
||||
case _: KeepAliveMessage =>
|
||||
sessionFuncs.keepAliveFunc()
|
||||
|
||||
case packet: BeginZoningMessage =>
|
||||
sessionFuncs.zoning.handleBeginZoning(packet)
|
||||
|
||||
case packet: PlayerStateMessageUpstream =>
|
||||
sessionFuncs.handlePlayerStateUpstream(packet)
|
||||
|
||||
case packet: ChildObjectStateMessage =>
|
||||
sessionFuncs.vehicles.handleChildObjectState(packet)
|
||||
|
||||
case packet: VehicleStateMessage =>
|
||||
sessionFuncs.vehicles.handleVehicleState(packet)
|
||||
|
||||
case packet: VehicleSubStateMessage =>
|
||||
sessionFuncs.vehicles.handleVehicleSubState(packet)
|
||||
|
||||
case packet: FrameVehicleStateMessage =>
|
||||
sessionFuncs.vehicles.handleFrameVehicleState(packet)
|
||||
|
||||
case packet: ProjectileStateMessage =>
|
||||
sessionFuncs.shooting.handleProjectileState(packet)
|
||||
|
||||
case packet: LongRangeProjectileInfoMessage =>
|
||||
sessionFuncs.shooting.handleLongRangeProjectileState(packet)
|
||||
|
||||
case packet: ReleaseAvatarRequestMessage =>
|
||||
sessionFuncs.zoning.spawn.handleReleaseAvatarRequest(packet)
|
||||
|
||||
case packet: SpawnRequestMessage =>
|
||||
sessionFuncs.zoning.spawn.handleSpawnRequest(packet)
|
||||
|
||||
case packet: ChatMsg =>
|
||||
sessionFuncs.handleChat(packet)
|
||||
|
||||
case packet: SetChatFilterMessage =>
|
||||
sessionFuncs.handleChatFilter(packet)
|
||||
|
||||
case packet: VoiceHostRequest =>
|
||||
sessionFuncs.handleVoiceHostRequest(packet)
|
||||
|
||||
case packet: VoiceHostInfo =>
|
||||
sessionFuncs.handleVoiceHostInfo(packet)
|
||||
|
||||
case packet: ChangeAmmoMessage =>
|
||||
sessionFuncs.shooting.handleChangeAmmo(packet)
|
||||
|
||||
case packet: ChangeFireModeMessage =>
|
||||
sessionFuncs.shooting.handleChangeFireMode(packet)
|
||||
|
||||
case packet: ChangeFireStateMessage_Start =>
|
||||
sessionFuncs.shooting.handleChangeFireStateStart(packet)
|
||||
|
||||
case packet: ChangeFireStateMessage_Stop =>
|
||||
sessionFuncs.shooting.handleChangeFireStateStop(packet)
|
||||
|
||||
case packet: EmoteMsg =>
|
||||
sessionFuncs.handleEmote(packet)
|
||||
|
||||
case packet: DropItemMessage =>
|
||||
sessionFuncs.handleDropItem(packet)
|
||||
|
||||
case packet: PickupItemMessage =>
|
||||
sessionFuncs.handlePickupItem(packet)
|
||||
|
||||
case packet: ReloadMessage =>
|
||||
sessionFuncs.shooting.handleReload(packet)
|
||||
|
||||
case packet: ObjectHeldMessage =>
|
||||
sessionFuncs.handleObjectHeld(packet)
|
||||
|
||||
case packet: AvatarJumpMessage =>
|
||||
sessionFuncs.handleAvatarJump(packet)
|
||||
|
||||
case packet: ZipLineMessage =>
|
||||
sessionFuncs.handleZipLine(packet)
|
||||
|
||||
case packet: RequestDestroyMessage =>
|
||||
sessionFuncs.handleRequestDestroy(packet)
|
||||
|
||||
case packet: MoveItemMessage =>
|
||||
sessionFuncs.handleMoveItem(packet)
|
||||
|
||||
case packet: LootItemMessage =>
|
||||
sessionFuncs.handleLootItem(packet)
|
||||
|
||||
case packet: AvatarImplantMessage =>
|
||||
sessionFuncs.handleAvatarImplant(packet)
|
||||
|
||||
case packet: UseItemMessage =>
|
||||
sessionFuncs.handleUseItem(packet)
|
||||
|
||||
case packet: UnuseItemMessage =>
|
||||
sessionFuncs.handleUnuseItem(packet)
|
||||
|
||||
case packet: ProximityTerminalUseMessage =>
|
||||
sessionFuncs.terminals.handleProximityTerminalUse(packet)
|
||||
|
||||
case packet: DeployObjectMessage =>
|
||||
sessionFuncs.handleDeployObject(packet)
|
||||
|
||||
case packet: GenericObjectActionMessage =>
|
||||
sessionFuncs.handleGenericObjectAction(packet)
|
||||
|
||||
case packet: GenericObjectActionAtPositionMessage =>
|
||||
sessionFuncs.handleGenericObjectActionAtPosition(packet)
|
||||
|
||||
case packet: GenericObjectStateMsg =>
|
||||
sessionFuncs.handleGenericObjectState(packet)
|
||||
|
||||
case packet: GenericActionMessage =>
|
||||
sessionFuncs.handleGenericAction(packet)
|
||||
|
||||
case packet: ItemTransactionMessage =>
|
||||
sessionFuncs.terminals.handleItemTransaction(packet)
|
||||
|
||||
case packet: FavoritesRequest =>
|
||||
sessionFuncs.handleFavoritesRequest(packet)
|
||||
|
||||
case packet: WeaponDelayFireMessage =>
|
||||
sessionFuncs.shooting.handleWeaponDelayFire(packet)
|
||||
|
||||
case packet: WeaponDryFireMessage =>
|
||||
sessionFuncs.shooting.handleWeaponDryFire(packet)
|
||||
|
||||
case packet: WeaponFireMessage =>
|
||||
sessionFuncs.shooting.handleWeaponFire(packet)
|
||||
|
||||
case packet: WeaponLazeTargetPositionMessage =>
|
||||
sessionFuncs.shooting.handleWeaponLazeTargetPosition(packet)
|
||||
|
||||
case packet: HitMessage =>
|
||||
sessionFuncs.shooting.handleDirectHit(packet)
|
||||
|
||||
case packet: SplashHitMessage =>
|
||||
sessionFuncs.shooting.handleSplashHit(packet)
|
||||
|
||||
case packet: LashMessage =>
|
||||
sessionFuncs.shooting.handleLashHit(packet)
|
||||
|
||||
case packet: AIDamage =>
|
||||
sessionFuncs.shooting.handleAIDamage(packet)
|
||||
|
||||
case packet: AvatarFirstTimeEventMessage =>
|
||||
sessionFuncs.handleAvatarFirstTimeEvent(packet)
|
||||
|
||||
case packet: WarpgateRequest =>
|
||||
sessionFuncs.zoning.handleWarpgateRequest(packet)
|
||||
|
||||
case packet: MountVehicleMsg =>
|
||||
sessionFuncs.vehicles.handleMountVehicle(packet)
|
||||
|
||||
case packet: DismountVehicleMsg =>
|
||||
sessionFuncs.vehicles.handleDismountVehicle(packet)
|
||||
|
||||
case packet: DeployRequestMessage =>
|
||||
sessionFuncs.vehicles.handleDeployRequest(packet)
|
||||
|
||||
case packet: AvatarGrenadeStateMessage =>
|
||||
sessionFuncs.shooting.handleAvatarGrenadeState(packet)
|
||||
|
||||
case packet: SquadDefinitionActionMessage =>
|
||||
sessionFuncs.squad.handleSquadDefinitionAction(packet)
|
||||
|
||||
case packet: SquadMembershipRequest =>
|
||||
sessionFuncs.squad.handleSquadMemberRequest(packet)
|
||||
|
||||
case packet: SquadWaypointRequest =>
|
||||
sessionFuncs.squad.handleSquadWaypointRequest(packet)
|
||||
|
||||
case packet: GenericCollisionMsg =>
|
||||
sessionFuncs.handleGenericCollision(packet)
|
||||
|
||||
case packet: BugReportMessage =>
|
||||
sessionFuncs.handleBugReport(packet)
|
||||
|
||||
case packet: BindPlayerMessage =>
|
||||
sessionFuncs.handleBindPlayer(packet)
|
||||
|
||||
case packet: PlanetsideAttributeMessage =>
|
||||
sessionFuncs.handlePlanetsideAttribute(packet)
|
||||
|
||||
case packet: FacilityBenefitShieldChargeRequestMessage =>
|
||||
sessionFuncs.handleFacilityBenefitShieldChargeRequest(packet)
|
||||
|
||||
case packet: BattleplanMessage =>
|
||||
sessionFuncs.handleBattleplan(packet)
|
||||
|
||||
case packet: CreateShortcutMessage =>
|
||||
sessionFuncs.handleCreateShortcut(packet)
|
||||
|
||||
case packet: ChangeShortcutBankMessage =>
|
||||
sessionFuncs.handleChangeShortcutBank(packet)
|
||||
|
||||
case packet: FriendsRequest =>
|
||||
sessionFuncs.handleFriendRequest(packet)
|
||||
|
||||
case packet: DroppodLaunchRequestMessage =>
|
||||
sessionFuncs.zoning.handleDroppodLaunchRequest(packet)
|
||||
|
||||
case packet: InvalidTerrainMessage =>
|
||||
sessionFuncs.handleInvalidTerrain(packet)
|
||||
|
||||
case packet: ActionCancelMessage =>
|
||||
sessionFuncs.handleActionCancel(packet)
|
||||
|
||||
case packet: TradeMessage =>
|
||||
sessionFuncs.handleTrade(packet)
|
||||
|
||||
case packet: DisplayedAwardMessage =>
|
||||
sessionFuncs.handleDisplayedAward(packet)
|
||||
|
||||
case packet: ObjectDetectedMessage =>
|
||||
sessionFuncs.handleObjectDetected(packet)
|
||||
|
||||
case packet: TargetingImplantRequest =>
|
||||
sessionFuncs.handleTargetingImplantRequest(packet)
|
||||
|
||||
case packet: HitHint =>
|
||||
sessionFuncs.handleHitHint(packet)
|
||||
|
||||
case _: OutfitRequest => ()
|
||||
|
||||
case pkt =>
|
||||
log.warning(s"Unhandled GamePacket $pkt")
|
||||
case packet =>
|
||||
logic.parse(sender())(packet)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,23 +23,25 @@ trait CommonSessionInterfacingFunctionality {
|
|||
|
||||
protected def context: ActorContext
|
||||
|
||||
protected def sessionData: SessionData
|
||||
protected def sessionLogic: SessionLogic
|
||||
|
||||
protected def session: Session = sessionData.session
|
||||
protected def session: Session = sessionLogic.session
|
||||
|
||||
protected def session_=(newsession: Session): Unit = sessionData.session_=(newsession)
|
||||
protected def session_=(newsession: Session): Unit = sessionLogic.session_=(newsession)
|
||||
|
||||
protected def account: Account = sessionData.account
|
||||
protected def account: Account = sessionLogic.account
|
||||
|
||||
protected def continent: Zone = sessionData.continent
|
||||
protected def continent: Zone = sessionLogic.continent
|
||||
|
||||
protected def player: Player = sessionData.player
|
||||
protected def player: Player = sessionLogic.player
|
||||
|
||||
protected def avatar: Avatar = sessionData.avatar
|
||||
protected def avatar: Avatar = sessionLogic.avatar
|
||||
|
||||
protected def log: Logger = sessionData.log
|
||||
protected def log: Logger = sessionLogic.log
|
||||
|
||||
protected def sendResponse(pkt: PlanetSideGamePacket): Unit = sessionData.sendResponse(pkt)
|
||||
protected def sendResponse(pkt: PlanetSideGamePacket): Unit = sessionLogic.sendResponse(pkt)
|
||||
|
||||
protected[session] def stop(): Unit = { /* to override */ }
|
||||
protected[support] def actionsToCancel(): Unit = { /* to override */ }
|
||||
|
||||
protected[support] def stop(): Unit = { /* to override */ }
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,496 @@
|
|||
// Copyright (c) 2024 PSForever
|
||||
package net.psforever.actors.session.support
|
||||
|
||||
import akka.actor.Actor.Receive
|
||||
import akka.actor.{ActorContext, ActorRef, typed}
|
||||
import net.psforever.actors.net.MiddlewareActor
|
||||
import net.psforever.actors.session.{AvatarActor, SessionActor}
|
||||
import net.psforever.objects.TurretDeployable
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
import net.psforever.objects.serverobject.containable.Containable
|
||||
import net.psforever.objects.serverobject.deploy.Deployment
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.{AIDamage, ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarGrenadeStateMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BeginZoningMessage, BindPlayerMessage, BugReportMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ChatMsg, ChildObjectStateMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DeployRequestMessage, DismountVehicleCargoMsg, DismountVehicleMsg, DisplayedAwardMessage, DropItemMessage, DroppodLaunchRequestMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FavoritesRequest, FrameVehicleStateMessage, FriendsRequest, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, HitMessage, InvalidTerrainMessage, ItemTransactionMessage, KeepAliveMessage, LashMessage, LongRangeProjectileInfoMessage, LootItemMessage, MountVehicleCargoMsg, MountVehicleMsg, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitRequest, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, ProjectileStateMessage, ProximityTerminalUseMessage, ReleaseAvatarRequestMessage, ReloadMessage, RequestDestroyMessage, SetChatFilterMessage, SpawnRequestMessage, SplashHitMessage, SquadDefinitionActionMessage, SquadMembershipRequest, SquadWaypointRequest, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UseItemMessage, VehicleStateMessage, VehicleSubStateMessage, VoiceHostInfo, VoiceHostRequest, WarpgateRequest, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage, ZipLineMessage}
|
||||
import net.psforever.services.{InterstellarClusterService => ICS}
|
||||
import net.psforever.services.CavernRotationService
|
||||
import net.psforever.services.CavernRotationService.SendCavernRotationUpdates
|
||||
import net.psforever.services.ServiceManager.LookupResult
|
||||
import net.psforever.services.account.{PlayerToken, ReceiveAccountData}
|
||||
import net.psforever.services.avatar.AvatarServiceResponse
|
||||
import net.psforever.services.galaxy.GalaxyServiceResponse
|
||||
import net.psforever.services.local.LocalServiceResponse
|
||||
import net.psforever.services.teamwork.SquadServiceResponse
|
||||
import net.psforever.services.vehicle.VehicleServiceResponse
|
||||
import net.psforever.util.Config
|
||||
|
||||
class NormalUser(
|
||||
val middlewareActor: typed.ActorRef[MiddlewareActor.Command],
|
||||
implicit val context: ActorContext
|
||||
) extends SessionLogic {
|
||||
def parse(sender: ActorRef): Receive = {
|
||||
/* really common messages (very frequently, every life) */
|
||||
case packet: PlanetSideGamePacket =>
|
||||
handleGamePkt(packet)
|
||||
|
||||
case AvatarServiceResponse(toChannel, guid, reply) =>
|
||||
avatarResponse.handle(toChannel, guid, reply)
|
||||
|
||||
case GalaxyServiceResponse(_, reply) =>
|
||||
galaxyResponseHandlers.handle(reply)
|
||||
|
||||
case LocalServiceResponse(toChannel, guid, reply) =>
|
||||
localResponse.handle(toChannel, guid, reply)
|
||||
|
||||
case Mountable.MountMessages(tplayer, reply) =>
|
||||
mountResponse.handle(tplayer, reply)
|
||||
|
||||
case SquadServiceResponse(_, excluded, response) =>
|
||||
squad.handle(response, excluded)
|
||||
|
||||
case Terminal.TerminalMessage(tplayer, msg, order) =>
|
||||
terminals.handle(tplayer, msg, order)
|
||||
|
||||
case VehicleServiceResponse(toChannel, guid, reply) =>
|
||||
vehicleResponseOperations.handle(toChannel, guid, reply)
|
||||
|
||||
case SessionActor.PokeClient() =>
|
||||
sendResponse(KeepAliveMessage())
|
||||
|
||||
case SessionActor.SendResponse(packet) =>
|
||||
sendResponse(packet)
|
||||
|
||||
case SessionActor.CharSaved =>
|
||||
general.renewCharSavedTimer(
|
||||
Config.app.game.savedMsg.interruptedByAction.fixed,
|
||||
Config.app.game.savedMsg.interruptedByAction.variable
|
||||
)
|
||||
|
||||
case SessionActor.CharSavedMsg =>
|
||||
general.displayCharSavedMsgThenRenewTimer(
|
||||
Config.app.game.savedMsg.renewal.fixed,
|
||||
Config.app.game.savedMsg.renewal.variable
|
||||
)
|
||||
|
||||
/* common messages (maybe once every respawn) */
|
||||
case ICS.SpawnPointResponse(response) =>
|
||||
zoning.handleSpawnPointResponse(response)
|
||||
|
||||
case SessionActor.NewPlayerLoaded(tplayer) =>
|
||||
zoning.spawn.handleNewPlayerLoaded(tplayer)
|
||||
|
||||
case SessionActor.PlayerLoaded(tplayer) =>
|
||||
zoning.spawn.handlePlayerLoaded(tplayer)
|
||||
|
||||
case Zone.Population.PlayerHasLeft(zone, None) =>
|
||||
log.debug(s"PlayerHasLeft: ${player.Name} does not have a body on ${zone.id}")
|
||||
|
||||
case Zone.Population.PlayerHasLeft(zone, Some(tplayer)) =>
|
||||
if (tplayer.isAlive) {
|
||||
log.info(s"${tplayer.Name} has left zone ${zone.id}")
|
||||
}
|
||||
|
||||
case Zone.Population.PlayerCanNotSpawn(zone, tplayer) =>
|
||||
log.warn(s"${tplayer.Name} can not spawn in zone ${zone.id}; why?")
|
||||
|
||||
case Zone.Population.PlayerAlreadySpawned(zone, tplayer) =>
|
||||
log.warn(s"${tplayer.Name} is already spawned on zone ${zone.id}; is this a clerical error?")
|
||||
|
||||
case Zone.Vehicle.CanNotSpawn(zone, vehicle, reason) =>
|
||||
log.warn(
|
||||
s"${player.Name}'s ${vehicle.Definition.Name} can not spawn in ${zone.id} because $reason"
|
||||
)
|
||||
|
||||
case Zone.Vehicle.CanNotDespawn(zone, vehicle, reason) =>
|
||||
log.warn(
|
||||
s"${player.Name}'s ${vehicle.Definition.Name} can not deconstruct in ${zone.id} because $reason"
|
||||
)
|
||||
|
||||
case ICS.ZoneResponse(Some(zone)) =>
|
||||
zoning.handleZoneResponse(zone)
|
||||
|
||||
/* uncommon messages (once a session) */
|
||||
case ICS.ZonesResponse(zones) =>
|
||||
zoning.handleZonesResponse(zones)
|
||||
|
||||
case SessionActor.SetAvatar(avatar) =>
|
||||
general.handleSetAvatar(avatar)
|
||||
|
||||
case PlayerToken.LoginInfo(name, Zone.Nowhere, _) =>
|
||||
zoning.spawn.handleLoginInfoNowhere(name, sender)
|
||||
|
||||
case PlayerToken.LoginInfo(name, inZone, optionalSavedData) =>
|
||||
zoning.spawn.handleLoginInfoSomewhere(name, inZone, optionalSavedData, sender)
|
||||
|
||||
case PlayerToken.RestoreInfo(playerName, inZone, pos) =>
|
||||
zoning.spawn.handleLoginInfoRestore(playerName, inZone, pos, sender)
|
||||
|
||||
case PlayerToken.CanNotLogin(playerName, reason) =>
|
||||
zoning.spawn.handleLoginCanNot(playerName, reason)
|
||||
|
||||
case ReceiveAccountData(account) =>
|
||||
general.handleReceiveAccountData(account)
|
||||
|
||||
case AvatarActor.AvatarResponse(avatar) =>
|
||||
general.handleAvatarResponse(avatar)
|
||||
|
||||
case AvatarActor.AvatarLoginResponse(avatar) =>
|
||||
zoning.spawn.avatarLoginResponse(avatar)
|
||||
|
||||
case SessionActor.SetCurrentAvatar(tplayer, max_attempts, attempt) =>
|
||||
zoning.spawn.ReadyToSetCurrentAvatar(tplayer, max_attempts, attempt)
|
||||
|
||||
case SessionActor.SetConnectionState(state) =>
|
||||
connectionState = state
|
||||
|
||||
case SessionActor.AvatarLoadingSync(state) =>
|
||||
zoning.spawn.handleAvatarLoadingSync(state)
|
||||
|
||||
/* uncommon messages (utility, or once in a while) */
|
||||
case ZoningOperations.AvatarAwardMessageBundle(pkts, delay) =>
|
||||
zoning.spawn.performAvatarAwardMessageDelivery(pkts, delay)
|
||||
|
||||
case CommonMessages.ProgressEvent(delta, finishedAction, stepAction, tick) =>
|
||||
general.handleProgressChange(delta, finishedAction, stepAction, tick)
|
||||
|
||||
case CommonMessages.Progress(rate, finishedAction, stepAction) =>
|
||||
general.setupProgressChange(rate, finishedAction, stepAction)
|
||||
|
||||
case CavernRotationService.CavernRotationServiceKey.Listing(listings) =>
|
||||
listings.head ! SendCavernRotationUpdates(context.self)
|
||||
|
||||
case LookupResult("propertyOverrideManager", endpoint) =>
|
||||
zoning.propertyOverrideManagerLoadOverrides(endpoint)
|
||||
|
||||
case SessionActor.UpdateIgnoredPlayers(msg) =>
|
||||
galaxyResponseHandlers.handleUpdateIgnoredPlayers(msg)
|
||||
|
||||
case SessionActor.UseCooldownRenewed(definition, _) =>
|
||||
general.handleUseCooldownRenew(definition)
|
||||
|
||||
case Deployment.CanDeploy(obj, state) =>
|
||||
vehicles.handleCanDeploy(obj, state)
|
||||
|
||||
case Deployment.CanUndeploy(obj, state) =>
|
||||
vehicles.handleCanUndeploy(obj, state)
|
||||
|
||||
case Deployment.CanNotChangeDeployment(obj, state, reason) =>
|
||||
vehicles.handleCanNotChangeDeployment(obj, state, reason)
|
||||
|
||||
/* rare messages */
|
||||
case ProximityUnit.StopAction(term, _) =>
|
||||
terminals.LocalStopUsingProximityUnit(term)
|
||||
|
||||
case SessionActor.Suicide() =>
|
||||
general.suicide(player)
|
||||
|
||||
case SessionActor.Recall() =>
|
||||
zoning.handleRecall()
|
||||
|
||||
case SessionActor.InstantAction() =>
|
||||
zoning.handleInstantAction()
|
||||
|
||||
case SessionActor.Quit() =>
|
||||
zoning.handleQuit()
|
||||
|
||||
case ICS.DroppodLaunchDenial(errorCode, _) =>
|
||||
zoning.handleDroppodLaunchDenial(errorCode)
|
||||
|
||||
case ICS.DroppodLaunchConfirmation(zone, position) =>
|
||||
zoning.LoadZoneLaunchDroppod(zone, position)
|
||||
|
||||
case SessionActor.PlayerFailedToLoad(tplayer) =>
|
||||
failWithError(s"${tplayer.Name} failed to load anywhere")
|
||||
|
||||
/* csr only */
|
||||
case SessionActor.SetSpeed(speed) =>
|
||||
general.handleSetSpeed(speed)
|
||||
|
||||
case SessionActor.SetFlying(isFlying) =>
|
||||
general.handleSetFlying(isFlying)
|
||||
|
||||
case SessionActor.SetSpectator(isSpectator) =>
|
||||
general.handleSetSpectator(isSpectator)
|
||||
|
||||
case SessionActor.Kick(player, time) =>
|
||||
general.handleKick(player, time)
|
||||
|
||||
case SessionActor.SetZone(zoneId, position) =>
|
||||
zoning.handleSetZone(zoneId, position)
|
||||
|
||||
case SessionActor.SetPosition(position) =>
|
||||
zoning.spawn.handleSetPosition(position)
|
||||
|
||||
case SessionActor.SetSilenced(silenced) =>
|
||||
general.handleSilenced(silenced)
|
||||
|
||||
/* catch these messages */
|
||||
case _: ProximityUnit.Action => ;
|
||||
|
||||
case _: Zone.Vehicle.HasSpawned => ;
|
||||
|
||||
case _: Zone.Vehicle.HasDespawned => ;
|
||||
|
||||
case Zone.Deployable.IsDismissed(obj: TurretDeployable) => //only if target deployable was never fully introduced
|
||||
TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(continent.GUID, obj))
|
||||
|
||||
case Zone.Deployable.IsDismissed(obj) => //only if target deployable was never fully introduced
|
||||
TaskWorkflow.execute(GUIDTask.unregisterObject(continent.GUID, obj))
|
||||
|
||||
case msg: Containable.ItemPutInSlot =>
|
||||
log.debug(s"ItemPutInSlot: $msg")
|
||||
|
||||
case msg: Containable.CanNotPutItemInSlot =>
|
||||
log.debug(s"CanNotPutItemInSlot: $msg")
|
||||
|
||||
case default =>
|
||||
log.warn(s"Invalid packet class received: $default from $sender")
|
||||
}
|
||||
|
||||
private def handleGamePkt: PlanetSideGamePacket => Unit = {
|
||||
case packet: ConnectToWorldRequestMessage =>
|
||||
general.handleConnectToWorldRequest(packet)
|
||||
|
||||
case packet: MountVehicleCargoMsg =>
|
||||
vehicles.handleMountVehicleCargo(packet)
|
||||
|
||||
case packet: DismountVehicleCargoMsg =>
|
||||
vehicles.handleDismountVehicleCargo(packet)
|
||||
|
||||
case packet: CharacterCreateRequestMessage =>
|
||||
general.handleCharacterCreateRequest(packet)
|
||||
|
||||
case packet: CharacterRequestMessage =>
|
||||
general.handleCharacterRequest(packet)
|
||||
|
||||
case _: KeepAliveMessage =>
|
||||
keepAliveFunc()
|
||||
|
||||
case packet: BeginZoningMessage =>
|
||||
zoning.handleBeginZoning(packet)
|
||||
|
||||
case packet: PlayerStateMessageUpstream =>
|
||||
general.handlePlayerStateUpstream(packet)
|
||||
|
||||
case packet: ChildObjectStateMessage =>
|
||||
vehicles.handleChildObjectState(packet)
|
||||
|
||||
case packet: VehicleStateMessage =>
|
||||
vehicles.handleVehicleState(packet)
|
||||
|
||||
case packet: VehicleSubStateMessage =>
|
||||
vehicles.handleVehicleSubState(packet)
|
||||
|
||||
case packet: FrameVehicleStateMessage =>
|
||||
vehicles.handleFrameVehicleState(packet)
|
||||
|
||||
case packet: ProjectileStateMessage =>
|
||||
shooting.handleProjectileState(packet)
|
||||
|
||||
case packet: LongRangeProjectileInfoMessage =>
|
||||
shooting.handleLongRangeProjectileState(packet)
|
||||
|
||||
case packet: ReleaseAvatarRequestMessage =>
|
||||
zoning.spawn.handleReleaseAvatarRequest(packet)
|
||||
|
||||
case packet: SpawnRequestMessage =>
|
||||
zoning.spawn.handleSpawnRequest(packet)
|
||||
|
||||
case packet: ChatMsg =>
|
||||
general.handleChat(packet)
|
||||
|
||||
case packet: SetChatFilterMessage =>
|
||||
general.handleChatFilter(packet)
|
||||
|
||||
case packet: VoiceHostRequest =>
|
||||
general.handleVoiceHostRequest(packet)
|
||||
|
||||
case packet: VoiceHostInfo =>
|
||||
general.handleVoiceHostInfo(packet)
|
||||
|
||||
case packet: ChangeAmmoMessage =>
|
||||
shooting.handleChangeAmmo(packet)
|
||||
|
||||
case packet: ChangeFireModeMessage =>
|
||||
shooting.handleChangeFireMode(packet)
|
||||
|
||||
case packet: ChangeFireStateMessage_Start =>
|
||||
shooting.handleChangeFireStateStart(packet)
|
||||
|
||||
case packet: ChangeFireStateMessage_Stop =>
|
||||
shooting.handleChangeFireStateStop(packet)
|
||||
|
||||
case packet: EmoteMsg =>
|
||||
general.handleEmote(packet)
|
||||
|
||||
case packet: DropItemMessage =>
|
||||
general.handleDropItem(packet)
|
||||
|
||||
case packet: PickupItemMessage =>
|
||||
general.handlePickupItem(packet)
|
||||
|
||||
case packet: ReloadMessage =>
|
||||
shooting.handleReload(packet)
|
||||
|
||||
case packet: ObjectHeldMessage =>
|
||||
general.handleObjectHeld(packet)
|
||||
|
||||
case packet: AvatarJumpMessage =>
|
||||
general.handleAvatarJump(packet)
|
||||
|
||||
case packet: ZipLineMessage =>
|
||||
general.handleZipLine(packet)
|
||||
|
||||
case packet: RequestDestroyMessage =>
|
||||
general.handleRequestDestroy(packet)
|
||||
|
||||
case packet: MoveItemMessage =>
|
||||
general.handleMoveItem(packet)
|
||||
|
||||
case packet: LootItemMessage =>
|
||||
general.handleLootItem(packet)
|
||||
|
||||
case packet: AvatarImplantMessage =>
|
||||
general.handleAvatarImplant(packet)
|
||||
|
||||
case packet: UseItemMessage =>
|
||||
general.handleUseItem(packet)
|
||||
|
||||
case packet: UnuseItemMessage =>
|
||||
general.handleUnuseItem(packet)
|
||||
|
||||
case packet: ProximityTerminalUseMessage =>
|
||||
terminals.handleProximityTerminalUse(packet)
|
||||
|
||||
case packet: DeployObjectMessage =>
|
||||
general.handleDeployObject(packet)
|
||||
|
||||
case packet: GenericObjectActionMessage =>
|
||||
general.handleGenericObjectAction(packet)
|
||||
|
||||
case packet: GenericObjectActionAtPositionMessage =>
|
||||
general.handleGenericObjectActionAtPosition(packet)
|
||||
|
||||
case packet: GenericObjectStateMsg =>
|
||||
general.handleGenericObjectState(packet)
|
||||
|
||||
case packet: GenericActionMessage =>
|
||||
general.handleGenericAction(packet)
|
||||
|
||||
case packet: ItemTransactionMessage =>
|
||||
terminals.handleItemTransaction(packet)
|
||||
|
||||
case packet: FavoritesRequest =>
|
||||
general.handleFavoritesRequest(packet)
|
||||
|
||||
case packet: WeaponDelayFireMessage =>
|
||||
shooting.handleWeaponDelayFire(packet)
|
||||
|
||||
case packet: WeaponDryFireMessage =>
|
||||
shooting.handleWeaponDryFire(packet)
|
||||
|
||||
case packet: WeaponFireMessage =>
|
||||
shooting.handleWeaponFire(packet)
|
||||
|
||||
case packet: WeaponLazeTargetPositionMessage =>
|
||||
shooting.handleWeaponLazeTargetPosition(packet)
|
||||
|
||||
case packet: HitMessage =>
|
||||
shooting.handleDirectHit(packet)
|
||||
|
||||
case packet: SplashHitMessage =>
|
||||
shooting.handleSplashHit(packet)
|
||||
|
||||
case packet: LashMessage =>
|
||||
shooting.handleLashHit(packet)
|
||||
|
||||
case packet: AIDamage =>
|
||||
shooting.handleAIDamage(packet)
|
||||
|
||||
case packet: AvatarFirstTimeEventMessage =>
|
||||
general.handleAvatarFirstTimeEvent(packet)
|
||||
|
||||
case packet: WarpgateRequest =>
|
||||
zoning.handleWarpgateRequest(packet)
|
||||
|
||||
case packet: MountVehicleMsg =>
|
||||
vehicles.handleMountVehicle(packet)
|
||||
|
||||
case packet: DismountVehicleMsg =>
|
||||
vehicles.handleDismountVehicle(packet)
|
||||
|
||||
case packet: DeployRequestMessage =>
|
||||
vehicles.handleDeployRequest(packet)
|
||||
|
||||
case packet: AvatarGrenadeStateMessage =>
|
||||
shooting.handleAvatarGrenadeState(packet)
|
||||
|
||||
case packet: SquadDefinitionActionMessage =>
|
||||
squad.handleSquadDefinitionAction(packet)
|
||||
|
||||
case packet: SquadMembershipRequest =>
|
||||
squad.handleSquadMemberRequest(packet)
|
||||
|
||||
case packet: SquadWaypointRequest =>
|
||||
squad.handleSquadWaypointRequest(packet)
|
||||
|
||||
case packet: GenericCollisionMsg =>
|
||||
general.handleGenericCollision(packet)
|
||||
|
||||
case packet: BugReportMessage =>
|
||||
general.handleBugReport(packet)
|
||||
|
||||
case packet: BindPlayerMessage =>
|
||||
general.handleBindPlayer(packet)
|
||||
|
||||
case packet: PlanetsideAttributeMessage =>
|
||||
general.handlePlanetsideAttribute(packet)
|
||||
|
||||
case packet: FacilityBenefitShieldChargeRequestMessage =>
|
||||
general.handleFacilityBenefitShieldChargeRequest(packet)
|
||||
|
||||
case packet: BattleplanMessage =>
|
||||
general.handleBattleplan(packet)
|
||||
|
||||
case packet: CreateShortcutMessage =>
|
||||
general.handleCreateShortcut(packet)
|
||||
|
||||
case packet: ChangeShortcutBankMessage =>
|
||||
general.handleChangeShortcutBank(packet)
|
||||
|
||||
case packet: FriendsRequest =>
|
||||
general.handleFriendRequest(packet)
|
||||
|
||||
case packet: DroppodLaunchRequestMessage =>
|
||||
zoning.handleDroppodLaunchRequest(packet)
|
||||
|
||||
case packet: InvalidTerrainMessage =>
|
||||
general.handleInvalidTerrain(packet)
|
||||
|
||||
case packet: ActionCancelMessage =>
|
||||
general.handleActionCancel(packet)
|
||||
|
||||
case packet: TradeMessage =>
|
||||
general.handleTrade(packet)
|
||||
|
||||
case packet: DisplayedAwardMessage =>
|
||||
general.handleDisplayedAward(packet)
|
||||
|
||||
case packet: ObjectDetectedMessage =>
|
||||
general.handleObjectDetected(packet)
|
||||
|
||||
case packet: TargetingImplantRequest =>
|
||||
general.handleTargetingImplantRequest(packet)
|
||||
|
||||
case packet: HitHint =>
|
||||
general.handleHitHint(packet)
|
||||
|
||||
case _: OutfitRequest => ()
|
||||
|
||||
case pkt =>
|
||||
log.warn(s"Unhandled GamePacket $pkt")
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package net.psforever.actors.session.support
|
|||
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
|
||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.objects.zones.exp
|
||||
|
|
@ -29,7 +30,7 @@ import net.psforever.util.Config
|
|||
import net.psforever.zones.Zones
|
||||
|
||||
class SessionAvatarHandlers(
|
||||
val sessionData: SessionData,
|
||||
val sessionLogic: SessionLogic,
|
||||
avatarActor: typed.ActorRef[AvatarActor.Command],
|
||||
chatActor: typed.ActorRef[ChatActor.Command],
|
||||
implicit val context: ActorContext
|
||||
|
|
@ -86,7 +87,7 @@ class SessionAvatarHandlers(
|
|||
val inDrawableRange = currentDistance <= maxRange
|
||||
val now = System.currentTimeMillis() //ms
|
||||
if (
|
||||
sessionData.zoning.zoningStatus != Zoning.Status.Deconstructing &&
|
||||
sessionLogic.zoning.zoningStatus != Zoning.Status.Deconstructing &&
|
||||
!isNotRendered && inDrawableRange
|
||||
) {
|
||||
//conditions where visibility is assured
|
||||
|
|
@ -95,7 +96,7 @@ class SessionAvatarHandlers(
|
|||
lazy val targetDelay = {
|
||||
val populationOver = math.max(
|
||||
0,
|
||||
sessionData.localSector.livePlayerList.size - drawConfig.populationThreshold
|
||||
sessionLogic.localSector.livePlayerList.size - drawConfig.populationThreshold
|
||||
)
|
||||
val distanceAdjustment = math.pow(populationOver / drawConfig.populationStep * drawConfig.rangeStep, 2) //sq.m
|
||||
val adjustedDistance = currentDistance + distanceAdjustment //sq.m
|
||||
|
|
@ -110,7 +111,7 @@ class SessionAvatarHandlers(
|
|||
(!lastMsg.contains(pstateToSave) &&
|
||||
(canSeeReallyFar ||
|
||||
currentDistance < drawConfig.rangeMin * drawConfig.rangeMin ||
|
||||
sessionData.canSeeReallyFar ||
|
||||
sessionLogic.general.canSeeReallyFar ||
|
||||
durationSince > targetDelay
|
||||
)
|
||||
)
|
||||
|
|
@ -164,11 +165,11 @@ class SessionAvatarHandlers(
|
|||
if isSameTarget && player.VisibleSlots.contains(slot) =>
|
||||
sendResponse(ObjectHeldMessage(guid, slot, unk1=true))
|
||||
//Stop using proximity terminals if player unholsters a weapon
|
||||
continent.GUID(sessionData.terminals.usingMedicalTerminal).collect {
|
||||
case term: Terminal with ProximityUnit => sessionData.terminals.StopUsingProximityUnit(term)
|
||||
continent.GUID(sessionLogic.terminals.usingMedicalTerminal).collect {
|
||||
case term: Terminal with ProximityUnit => sessionLogic.terminals.StopUsingProximityUnit(term)
|
||||
}
|
||||
if (sessionData.zoning.zoningStatus == Zoning.Status.Deconstructing) {
|
||||
sessionData.stopDeconstructing()
|
||||
if (sessionLogic.zoning.zoningStatus == Zoning.Status.Deconstructing) {
|
||||
sessionLogic.zoning.spawn.stopDeconstructing()
|
||||
}
|
||||
|
||||
case AvatarResponse.ObjectHeld(slot, _)
|
||||
|
|
@ -221,32 +222,28 @@ class SessionAvatarHandlers(
|
|||
|
||||
case AvatarResponse.HitHint(sourceGuid) if player.isAlive =>
|
||||
sendResponse(HitHint(sourceGuid, guid))
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg")
|
||||
|
||||
case AvatarResponse.DestroyDisplay(killer, victim, method, unk)
|
||||
if killer.CharId == avatar.id && killer.Faction != victim.Faction =>
|
||||
sendResponse(sessionData.destroyDisplayMessage(killer, victim, method, unk))
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg")
|
||||
|
||||
case AvatarResponse.Destroy(victim, killer, weapon, pos) =>
|
||||
// guid = victim // killer = killer
|
||||
sendResponse(DestroyMessage(victim, killer, weapon, pos))
|
||||
|
||||
case AvatarResponse.DestroyDisplay(killer, victim, method, unk) =>
|
||||
sendResponse(sessionData.destroyDisplayMessage(killer, victim, method, unk))
|
||||
sendResponse(destroyDisplayMessage(killer, victim, method, unk))
|
||||
|
||||
case AvatarResponse.TerminalOrderResult(terminalGuid, action, result)
|
||||
if result && (action == TransactionType.Buy || action == TransactionType.Loadout) =>
|
||||
sendResponse(ItemTransactionResultMessage(terminalGuid, action, result))
|
||||
sessionData.terminals.lastTerminalOrderFulfillment = true
|
||||
sessionLogic.terminals.lastTerminalOrderFulfillment = true
|
||||
AvatarActor.savePlayerData(player)
|
||||
sessionData.renewCharSavedTimer(
|
||||
sessionLogic.general.renewCharSavedTimer(
|
||||
Config.app.game.savedMsg.interruptedByAction.fixed,
|
||||
Config.app.game.savedMsg.interruptedByAction.variable
|
||||
)
|
||||
|
||||
case AvatarResponse.TerminalOrderResult(terminalGuid, action, result) =>
|
||||
sendResponse(ItemTransactionResultMessage(terminalGuid, action, result))
|
||||
sessionData.terminals.lastTerminalOrderFulfillment = true
|
||||
sessionLogic.terminals.lastTerminalOrderFulfillment = true
|
||||
|
||||
case AvatarResponse.ChangeExosuit(
|
||||
target,
|
||||
|
|
@ -358,7 +355,7 @@ class SessionAvatarHandlers(
|
|||
slot = 0
|
||||
))
|
||||
}
|
||||
sessionData.applyPurchaseTimersBeforePackingLoadout(player, player, holsters ++ inventory)
|
||||
sessionLogic.general.applyPurchaseTimersBeforePackingLoadout(player, player, holsters ++ inventory)
|
||||
DropLeftovers(player)(drops)
|
||||
|
||||
case AvatarResponse.ChangeLoadout(target, armor, exosuit, subtype, slot, _, oldHolsters, _, _, _, _) =>
|
||||
|
|
@ -389,10 +386,10 @@ class SessionAvatarHandlers(
|
|||
sendResponse(ObjectDeleteMessage(kguid, unk1=0))
|
||||
|
||||
case AvatarResponse.KitNotUsed(_, "") =>
|
||||
sessionData.kitToBeUsed = None
|
||||
sessionLogic.general.kitToBeUsed = None
|
||||
|
||||
case AvatarResponse.KitNotUsed(_, msg) =>
|
||||
sessionData.kitToBeUsed = None
|
||||
sessionLogic.general.kitToBeUsed = None
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_225, msg))
|
||||
|
||||
case AvatarResponse.UpdateKillsDeathsAssists(_, kda) =>
|
||||
|
|
@ -439,40 +436,40 @@ class SessionAvatarHandlers(
|
|||
adversarial.map {_.Name }.orElse { Some(s"a ${reason.getClass.getSimpleName}") }
|
||||
}.getOrElse { s"an unfortunate circumstance (probably ${player.Sex.pronounObject} own fault)" }
|
||||
log.info(s"${player.Name} has died, killed by $cause")
|
||||
if (sessionData.shooting.shotsWhileDead > 0) {
|
||||
if (sessionLogic.shooting.shotsWhileDead > 0) {
|
||||
log.warn(
|
||||
s"SHOTS_WHILE_DEAD: client of ${avatar.name} fired ${sessionData.shooting.shotsWhileDead} rounds while character was dead on server"
|
||||
s"SHOTS_WHILE_DEAD: client of ${avatar.name} fired ${sessionLogic.shooting.shotsWhileDead} rounds while character was dead on server"
|
||||
)
|
||||
sessionData.shooting.shotsWhileDead = 0
|
||||
sessionLogic.shooting.shotsWhileDead = 0
|
||||
}
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason(msg = "cancel")
|
||||
sessionData.renewCharSavedTimer(fixedLen = 1800L, varLen = 0L)
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason(msg = "cancel")
|
||||
sessionLogic.general.renewCharSavedTimer(fixedLen = 1800L, varLen = 0L)
|
||||
|
||||
//player state changes
|
||||
AvatarActor.updateToolDischargeFor(avatar)
|
||||
player.FreeHand.Equipment.foreach { item =>
|
||||
DropEquipmentFromInventory(player)(item)
|
||||
}
|
||||
sessionData.dropSpecialSlotItem()
|
||||
sessionData.toggleMaxSpecialState(enable = false)
|
||||
sessionData.keepAliveFunc = sessionData.zoning.NormalKeepAlive
|
||||
sessionData.zoning.zoningStatus = Zoning.Status.None
|
||||
sessionData.zoning.spawn.deadState = DeadState.Dead
|
||||
sessionLogic.general.dropSpecialSlotItem()
|
||||
sessionLogic.general.toggleMaxSpecialState(enable = false)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive
|
||||
sessionLogic.zoning.zoningStatus = Zoning.Status.None
|
||||
sessionLogic.zoning.spawn.deadState = DeadState.Dead
|
||||
continent.GUID(mount).collect { case obj: Vehicle =>
|
||||
sessionData.vehicles.ConditionalDriverVehicleControl(obj)
|
||||
sessionData.unaccessContainer(obj)
|
||||
sessionLogic.vehicles.ConditionalDriverVehicleControl(obj)
|
||||
sessionLogic.general.unaccessContainer(obj)
|
||||
}
|
||||
sessionData.playerActionsToCancel()
|
||||
sessionData.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.actionsToCancel()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
AvatarActor.savePlayerLocation(player)
|
||||
sessionData.zoning.spawn.shiftPosition = Some(player.Position)
|
||||
sessionLogic.zoning.spawn.shiftPosition = Some(player.Position)
|
||||
|
||||
//respawn
|
||||
val respawnTimer = 300.seconds
|
||||
sessionData.zoning.spawn.reviveTimer.cancel()
|
||||
sessionLogic.zoning.spawn.reviveTimer.cancel()
|
||||
if (player.death_by == 0) {
|
||||
sessionData.zoning.spawn.reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer) {
|
||||
sessionData.cluster ! ICS.GetRandomSpawnPoint(
|
||||
sessionLogic.zoning.spawn.reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer) {
|
||||
sessionLogic.cluster ! ICS.GetRandomSpawnPoint(
|
||||
Zones.sanctuaryZoneNumber(player.Faction),
|
||||
player.Faction,
|
||||
Seq(SpawnGroup.Sanctuary),
|
||||
|
|
@ -480,16 +477,16 @@ class SessionAvatarHandlers(
|
|||
)
|
||||
}
|
||||
} else {
|
||||
sessionData.zoning.spawn.HandleReleaseAvatar(player, continent)
|
||||
sessionLogic.zoning.spawn.HandleReleaseAvatar(player, continent)
|
||||
}
|
||||
|
||||
case AvatarResponse.Release(tplayer) if isNotSameTarget =>
|
||||
sessionData.zoning.spawn.DepictPlayerAsCorpse(tplayer)
|
||||
sessionLogic.zoning.spawn.DepictPlayerAsCorpse(tplayer)
|
||||
|
||||
case AvatarResponse.Revive(revivalTargetGuid) if resolvedPlayerGuid == revivalTargetGuid =>
|
||||
log.info(s"No time for rest, ${player.Name}. Back on your feet!")
|
||||
sessionData.zoning.spawn.reviveTimer.cancel()
|
||||
sessionData.zoning.spawn.deadState = DeadState.Alive
|
||||
sessionLogic.zoning.spawn.reviveTimer.cancel()
|
||||
sessionLogic.zoning.spawn.deadState = DeadState.Alive
|
||||
player.Revive
|
||||
val health = player.Health
|
||||
sendResponse(PlanetsideAttributeMessage(revivalTargetGuid, attribute_type=0, health))
|
||||
|
|
@ -517,7 +514,7 @@ class SessionAvatarHandlers(
|
|||
|
||||
case AvatarResponse.EnvironmentalDamage(_, _, _) =>
|
||||
//TODO damage marker?
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg")
|
||||
|
||||
case AvatarResponse.DropItem(pkt) if isNotSameTarget =>
|
||||
sendResponse(pkt)
|
||||
|
|
@ -530,7 +527,7 @@ class SessionAvatarHandlers(
|
|||
sendResponse(SetEmpireMessage(objectGuid, faction))
|
||||
|
||||
case AvatarResponse.DropSpecialItem() =>
|
||||
sessionData.dropSpecialSlotItem()
|
||||
sessionLogic.general.dropSpecialSlotItem()
|
||||
|
||||
case AvatarResponse.OxygenState(player, vehicle) =>
|
||||
sendResponse(OxygenStateMessage(
|
||||
|
|
@ -612,7 +609,7 @@ class SessionAvatarHandlers(
|
|||
//TODO squad services deactivated, participation trophy rewards for now - 11-20-2023
|
||||
//must be in a squad to earn experience
|
||||
val charId = player.CharId
|
||||
val squadUI = sessionData.squad.squadUI
|
||||
val squadUI = sessionLogic.squad.squadUI
|
||||
val participation = continent
|
||||
.Building(buildingId)
|
||||
.map { building =>
|
||||
|
|
@ -672,6 +669,45 @@ class SessionAvatarHandlers(
|
|||
Some(modifiedExp)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly format a `DestroyDisplayMessage` packet
|
||||
* given sufficient information about a target (victim) and an actor (killer).
|
||||
* For the packet, the `charId` field is important for determining distinction between players.
|
||||
* @param killer the killer's entry
|
||||
* @param victim the victim's entry
|
||||
* @param method the manner of death
|
||||
* @param unk na;
|
||||
* defaults to 121, the object id of `avatar`
|
||||
* @return a `DestroyDisplayMessage` packet that is properly formatted
|
||||
*/
|
||||
def destroyDisplayMessage(
|
||||
killer: SourceEntry,
|
||||
victim: SourceEntry,
|
||||
method: Int,
|
||||
unk: Int = 121
|
||||
): DestroyDisplayMessage = {
|
||||
val killerSeated = killer match {
|
||||
case obj: PlayerSource => obj.Seated
|
||||
case _ => false
|
||||
}
|
||||
val victimSeated = victim match {
|
||||
case obj: PlayerSource => obj.Seated
|
||||
case _ => false
|
||||
}
|
||||
new DestroyDisplayMessage(
|
||||
killer.Name,
|
||||
killer.CharId,
|
||||
killer.Faction,
|
||||
killerSeated,
|
||||
unk,
|
||||
method,
|
||||
victim.Name,
|
||||
victim.CharId,
|
||||
victim.Faction,
|
||||
victimSeated
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object SessionAvatarHandlers {
|
||||
|
|
|
|||
|
|
@ -2,21 +2,34 @@
|
|||
package net.psforever.actors.session.support
|
||||
|
||||
import akka.actor.{ActorContext, ActorRef, typed}
|
||||
import scala.concurrent.duration._
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.FriendsResponse
|
||||
import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage}
|
||||
//
|
||||
import net.psforever.actors.session.AvatarActor
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.packet.game.{AvatarDeadStateMessage, BroadcastWarpgateUpdateMessage, DeadState, HotSpotInfo => PacketHotSpotInfo, HotSpotUpdateMessage, ZoneInfoMessage, ZonePopulationUpdateMessage}
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.packet.game.{BroadcastWarpgateUpdateMessage, HotSpotInfo => PacketHotSpotInfo, HotSpotUpdateMessage, ZoneInfoMessage, ZonePopulationUpdateMessage}
|
||||
import net.psforever.services.galaxy.GalaxyResponse
|
||||
import net.psforever.types.{MemberAction, PlanetSideEmpire}
|
||||
|
||||
class SessionGalaxyHandlers(
|
||||
val sessionData: SessionData,
|
||||
val sessionLogic: SessionLogic,
|
||||
avatarActor: typed.ActorRef[AvatarActor.Command],
|
||||
galaxyService: ActorRef,
|
||||
implicit val context: ActorContext
|
||||
) extends CommonSessionInterfacingFunctionality {
|
||||
/* packets */
|
||||
|
||||
def handleUpdateIgnoredPlayers: PlanetSideGamePacket => Unit = {
|
||||
case msg: FriendsResponse =>
|
||||
sendResponse(msg)
|
||||
msg.friends.foreach { f =>
|
||||
galaxyService ! GalaxyServiceMessage(GalaxyAction.LogStatusChange(f.name))
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
|
||||
/* response handlers */
|
||||
|
||||
def handle(reply: GalaxyResponse.Response): Unit = {
|
||||
reply match {
|
||||
case GalaxyResponse.HotSpotUpdate(zone_index, priority, hot_spot_info) =>
|
||||
|
|
@ -45,7 +58,7 @@ class SessionGalaxyHandlers(
|
|||
sendResponse(msg)
|
||||
|
||||
case GalaxyResponse.TransferPassenger(temp_channel, vehicle, _, manifest) =>
|
||||
sessionData.zoning.handleTransferPassenger(temp_channel, vehicle, manifest)
|
||||
sessionLogic.zoning.handleTransferPassenger(temp_channel, vehicle, manifest)
|
||||
|
||||
case GalaxyResponse.LockedZoneUpdate(zone, time) =>
|
||||
sendResponse(ZoneInfoMessage(zone.Number, empire_status=false, lock_time=time))
|
||||
|
|
@ -58,7 +71,7 @@ class SessionGalaxyHandlers(
|
|||
val popVS = zone.Players.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 GalaxyResponse.LogStatusChange(name) if avatar.people.friend.exists(_.name.equals(name)) =>
|
||||
avatarActor ! AvatarActor.MemberListRequest(MemberAction.UpdateFriend, name)
|
||||
|
||||
case GalaxyResponse.SendResponse(msg) =>
|
||||
|
|
|
|||
|
|
@ -5,14 +5,13 @@ import akka.actor.ActorContext
|
|||
import net.psforever.objects.ce.Deployable
|
||||
import net.psforever.objects.vehicles.MountableWeapons
|
||||
import net.psforever.objects._
|
||||
import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.services.Service
|
||||
import net.psforever.services.local.LocalResponse
|
||||
import net.psforever.types.{ChatMessageType, PlanetSideGUID, Vector3}
|
||||
|
||||
class SessionLocalHandlers(
|
||||
val sessionData: SessionData,
|
||||
val sessionLogic: SessionLogic,
|
||||
implicit val context: ActorContext
|
||||
) extends CommonSessionInterfacingFunctionality {
|
||||
/**
|
||||
|
|
@ -33,7 +32,7 @@ class SessionLocalHandlers(
|
|||
sendResponse(DeployableObjectsInfoMessage(behavior, deployInfo))
|
||||
|
||||
case LocalResponse.DeployableUIFor(item) =>
|
||||
sessionData.updateDeployableUIElements(avatar.deployables.UpdateUIElement(item))
|
||||
sessionLogic.general.updateDeployableUIElements(avatar.deployables.UpdateUIElement(item))
|
||||
|
||||
case LocalResponse.Detonate(dguid, _: BoomerDeployable) =>
|
||||
sendResponse(TriggerEffectMessage(dguid, "detonate_boomer"))
|
||||
|
|
@ -112,10 +111,10 @@ class SessionLocalHandlers(
|
|||
sendResponse(HackMessage(unk1=0, targetGuid, guid, progress=0, unk1, HackState.HackCleared, unk2))
|
||||
|
||||
case LocalResponse.HackObject(targetGuid, unk1, unk2) =>
|
||||
HackObject(targetGuid, unk1, unk2)
|
||||
sessionLogic.general.hackObject(targetGuid, unk1, unk2)
|
||||
|
||||
case LocalResponse.PlanetsideAttribute(targetGuid, attributeType, attributeValue) =>
|
||||
SendPlanetsideAttributeMessage(targetGuid, attributeType, attributeValue)
|
||||
sessionLogic.general.sendPlanetsideAttributeMessage(targetGuid, attributeType, attributeValue)
|
||||
|
||||
case LocalResponse.GenericObjectAction(targetGuid, actionNumber) =>
|
||||
sendResponse(GenericObjectActionMessage(targetGuid, actionNumber))
|
||||
|
|
@ -142,8 +141,8 @@ class SessionLocalHandlers(
|
|||
sendResponse(TriggerSoundMessage(TriggeredSound.LLUDeconstruct, position, unk=20, volume=0.8000001f))
|
||||
sendResponse(ObjectDeleteMessage(lluGuid, unk1=0))
|
||||
// If the player was holding the LLU, remove it from their tracked special item slot
|
||||
sessionData.specialItemSlotGuid.collect { case guid if guid == lluGuid =>
|
||||
sessionData.specialItemSlotGuid = None
|
||||
sessionLogic.general.specialItemSlotGuid.collect { case guid if guid == lluGuid =>
|
||||
sessionLogic.general.specialItemSlotGuid = None
|
||||
player.Carrying = None
|
||||
}
|
||||
|
||||
|
|
@ -155,13 +154,13 @@ class SessionLocalHandlers(
|
|||
|
||||
case LocalResponse.ProximityTerminalEffect(objectGuid, false) =>
|
||||
sendResponse(ProximityTerminalUseMessage(Service.defaultPlayerGUID, objectGuid, unk=false))
|
||||
sessionData.terminals.ForgetAllProximityTerminals(objectGuid)
|
||||
sessionLogic.terminals.ForgetAllProximityTerminals(objectGuid)
|
||||
|
||||
case LocalResponse.RouterTelepadMessage(msg) =>
|
||||
sendResponse(ChatMsg(ChatMessageType.UNK_229, wideContents=false, recipient="", msg, note=None))
|
||||
|
||||
case LocalResponse.RouterTelepadTransport(passengerGuid, srcGuid, destGuid) =>
|
||||
sessionData.useRouterTelepadEffect(passengerGuid, srcGuid, destGuid)
|
||||
sessionLogic.general.useRouterTelepadEffect(passengerGuid, srcGuid, destGuid)
|
||||
|
||||
case LocalResponse.SendResponse(msg) =>
|
||||
sendResponse(msg)
|
||||
|
|
@ -190,7 +189,7 @@ class SessionLocalHandlers(
|
|||
sendResponse(VehicleStateMessage(sguid, unk1=0, pos, orient, vel=None, Some(state), unk3=0, unk4=0, wheel_direction=15, is_decelerating=false, is_cloaked=false))
|
||||
|
||||
case LocalResponse.ToggleTeleportSystem(router, systemPlan) =>
|
||||
sessionData.toggleTeleportSystem(router, systemPlan)
|
||||
sessionLogic.general.toggleTeleportSystem(router, systemPlan)
|
||||
|
||||
case LocalResponse.TriggerEffect(targetGuid, effect, effectInfo, triggerLocation) =>
|
||||
sendResponse(TriggerEffectMessage(targetGuid, effect, effectInfo, triggerLocation))
|
||||
|
|
@ -236,28 +235,4 @@ class SessionLocalHandlers(
|
|||
sendResponse(PlanetsideAttributeMessage(guid, 29, 1)) //make deployable vanish
|
||||
sendResponse(ObjectDeleteMessage(guid, deletionType))
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param targetGuid na
|
||||
* @param unk1 na
|
||||
* @param unk2 na
|
||||
*/
|
||||
def HackObject(targetGuid: PlanetSideGUID, unk1: Long, unk2: Long): Unit = {
|
||||
sendResponse(HackMessage(unk1=0, targetGuid, player_guid=Service.defaultPlayerGUID, progress=100, unk1, HackState.Hacked, unk2))
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a PlanetsideAttributeMessage packet to the client
|
||||
* @param targetGuid The target of the attribute
|
||||
* @param attributeType The attribute number
|
||||
* @param attributeValue The attribute value
|
||||
*/
|
||||
def SendPlanetsideAttributeMessage(
|
||||
targetGuid: PlanetSideGUID,
|
||||
attributeType: PlanetsideAttributeEnum,
|
||||
attributeValue: Long
|
||||
): Unit = {
|
||||
sendResponse(PlanetsideAttributeMessage(targetGuid, attributeType, attributeValue))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,573 @@
|
|||
// Copyright (c) 2023 PSForever
|
||||
package net.psforever.actors.session.support
|
||||
|
||||
import akka.actor.Actor.Receive
|
||||
import akka.actor.typed.receptionist.Receptionist
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
import akka.actor.{ActorContext, ActorRef, typed}
|
||||
import net.psforever.objects.zones.blockmap.{SectorGroup, SectorPopulation}
|
||||
import net.psforever.services.ServiceManager
|
||||
import net.psforever.services.ServiceManager.Lookup
|
||||
|
||||
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.zone.ZoneActor
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.avatar._
|
||||
import net.psforever.objects.ce._
|
||||
import net.psforever.objects.equipment._
|
||||
import net.psforever.objects.inventory.{Container, InventoryItem}
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.structures.Amenity
|
||||
import net.psforever.objects.vehicles._
|
||||
import net.psforever.objects.vital._
|
||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||
import net.psforever.objects.zones._
|
||||
import net.psforever.objects.zones.blockmap.{BlockMap, BlockMapEntity}
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.services.account.AccountPersistenceService
|
||||
import net.psforever.services.ServiceManager.LookupResult
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
import net.psforever.services.{Service, InterstellarClusterService => ICS}
|
||||
import net.psforever.types._
|
||||
import net.psforever.util.Config
|
||||
|
||||
object SessionLogic {
|
||||
//noinspection ScalaUnusedSymbol
|
||||
private def NoTurnCounterYet(guid: PlanetSideGUID): Unit = { }
|
||||
|
||||
private def updateOldRefsMap(inventory: net.psforever.objects.inventory.GridInventory): IterableOnce[(PlanetSideGUID, String)] = {
|
||||
inventory.Items.flatMap {
|
||||
case InventoryItem(o, _) => updateOldRefsMap(o)
|
||||
}
|
||||
}
|
||||
|
||||
private def updateOldRefsMap(item: PlanetSideGameObject): IterableOnce[(PlanetSideGUID, String)] = {
|
||||
item match {
|
||||
case t: Tool =>
|
||||
t.AmmoSlots.map { slot =>
|
||||
val box = slot.Box
|
||||
box.GUID -> box.Definition.Name
|
||||
} :+ (t.GUID -> t.Definition.Name)
|
||||
case _ =>
|
||||
Seq(item.GUID -> item.Definition.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait SessionLogic {
|
||||
def middlewareActor: typed.ActorRef[MiddlewareActor.Command]
|
||||
|
||||
implicit def context: ActorContext
|
||||
|
||||
/**
|
||||
* Hardwire an implicit `sender` to be the same as `context.self` of the `SessionActor` actor class
|
||||
* for which this support class was initialized.
|
||||
* Allows for proper use for `ActorRef.tell` or an actor's `!` in the support class,
|
||||
* one where the result is always directed back to the same `SessionActor` instance.
|
||||
* If there is a different packet "sender" that has to be respected by a given method,
|
||||
* pass that `ActorRef` into the method as a parameter.
|
||||
* @see `ActorRef.!(Any)(ActorRef)`
|
||||
* @see `ActorRef.tell(Any)(ActorRef)`
|
||||
*/
|
||||
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[support] val log = org.log4s.getLogger
|
||||
private[support] var theSession: Session = Session()
|
||||
private[support] var accountIntermediary: ActorRef = Default.Actor
|
||||
private[support] var accountPersistence: ActorRef = Default.Actor
|
||||
private[support] var galaxyService: ActorRef = Default.Actor
|
||||
private[support] var squadService: ActorRef = Default.Actor
|
||||
private[support] var cluster: typed.ActorRef[ICS.Command] = Default.typed.Actor
|
||||
private[session] var connectionState: Int = 25
|
||||
private[support] var persistFunc: () => Unit = noPersistence
|
||||
private[support] var persist: () => Unit = updatePersistenceOnly
|
||||
private[session] var keepAliveFunc: () => Unit = keepAlivePersistenceInitial
|
||||
private[support] var turnCounterFunc: PlanetSideGUID => Unit = SessionLogic.NoTurnCounterYet
|
||||
private[support] val oldRefsMap: mutable.HashMap[PlanetSideGUID, String] = new mutable.HashMap[PlanetSideGUID, String]()
|
||||
private var contextSafeEntity: PlanetSideGUID = PlanetSideGUID(0)
|
||||
|
||||
val general: GeneralOperations =
|
||||
new GeneralOperations(sessionLogic=this, avatarActor, chatActor, context)
|
||||
val shooting: WeaponAndProjectileOperations =
|
||||
new WeaponAndProjectileOperations(sessionLogic=this, avatarActor, chatActor, context)
|
||||
val vehicles: VehicleOperations =
|
||||
new VehicleOperations(sessionLogic=this, avatarActor, context)
|
||||
val avatarResponse: SessionAvatarHandlers =
|
||||
new SessionAvatarHandlers(sessionLogic=this, avatarActor, chatActor, context)
|
||||
val localResponse: SessionLocalHandlers =
|
||||
new SessionLocalHandlers(sessionLogic=this, context)
|
||||
val mountResponse: SessionMountHandlers =
|
||||
new SessionMountHandlers(sessionLogic=this, avatarActor, context)
|
||||
val terminals: SessionTerminalHandlers =
|
||||
new SessionTerminalHandlers(sessionLogic=this, avatarActor, context)
|
||||
private var vehicleResponseOpt: Option[SessionVehicleHandlers] = None
|
||||
private var galaxyResponseOpt: Option[SessionGalaxyHandlers] = None
|
||||
private var squadResponseOpt: Option[SessionSquadHandlers] = None
|
||||
private var zoningOpt: Option[ZoningOperations] = None
|
||||
def vehicleResponseOperations: SessionVehicleHandlers = vehicleResponseOpt.orNull
|
||||
def galaxyResponseHandlers: SessionGalaxyHandlers = galaxyResponseOpt.orNull
|
||||
def squad: SessionSquadHandlers = squadResponseOpt.orNull
|
||||
def zoning: ZoningOperations = zoningOpt.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)
|
||||
|
||||
/**
|
||||
* updated when an upstream packet arrives;
|
||||
* allow to be a little stale for a short while
|
||||
*/
|
||||
private[support] var localSector: SectorPopulation = SectorGroup(Nil)
|
||||
|
||||
def session: Session = theSession
|
||||
|
||||
def session_=(session: Session): Unit = {
|
||||
chatActor ! ChatActor.SetSession(session)
|
||||
avatarActor ! AvatarActor.SetSession(session)
|
||||
theSession = session
|
||||
}
|
||||
|
||||
def account: Account = theSession.account
|
||||
|
||||
def continent: Zone = theSession.zone
|
||||
|
||||
def player: Player = theSession.player
|
||||
|
||||
def avatar: Avatar = theSession.avatar
|
||||
|
||||
/* setup functions */
|
||||
|
||||
def assignEventBus(msg: Any): Boolean = {
|
||||
msg match {
|
||||
case LookupResult("accountIntermediary", endpoint) =>
|
||||
accountIntermediary = endpoint
|
||||
true
|
||||
case LookupResult("accountPersistence", endpoint) =>
|
||||
accountPersistence = endpoint
|
||||
true
|
||||
case LookupResult("galaxy", endpoint) =>
|
||||
galaxyService = endpoint
|
||||
buildDependentOperationsForGalaxy(endpoint)
|
||||
buildDependentOperations(endpoint, cluster)
|
||||
true
|
||||
case LookupResult("squad", endpoint) =>
|
||||
squadService = endpoint
|
||||
buildDependentOperationsForSquad(endpoint)
|
||||
true
|
||||
case ICS.InterstellarClusterServiceKey.Listing(listings) =>
|
||||
cluster = listings.head
|
||||
buildDependentOperations(galaxyService, cluster)
|
||||
true
|
||||
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
def buildDependentOperationsForGalaxy(galaxyActor: ActorRef): Unit = {
|
||||
if (vehicleResponseOpt.isEmpty && galaxyActor != Default.Actor) {
|
||||
galaxyResponseOpt = Some(new SessionGalaxyHandlers(sessionLogic=this, avatarActor, galaxyActor, context))
|
||||
vehicleResponseOpt = Some(new SessionVehicleHandlers(sessionLogic=this, avatarActor, galaxyActor, context))
|
||||
}
|
||||
}
|
||||
|
||||
def buildDependentOperations(galaxyActor: ActorRef, clusterActor: typed.ActorRef[ICS.Command]): Unit = {
|
||||
if (zoningOpt.isEmpty && galaxyActor != Default.Actor && clusterActor != Default.typed.Actor) {
|
||||
zoningOpt = Some(new ZoningOperations(sessionLogic=this, avatarActor, galaxyActor, clusterActor, context))
|
||||
}
|
||||
}
|
||||
|
||||
def buildDependentOperationsForSquad(squadActor: ActorRef): Unit = {
|
||||
if (squadResponseOpt.isEmpty && squadActor != Default.Actor) {
|
||||
squadResponseOpt = Some(new SessionSquadHandlers(sessionLogic=this, avatarActor, chatActor, squadActor, context))
|
||||
}
|
||||
}
|
||||
|
||||
def whenAllEventBusesLoaded(): Boolean = {
|
||||
accountIntermediary != Default.Actor &&
|
||||
accountPersistence != Default.Actor &&
|
||||
vehicleResponseOpt.nonEmpty &&
|
||||
galaxyResponseOpt.nonEmpty &&
|
||||
squadResponseOpt.nonEmpty &&
|
||||
zoningOpt.nonEmpty
|
||||
}
|
||||
|
||||
/* message processing */
|
||||
|
||||
def parse(sender: ActorRef): Receive
|
||||
|
||||
/* support functions */
|
||||
|
||||
def validObject(id: Int): Option[PlanetSideGameObject] = validObject(Some(PlanetSideGUID(id)), decorator = "")
|
||||
|
||||
def validObject(id: Int, decorator: String): Option[PlanetSideGameObject] = validObject(Some(PlanetSideGUID(id)), decorator)
|
||||
|
||||
def validObject(id: PlanetSideGUID): Option[PlanetSideGameObject] = validObject(Some(id), decorator = "")
|
||||
|
||||
def validObject(id: PlanetSideGUID, decorator: String): Option[PlanetSideGameObject] = validObject(Some(id), decorator)
|
||||
|
||||
def validObject(id: Option[PlanetSideGUID]): Option[PlanetSideGameObject] = validObject(id, decorator = "")
|
||||
|
||||
def validObject(id: Option[PlanetSideGUID], decorator: String): Option[PlanetSideGameObject] = {
|
||||
val elevatedDecorator = if (decorator.nonEmpty) decorator else "ValidObject"
|
||||
id match {
|
||||
case Some(guid) =>
|
||||
val hint = oldRefsMap.getOrElse(guid, "thing")
|
||||
continent.GUID(guid) match {
|
||||
case Some(_: LocalProjectile) =>
|
||||
shooting.FindProjectileEntry(guid)
|
||||
|
||||
case Some(_: LocalLockerItem) =>
|
||||
player.avatar.locker.Inventory.hasItem(guid) match {
|
||||
case out @ Some(_) =>
|
||||
contextSafeEntity = guid
|
||||
out
|
||||
case None if contextSafeEntity == guid =>
|
||||
//safeguard
|
||||
None
|
||||
case None =>
|
||||
//delete stale entity reference from client
|
||||
log.warn(
|
||||
s"$elevatedDecorator: ${player.Name} is looking for an invalid GUID $guid, believing it a $hint in ${player.Sex.possessive} locker"
|
||||
)
|
||||
sendResponse(ObjectDeleteMessage(guid, 0))
|
||||
None
|
||||
}
|
||||
|
||||
case Some(obj) if obj.HasGUID && obj.GUID != guid =>
|
||||
log.error(
|
||||
s"$elevatedDecorator: ${player.Name} found a ${obj.Definition.Name} that isn't the $hint ${player.Sex.pronounSubject} thought it was in zone ${continent.id}"
|
||||
)
|
||||
log.debug(
|
||||
s"$elevatedDecorator: potentially fatal error in ${continent.id} - requested $hint with $guid, got ${obj.Definition.Name} with ${obj.GUID}; mismatch"
|
||||
)
|
||||
None
|
||||
|
||||
case out @ Some(obj) if obj.HasGUID =>
|
||||
out
|
||||
|
||||
case None if !id.contains(PlanetSideGUID(0)) =>
|
||||
//delete stale entity reference from client
|
||||
//deleting guid=0 will cause BAD things to happen
|
||||
log.error(s"$elevatedDecorator: ${player.Name} has an invalid reference to $hint with GUID $guid in zone ${continent.id}")
|
||||
sendResponse(ObjectDeleteMessage(guid, 0))
|
||||
None
|
||||
|
||||
case None if contextSafeEntity == guid =>
|
||||
//safeguard
|
||||
None
|
||||
|
||||
case _ =>
|
||||
None
|
||||
}
|
||||
|
||||
case None =>
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this player avatar for persistence.
|
||||
* Set to `persist` initially.
|
||||
*/
|
||||
def updatePersistenceOnly(): Unit = {
|
||||
persistFunc()
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not update this player avatar for persistence.
|
||||
* Set to `persistFunc` initially.
|
||||
*/
|
||||
def noPersistence(): Unit = { }
|
||||
|
||||
/**
|
||||
* Check two locations for a controlled piece of equipment that is associated with the `player`.<br>
|
||||
* <br>
|
||||
* The first location is dependent on whether the avatar is in a vehicle.
|
||||
* Some vehicle seats may have a "controlled weapon" which counts as the first location to be checked.
|
||||
* The second location is dependent on whether the avatar has a raised hand.
|
||||
* That is only possible if the player has something in their hand at the moment, hence the second location.
|
||||
* Players do have a concept called a "last drawn slot" (hand) but that former location is not eligible.<br>
|
||||
* <br>
|
||||
* Along with any discovered item, a containing object such that the statement:<br>
|
||||
* `container.Find(object) = Some(slot)`<br>
|
||||
* ... will return a proper result.
|
||||
* For a mount controlled weapon, the vehicle is returned.
|
||||
* For the player's hand, the player is returned.
|
||||
* @return a `Tuple` of the returned values;
|
||||
* the first value is a `Container` object;
|
||||
* the second value is an `Equipment` object in the former
|
||||
*/
|
||||
def findContainedEquipment(): (Option[PlanetSideGameObject with Container], Set[Equipment]) = {
|
||||
continent.GUID(player.VehicleSeated) match {
|
||||
case Some(vehicle: Mountable with MountableWeapons with Container) =>
|
||||
vehicle.PassengerInSeat(player) match {
|
||||
case Some(seatNum) =>
|
||||
(Some(vehicle), vehicle.WeaponControlledFromSeat(seatNum))
|
||||
case None =>
|
||||
(None, Set.empty)
|
||||
}
|
||||
case _ =>
|
||||
player.Slot(player.DrawnSlot).Equipment match {
|
||||
case Some(a) =>
|
||||
(Some(player), Set(a))
|
||||
case _ =>
|
||||
(None, Set.empty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check two locations for a controlled piece of equipment that is associated with the `player`
|
||||
* and has the specified global unique identifier number.
|
||||
*/
|
||||
def findContainedEquipment(
|
||||
guid: PlanetSideGUID
|
||||
): (Option[PlanetSideGameObject with Container], Set[Equipment]) = {
|
||||
val (o, equipment) = findContainedEquipment()
|
||||
equipment.find { _.GUID == guid } match {
|
||||
case Some(equip) => (o, Set(equip))
|
||||
case None => (None, Set.empty)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs `FindContainedEquipment` but ignores the `Container` object output.
|
||||
* @return an `Equipment` object
|
||||
*/
|
||||
def findEquipment(): Set[Equipment] = findContainedEquipment()._2
|
||||
|
||||
/**
|
||||
* Runs `FindContainedEquipment` but ignores the `Container` object output
|
||||
* and only discovers `Equipment` with the specified global unique identifier number.
|
||||
* @return an `Equipment` object
|
||||
*/
|
||||
def findEquipment(guid: PlanetSideGUID): Option[Equipment] = findEquipment().find { _.GUID == guid }
|
||||
|
||||
/**
|
||||
* An event has occurred that would cause the player character to stop certain stateful activities.
|
||||
* These activities include shooting, the weapon being drawn, hacking, accessing (a container), flying, and running.
|
||||
* Other players in the same zone must be made aware that the player has stopped as well.<br>
|
||||
* <br>
|
||||
* Things whose configuration should not be changed:<br>
|
||||
* - if the player is seated<br>
|
||||
* - if the player is anchored<br>
|
||||
* This is not a complete list but, for the purpose of enforcement, some pointers will be documented here.
|
||||
*/
|
||||
def actionsToCancel(): Unit = {
|
||||
general.actionsToCancel()
|
||||
shooting.actionsToCancel()
|
||||
terminals.actionsToCancel()
|
||||
if (session.flying) {
|
||||
session = session.copy(flying = false)
|
||||
chatActor ! ChatActor.Message(ChatMsg(ChatMessageType.CMT_FLY, wideContents=false, "", "off", None))
|
||||
}
|
||||
if (session.speed > 1) {
|
||||
session = session.copy(speed = 1)
|
||||
chatActor ! ChatActor.Message(ChatMsg(ChatMessageType.CMT_SPEED, wideContents=false, "", "1.000", None))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the amount of damage to be dealt to an active `target`
|
||||
* using the information reconstructed from a `ResolvedProjectile`
|
||||
* and affect the `target` in a synchronized manner.
|
||||
* The active `target` and the target of the `DamageResult` do not have be the same.
|
||||
* While the "tell" for being able to sustain damage is an entity of type `Vitality`,
|
||||
* only specific `Vitality` entity types are being screened for sustaining damage.
|
||||
* @see `DamageResistanceModel`
|
||||
* @see `Vitality`
|
||||
* @param target a valid game object that is known to the server
|
||||
* @param data a projectile that will affect the target
|
||||
*/
|
||||
def handleDealingDamage(target: PlanetSideGameObject with Vitality, data: DamageInteraction): Unit = {
|
||||
val func = data.calculate()
|
||||
target match {
|
||||
case obj: Player if obj.CanDamage && obj.Actor != Default.Actor =>
|
||||
if (obj.CharId != player.CharId) {
|
||||
log.info(s"${player.Name} is attacking ${obj.Name}")
|
||||
} else {
|
||||
log.info(s"${player.Name} hurt ${player.Sex.pronounObject}self")
|
||||
}
|
||||
// auto kick players damaging spectators
|
||||
if (obj.spectator && obj != player) {
|
||||
administrativeKick(player)
|
||||
} else {
|
||||
obj.Actor ! Vitality.Damage(func)
|
||||
}
|
||||
|
||||
case obj: Vehicle if obj.CanDamage =>
|
||||
val name = player.Name
|
||||
val ownerName = obj.OwnerName.getOrElse("someone")
|
||||
if (ownerName.equals(name)) {
|
||||
log.info(s"$name is damaging ${player.Sex.possessive} own ${obj.Definition.Name}")
|
||||
} else {
|
||||
log.info(s"$name is attacking $ownerName's ${obj.Definition.Name}")
|
||||
}
|
||||
obj.Actor ! Vitality.Damage(func)
|
||||
|
||||
case obj: Amenity if obj.CanDamage =>
|
||||
obj.Actor ! Vitality.Damage(func)
|
||||
|
||||
case obj: Deployable if obj.CanDamage =>
|
||||
val name = player.Name
|
||||
val ownerName = obj.OwnerName.getOrElse("someone")
|
||||
if (ownerName.equals(name)) {
|
||||
log.info(s"$name is damaging ${player.Sex.possessive} own ${obj.Definition.Name}")
|
||||
} else {
|
||||
log.info(s"$name is attacking $ownerName's ${obj.Definition.Name}")
|
||||
}
|
||||
obj.Actor ! Vitality.Damage(func)
|
||||
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The atypical response to receiving a `KeepAliveMessage` packet from the client.<br>
|
||||
* <br>
|
||||
* `KeepAliveMessage` packets are the primary vehicle for persistence due to client reporting
|
||||
* in the case where the player's avatar is riding in a vehicle in a mount with no weapon to control.
|
||||
* @see `KeepAliveMessage`
|
||||
* @see `keepAliveFunc`
|
||||
* @see `turnCounterFunc`
|
||||
* @see `persist`
|
||||
*/
|
||||
def keepAlivePersistence(): Unit = {
|
||||
zoning.spawn.interimUngunnedVehicle = None
|
||||
persist()
|
||||
if (player.HasGUID) {
|
||||
turnCounterFunc(player.GUID)
|
||||
} else {
|
||||
turnCounterFunc(PlanetSideGUID(0))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A really atypical response to receiving a `KeepAliveMessage` packet from the client
|
||||
* that applies only during the character select portion and part of the first zone load activity.
|
||||
*/
|
||||
def keepAlivePersistenceInitial(): Unit = {
|
||||
persist()
|
||||
if (player != null && player.HasGUID) {
|
||||
keepAliveFunc = keepAlivePersistence
|
||||
}
|
||||
}
|
||||
|
||||
def updateBlockMap(target: BlockMapEntity, newCoords: Vector3): Unit = {
|
||||
target.blockMapEntry.foreach { entry =>
|
||||
val sectorIndices = BlockMap.findSectorIndices(continent.blockMap, newCoords, entry.rangeX, entry.rangeY).toSet
|
||||
if (sectorIndices.equals(entry.sectors)) {
|
||||
target.updateBlockMapEntry(newCoords) //soft update
|
||||
localSector = continent.blockMap.sector(sectorIndices, Config.app.game.playerDraw.rangeMax.toFloat)
|
||||
} else {
|
||||
continent.actor ! ZoneActor.UpdateBlockMap(target, newCoords) //hard update
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def updateLocalBlockMap(pos: Vector3): Unit = {
|
||||
localSector = continent.blockMap.sector(pos, Config.app.game.playerDraw.rangeMax.toFloat)
|
||||
}
|
||||
|
||||
def updateOldRefsMap(): Unit = {
|
||||
if(player.HasGUID) {
|
||||
oldRefsMap.addAll(
|
||||
(continent.GUID(player.VehicleSeated) match {
|
||||
case Some(v : Vehicle) =>
|
||||
v.Weapons.toList.collect {
|
||||
case (_, slot : EquipmentSlot) if slot.Equipment.nonEmpty => SessionLogic.updateOldRefsMap(slot.Equipment.get)
|
||||
}.flatten ++
|
||||
SessionLogic.updateOldRefsMap(v.Inventory)
|
||||
case _ =>
|
||||
Map.empty[PlanetSideGUID, String]
|
||||
}) ++
|
||||
(general.accessedContainer match {
|
||||
case Some(cont) => SessionLogic.updateOldRefsMap(cont.Inventory)
|
||||
case None => Map.empty[PlanetSideGUID, String]
|
||||
}) ++
|
||||
player.Holsters().toList.collect {
|
||||
case slot if slot.Equipment.nonEmpty => SessionLogic.updateOldRefsMap(slot.Equipment.get)
|
||||
}.flatten ++
|
||||
SessionLogic.updateOldRefsMap(player.Inventory) ++
|
||||
SessionLogic.updateOldRefsMap(player.avatar.locker.Inventory)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def administrativeKick(tplayer: Player): Unit = {
|
||||
log.warn(s"${tplayer.Name} has been kicked by ${player.Name}")
|
||||
tplayer.death_by = -1
|
||||
accountPersistence ! AccountPersistenceService.Kick(tplayer.Name)
|
||||
//get out of that vehicle
|
||||
vehicles.GetMountableAndSeat(None, tplayer, continent) match {
|
||||
case (Some(obj), Some(seatNum)) =>
|
||||
tplayer.VehicleSeated = None
|
||||
obj.Seats(seatNum).unmount(tplayer)
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.KickPassenger(tplayer.GUID, seatNum, unk2=false, obj.GUID)
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def kickedByAdministration(): Unit = {
|
||||
sendResponse(DisconnectMessage("@kick_w"))
|
||||
context.system.scheduler.scheduleOnce(
|
||||
delay = 300.milliseconds,
|
||||
middlewareActor.toClassic,
|
||||
MiddlewareActor.Teardown()
|
||||
)
|
||||
}
|
||||
|
||||
def immediateDisconnect(): Unit = {
|
||||
if (avatar != null) {
|
||||
accountPersistence ! AccountPersistenceService.Logout(avatar.name)
|
||||
}
|
||||
middlewareActor ! MiddlewareActor.Teardown()
|
||||
}
|
||||
|
||||
def failWithError(error: String): Unit = {
|
||||
log.error(error)
|
||||
middlewareActor ! MiddlewareActor.Teardown()
|
||||
}
|
||||
|
||||
def sendResponse(packet: PlanetSidePacket): Unit = {
|
||||
middlewareActor ! MiddlewareActor.Send(packet)
|
||||
}
|
||||
|
||||
def stop(): Unit = {
|
||||
context.stop(avatarActor)
|
||||
context.stop(chatActor)
|
||||
general.stop()
|
||||
shooting.stop()
|
||||
vehicles.stop()
|
||||
avatarResponse.stop()
|
||||
localResponse.stop()
|
||||
mountResponse.stop()
|
||||
terminals.stop()
|
||||
vehicleResponseOpt.foreach(_.stop())
|
||||
galaxyResponseOpt.foreach(_.stop())
|
||||
squadResponseOpt.foreach(_.stop())
|
||||
zoningOpt.foreach(_.stop())
|
||||
continent.AvatarEvents ! Service.Leave()
|
||||
continent.LocalEvents ! Service.Leave()
|
||||
continent.VehicleEvents ! Service.Leave()
|
||||
galaxyService ! Service.Leave()
|
||||
if (avatar != null && squadService != Default.Actor) {
|
||||
squadService ! Service.Leave()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,9 +2,12 @@
|
|||
package net.psforever.actors.session.support
|
||||
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.objects.Tool
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinity
|
||||
import net.psforever.objects.serverobject.environment.interaction.ResetAllEnvironmentInteractions
|
||||
import net.psforever.objects.vehicles.MountableWeapons
|
||||
import net.psforever.objects.vital.InGameHistory
|
||||
import net.psforever.packet.game.InventoryStateMessage
|
||||
|
||||
import scala.concurrent.duration._
|
||||
//
|
||||
|
|
@ -24,7 +27,7 @@ import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
|||
import net.psforever.types.{BailType, ChatMessageType, PlanetSideGUID, Vector3}
|
||||
|
||||
class SessionMountHandlers(
|
||||
val sessionData: SessionData,
|
||||
val sessionLogic: SessionLogic,
|
||||
avatarActor: typed.ActorRef[AvatarActor.Command],
|
||||
implicit val context: ActorContext
|
||||
) extends CommonSessionInterfacingFunctionality {
|
||||
|
|
@ -37,82 +40,82 @@ class SessionMountHandlers(
|
|||
def handle(tplayer: Player, reply: Mountable.Exchange): Unit = {
|
||||
reply match {
|
||||
case Mountable.CanMount(obj: ImplantTerminalMech, seatNumber, _) =>
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
log.info(s"${player.Name} mounts an implant terminal")
|
||||
sessionData.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
sessionData.keepAliveFunc = sessionData.keepAlivePersistence
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if obj.Definition == GlobalDefinitions.orbital_shuttle =>
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the orbital shuttle")
|
||||
sessionData.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
sessionData.keepAliveFunc = sessionData.keepAlivePersistence
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if obj.Definition == GlobalDefinitions.ant =>
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the driver seat of the ${obj.Definition.Name}")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionData.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields))
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=45, obj.NtuCapacitorScaled))
|
||||
sendResponse(GenericObjectActionMessage(obj_guid, code=11))
|
||||
sessionData.accessContainer(obj)
|
||||
sessionLogic.general.accessContainer(obj)
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if obj.Definition == GlobalDefinitions.quadstealth =>
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the driver seat of the ${obj.Definition.Name}")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionData.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields))
|
||||
//exclusive to the wraith, cloak state matches the cloak state of the driver
|
||||
//phantasm doesn't uncloak if the driver is uncloaked and no other vehicle cloaks
|
||||
obj.Cloaked = tplayer.Cloaked
|
||||
sendResponse(GenericObjectActionMessage(obj_guid, code=11))
|
||||
sessionData.accessContainer(obj)
|
||||
sessionLogic.general.accessContainer(obj)
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if seatNumber == 0 && obj.Definition.MaxCapacitor > 0 =>
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the driver seat of the ${obj.Definition.Name}")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionData.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields))
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=113, obj.Capacitor))
|
||||
sendResponse(GenericObjectActionMessage(obj_guid, code=11))
|
||||
sessionData.accessContainer(obj)
|
||||
sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
sessionLogic.general.accessContainer(obj)
|
||||
updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if seatNumber == 0 =>
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the driver seat of the ${obj.Definition.Name}")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionData.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields))
|
||||
sendResponse(GenericObjectActionMessage(obj_guid, code=11))
|
||||
sessionData.accessContainer(obj)
|
||||
sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
sessionLogic.general.accessContainer(obj)
|
||||
updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
|
||||
if obj.Definition.MaxCapacitor > 0 =>
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts ${
|
||||
obj.SeatPermissionGroup(seatNumber) match {
|
||||
case Some(seatType) => s"a $seatType seat (#$seatNumber)"
|
||||
|
|
@ -120,18 +123,18 @@ class SessionMountHandlers(
|
|||
}
|
||||
} of the ${obj.Definition.Name}")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionData.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields))
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=113, obj.Capacitor))
|
||||
sessionData.accessContainer(obj)
|
||||
sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
sessionData.keepAliveFunc = sessionData.keepAlivePersistence
|
||||
sessionLogic.general.accessContainer(obj)
|
||||
updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Vehicle, seatNumber, _) =>
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the ${
|
||||
obj.SeatPermissionGroup(seatNumber) match {
|
||||
case Some(seatType) => s"a $seatType seat (#$seatNumber)"
|
||||
|
|
@ -139,44 +142,44 @@ class SessionMountHandlers(
|
|||
}
|
||||
} of the ${obj.Definition.Name}")
|
||||
val obj_guid: PlanetSideGUID = obj.GUID
|
||||
sessionData.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
|
||||
sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields))
|
||||
sessionData.accessContainer(obj)
|
||||
sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
sessionData.keepAliveFunc = sessionData.keepAlivePersistence
|
||||
sessionLogic.general.accessContainer(obj)
|
||||
updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
|
||||
tplayer.Actor ! ResetAllEnvironmentInteractions
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: FacilityTurret, seatNumber, _)
|
||||
if obj.Definition == GlobalDefinitions.vanu_sentry_turret =>
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the ${obj.Definition.Name}")
|
||||
obj.Zone.LocalEvents ! LocalServiceMessage(obj.Zone.id, LocalAction.SetEmpire(obj.GUID, player.Faction))
|
||||
sendResponse(PlanetsideAttributeMessage(obj.GUID, attribute_type=0, obj.Health))
|
||||
sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: FacilityTurret, seatNumber, _)
|
||||
if !obj.isUpgrading || System.currentTimeMillis() - getTurretUpgradeTime >= 1500L =>
|
||||
obj.setMiddleOfUpgrade(false)
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the ${obj.Definition.Name}")
|
||||
sendResponse(PlanetsideAttributeMessage(obj.GUID, attribute_type=0, obj.Health))
|
||||
sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: FacilityTurret, _, _) =>
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.warn(
|
||||
s"MountVehicleMsg: ${tplayer.Name} wants to mount turret ${obj.GUID.guid}, but needs to wait until it finishes updating"
|
||||
)
|
||||
|
||||
case Mountable.CanMount(obj: PlanetSideGameObject with FactionAffinity with WeaponTurret with InGameHistory, seatNumber, _) =>
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
|
||||
log.info(s"${player.Name} mounts the ${obj.Definition.asInstanceOf[BasicDefinition].Name}")
|
||||
sendResponse(PlanetsideAttributeMessage(obj.GUID, attribute_type=0, obj.Health))
|
||||
sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
updateWeaponAtSeatPosition(obj, seatNumber)
|
||||
MountingAction(tplayer, obj, seatNumber)
|
||||
|
||||
case Mountable.CanMount(obj: Mountable, _, _) =>
|
||||
|
|
@ -199,7 +202,7 @@ class SessionMountHandlers(
|
|||
continent.id,
|
||||
LocalAction.SendResponse(ObjectDetachMessage(sguid, pguid, pos, roll=0, pitch=0, zang))
|
||||
)
|
||||
sessionData.keepAliveFunc = sessionData.zoning.NormalKeepAlive
|
||||
sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, seatNum, _)
|
||||
if obj.Definition == GlobalDefinitions.orbital_shuttle =>
|
||||
|
|
@ -229,12 +232,12 @@ class SessionMountHandlers(
|
|||
continent.id,
|
||||
VehicleAction.SendResponse(pguid, GenericObjectActionMessage(pguid, code=9)) //conceal the player
|
||||
)
|
||||
sessionData.keepAliveFunc = sessionData.zoning.NormalKeepAlive
|
||||
sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, seatNum, _)
|
||||
if obj.Definition == GlobalDefinitions.droppod =>
|
||||
log.info(s"${tplayer.Name} has landed on ${continent.id}")
|
||||
sessionData.unaccessContainer(obj)
|
||||
sessionLogic.general.unaccessContainer(obj)
|
||||
DismountAction(tplayer, obj, seatNum)
|
||||
obj.Actor ! Vehicle.Deconstruct()
|
||||
|
||||
|
|
@ -248,8 +251,8 @@ class SessionMountHandlers(
|
|||
case None => "seat"
|
||||
}
|
||||
}")
|
||||
sessionData.vehicles.ConditionalDriverVehicleControl(obj)
|
||||
sessionData.unaccessContainer(obj)
|
||||
sessionLogic.vehicles.ConditionalDriverVehicleControl(obj)
|
||||
sessionLogic.general.unaccessContainer(obj)
|
||||
DismountVehicleAction(tplayer, obj, seatNum)
|
||||
|
||||
case Mountable.CanDismount(obj: Vehicle, seat_num, _) =>
|
||||
|
|
@ -291,7 +294,7 @@ class SessionMountHandlers(
|
|||
def MountingAction(tplayer: Player, obj: PlanetSideGameObject with FactionAffinity with InGameHistory, seatNum: Int): Unit = {
|
||||
val playerGuid: PlanetSideGUID = tplayer.GUID
|
||||
val objGuid: PlanetSideGUID = obj.GUID
|
||||
sessionData.playerActionsToCancel()
|
||||
sessionLogic.actionsToCancel()
|
||||
avatarActor ! AvatarActor.DeactivateActiveImplants()
|
||||
avatarActor ! AvatarActor.SuspendStaminaRegeneration(3.seconds)
|
||||
sendResponse(ObjectAttachMessage(objGuid, playerGuid, seatNum))
|
||||
|
|
@ -313,8 +316,8 @@ class SessionMountHandlers(
|
|||
obj match {
|
||||
case v: Vehicle
|
||||
if seatNum == 0 && Vector3.MagnitudeSquared(v.Velocity.getOrElse(Vector3.Zero)) > 0f =>
|
||||
sessionData.vehicles.serverVehicleControlVelocity.collect { _ =>
|
||||
sessionData.vehicles.ServerVehicleOverrideStop(v)
|
||||
sessionLogic.vehicles.serverVehicleControlVelocity.collect { _ =>
|
||||
sessionLogic.vehicles.ServerVehicleOverrideStop(v)
|
||||
}
|
||||
v.Velocity = Vector3.Zero
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
|
|
@ -347,7 +350,7 @@ class SessionMountHandlers(
|
|||
def DismountAction(tplayer: Player, obj: PlanetSideGameObject with FactionAffinity with InGameHistory, seatNum: Int): Unit = {
|
||||
val playerGuid: PlanetSideGUID = tplayer.GUID
|
||||
tplayer.ContributionFrom(obj)
|
||||
sessionData.keepAliveFunc = sessionData.zoning.NormalKeepAlive
|
||||
sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive
|
||||
val bailType = if (tplayer.BailProtection) {
|
||||
BailType.Bailed
|
||||
} else {
|
||||
|
|
@ -359,4 +362,22 @@ class SessionMountHandlers(
|
|||
VehicleAction.DismountVehicle(playerGuid, bailType, unk2 = false)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* From a mount, find the weapon controlled from it, and update the ammunition counts for that weapon's magazines.
|
||||
* @param objWithSeat the object that owns seats (and weaponry)
|
||||
* @param seatNum the mount
|
||||
*/
|
||||
def updateWeaponAtSeatPosition(objWithSeat: MountableWeapons, seatNum: Int): Unit = {
|
||||
objWithSeat.WeaponControlledFromSeat(seatNum) foreach {
|
||||
case weapon: Tool =>
|
||||
//update mounted weapon belonging to mount
|
||||
weapon.AmmoSlots.foreach(slot => {
|
||||
//update the magazine(s) in the weapon, specifically
|
||||
val magazine = slot.Box
|
||||
sendResponse(InventoryStateMessage(magazine.GUID, weapon.GUID, magazine.Capacity.toLong))
|
||||
})
|
||||
case _ => () //no weapons to update
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ object SessionSquadHandlers {
|
|||
}
|
||||
|
||||
class SessionSquadHandlers(
|
||||
val sessionData: SessionData,
|
||||
val sessionLogic: SessionLogic,
|
||||
avatarActor: typed.ActorRef[AvatarActor.Command],
|
||||
chatActor: typed.ActorRef[ChatActor.Command],
|
||||
squadService: ActorRef,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
package net.psforever.actors.session.support
|
||||
|
||||
import akka.actor.{ActorContext, typed}
|
||||
import net.psforever.objects.guid.GUIDTask
|
||||
import net.psforever.objects.sourcing.AmenitySource
|
||||
import net.psforever.objects.vital.TerminalUsedActivity
|
||||
|
||||
|
|
@ -21,7 +22,7 @@ import net.psforever.packet.game.{ItemTransactionMessage, ItemTransactionResultM
|
|||
import net.psforever.types.{PlanetSideGUID, TransactionType, Vector3}
|
||||
|
||||
class SessionTerminalHandlers(
|
||||
val sessionData: SessionData,
|
||||
val sessionLogic: SessionLogic,
|
||||
avatarActor: typed.ActorRef[AvatarActor.Command],
|
||||
implicit val context: ActorContext
|
||||
) extends CommonSessionInterfacingFunctionality {
|
||||
|
|
@ -37,7 +38,7 @@ class SessionTerminalHandlers(
|
|||
val msg: String = if (itemName.nonEmpty) s" of $itemName" else ""
|
||||
log.info(s"${player.Name} is submitting an order - a $transactionType from a ${term.Definition.Name}$msg")
|
||||
lastTerminalOrderFulfillment = false
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
term.Actor ! Terminal.Request(player, pkt)
|
||||
case Some(_: Terminal) =>
|
||||
log.warn(s"Please Wait until your previous order has been fulfilled, ${player.Name}")
|
||||
|
|
@ -203,7 +204,7 @@ class SessionTerminalHandlers(
|
|||
Future(true)
|
||||
}
|
||||
},
|
||||
List(sessionData.registerVehicle(vehicle))
|
||||
List(registerVehicle(vehicle))
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -293,7 +294,7 @@ class SessionTerminalHandlers(
|
|||
|
||||
/**
|
||||
* Cease all current interactions with proximity-based units.
|
||||
* Pair with `PlayerActionsToCancel`, except when logging out (stopping).
|
||||
* Pair with `actionsToCancel`, except when logging out (stopping).
|
||||
* This operations may invoke callback messages.
|
||||
* @see `postStop`
|
||||
*/
|
||||
|
|
@ -303,7 +304,7 @@ class SessionTerminalHandlers(
|
|||
|
||||
/**
|
||||
* Cease all current interactions with proximity-based units.
|
||||
* Pair with `PlayerActionsToCancel`, except when logging out (stopping).
|
||||
* Pair with `actionsToCancel`, except when logging out (stopping).
|
||||
* This operations may invoke callback messages.
|
||||
* @param guid globally unique identifier for a proximity terminal
|
||||
* @see `postStop`
|
||||
|
|
@ -326,4 +327,28 @@ class SessionTerminalHandlers(
|
|||
usingMedicalTerminal = None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct tasking that adds a completed and registered vehicle into the scene.
|
||||
* Use this function to renew the globally unique identifiers on a vehicle that has already been added to the scene once.
|
||||
* @param vehicle the `Vehicle` object
|
||||
* @see `RegisterVehicleFromSpawnPad`
|
||||
* @return a `TaskBundle` message
|
||||
*/
|
||||
private[session] def registerVehicle(vehicle: Vehicle): TaskBundle = {
|
||||
TaskBundle(
|
||||
new StraightforwardTask() {
|
||||
private val localVehicle = vehicle
|
||||
|
||||
override def description(): String = s"register a ${localVehicle.Definition.Name}"
|
||||
|
||||
def action(): Future[Any] = Future(true)
|
||||
},
|
||||
List(GUIDTask.registerVehicle(continent.GUID, vehicle))
|
||||
)
|
||||
}
|
||||
|
||||
override protected[support] def actionsToCancel(): Unit = {
|
||||
lastTerminalOrderFulfillment = true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import net.psforever.types.{BailType, ChatMessageType, PlanetSideGUID, Vector3}
|
|||
import scala.concurrent.duration._
|
||||
|
||||
class SessionVehicleHandlers(
|
||||
val sessionData: SessionData,
|
||||
val sessionLogic: SessionLogic,
|
||||
avatarActor: typed.ActorRef[AvatarActor.Command],
|
||||
galaxyService: ActorRef,
|
||||
implicit val context: ActorContext
|
||||
|
|
@ -55,7 +55,7 @@ class SessionVehicleHandlers(
|
|||
player.Position = pos
|
||||
player.Orientation = orient
|
||||
player.Velocity = vel
|
||||
sessionData.updateLocalBlockMap(pos)
|
||||
sessionLogic.updateLocalBlockMap(pos)
|
||||
|
||||
case VehicleResponse.VehicleState(
|
||||
vehicleGuid,
|
||||
|
|
@ -165,7 +165,7 @@ class SessionVehicleHandlers(
|
|||
sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver))
|
||||
val typeOfRide = continent.GUID(vehicleGuid) match {
|
||||
case Some(obj: Vehicle) =>
|
||||
sessionData.unaccessContainer(obj)
|
||||
sessionLogic.general.unaccessContainer(obj)
|
||||
s"the ${obj.Definition.Name}'s seat by ${obj.OwnerName.getOrElse("the pilot")}"
|
||||
case _ =>
|
||||
s"${player.Sex.possessive} ride"
|
||||
|
|
@ -217,22 +217,22 @@ class SessionVehicleHandlers(
|
|||
sendResponse(ObjectDeleteMessage(itemGuid, unk1=0))
|
||||
|
||||
case VehicleResponse.UpdateAmsSpawnPoint(list) =>
|
||||
sessionData.zoning.spawn.amsSpawnPoints = list.filter(tube => tube.Faction == player.Faction)
|
||||
sessionData.zoning.spawn.DrawCurrentAmsSpawnPoint()
|
||||
sessionLogic.zoning.spawn.amsSpawnPoints = list.filter(tube => tube.Faction == player.Faction)
|
||||
sessionLogic.zoning.spawn.DrawCurrentAmsSpawnPoint()
|
||||
|
||||
case VehicleResponse.TransferPassengerChannel(oldChannel, tempChannel, vehicle, vehicleToDelete) if isNotSameTarget =>
|
||||
sessionData.zoning.interstellarFerry = Some(vehicle)
|
||||
sessionData.zoning.interstellarFerryTopLevelGUID = Some(vehicleToDelete)
|
||||
sessionLogic.zoning.interstellarFerry = Some(vehicle)
|
||||
sessionLogic.zoning.interstellarFerryTopLevelGUID = Some(vehicleToDelete)
|
||||
continent.VehicleEvents ! Service.Leave(Some(oldChannel)) //old vehicle-specific channel (was s"${vehicle.Actor}")
|
||||
galaxyService ! Service.Join(tempChannel) //temporary vehicle-specific channel
|
||||
log.debug(s"TransferPassengerChannel: ${player.Name} now subscribed to $tempChannel for vehicle gating")
|
||||
|
||||
case VehicleResponse.KickCargo(vehicle, speed, delay)
|
||||
if player.VehicleSeated.nonEmpty && sessionData.zoning.spawn.deadState == DeadState.Alive && speed > 0 =>
|
||||
if player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive && speed > 0 =>
|
||||
val strafe = 1 + Vehicles.CargoOrientation(vehicle)
|
||||
val reverseSpeed = if (strafe > 1) { 0 } else { speed }
|
||||
//strafe or reverse, not both
|
||||
sessionData.vehicles.ServerVehicleOverrideWithPacket(
|
||||
sessionLogic.vehicles.ServerVehicleOverrideWithPacket(
|
||||
vehicle,
|
||||
ServerVehicleOverrideMsg(
|
||||
lock_accelerator=true,
|
||||
|
|
@ -253,8 +253,8 @@ class SessionVehicleHandlers(
|
|||
)
|
||||
|
||||
case VehicleResponse.KickCargo(cargo, _, _)
|
||||
if player.VehicleSeated.nonEmpty && sessionData.zoning.spawn.deadState == DeadState.Alive =>
|
||||
sessionData.vehicles.TotalDriverVehicleControl(cargo)
|
||||
if player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive =>
|
||||
sessionLogic.vehicles.TotalDriverVehicleControl(cargo)
|
||||
|
||||
case VehicleResponse.StartPlayerSeatedInVehicle(vehicle, _)
|
||||
if player.VisibleSlots.contains(player.DrawnSlot) =>
|
||||
|
|
@ -266,7 +266,7 @@ class SessionVehicleHandlers(
|
|||
|
||||
case VehicleResponse.PlayerSeatedInVehicle(vehicle, _) =>
|
||||
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
|
||||
sessionData.vehicles.ServerVehicleOverrideWithPacket(
|
||||
sessionLogic.vehicles.ServerVehicleOverrideWithPacket(
|
||||
vehicle,
|
||||
ServerVehicleOverrideMsg(
|
||||
lock_accelerator=true,
|
||||
|
|
@ -279,11 +279,11 @@ class SessionVehicleHandlers(
|
|||
unk8=Some(0)
|
||||
)
|
||||
)
|
||||
sessionData.vehicles.serverVehicleControlVelocity = Some(0)
|
||||
sessionLogic.vehicles.serverVehicleControlVelocity = Some(0)
|
||||
|
||||
case VehicleResponse.ServerVehicleOverrideStart(vehicle, _) =>
|
||||
val vdef = vehicle.Definition
|
||||
sessionData.vehicles.ServerVehicleOverrideWithPacket(
|
||||
sessionLogic.vehicles.ServerVehicleOverrideWithPacket(
|
||||
vehicle,
|
||||
ServerVehicleOverrideMsg(
|
||||
lock_accelerator=true,
|
||||
|
|
@ -298,7 +298,7 @@ class SessionVehicleHandlers(
|
|||
)
|
||||
|
||||
case VehicleResponse.ServerVehicleOverrideEnd(vehicle, _) =>
|
||||
sessionData.vehicles.ServerVehicleOverrideStop(vehicle)
|
||||
sessionLogic.vehicles.ServerVehicleOverrideStop(vehicle)
|
||||
|
||||
case VehicleResponse.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, data) =>
|
||||
sendResponse(ChatMsg(
|
||||
|
|
@ -328,7 +328,7 @@ class SessionVehicleHandlers(
|
|||
sendResponse(ObjectDeleteMessage(eguid, unk1=0))
|
||||
TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj))
|
||||
}
|
||||
sessionData.applyPurchaseTimersBeforePackingLoadout(player, vehicle, addedWeapons ++ newInventory)
|
||||
sessionLogic.general.applyPurchaseTimersBeforePackingLoadout(player, vehicle, addedWeapons ++ newInventory)
|
||||
//jammer or unjamm new weapons based on vehicle status
|
||||
val vehicleJammered = vehicle.Jammed
|
||||
addedWeapons
|
||||
|
|
@ -342,7 +342,7 @@ class SessionVehicleHandlers(
|
|||
}
|
||||
|
||||
case VehicleResponse.ChangeLoadout(target, oldWeapons, _, oldInventory, _)
|
||||
if sessionData.accessedContainer.map { _.GUID }.contains(target) =>
|
||||
if sessionLogic.general.accessedContainer.map(_.GUID).contains(target) =>
|
||||
//TODO when vehicle weapons can be changed without visual glitches, rewrite this
|
||||
continent.GUID(target).collect { case vehicle: Vehicle =>
|
||||
//external participant: observe changes to equipment
|
||||
|
|
@ -371,7 +371,7 @@ class SessionVehicleHandlers(
|
|||
(oldWeapons ++ oldInventory).foreach {
|
||||
case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, unk1=0))
|
||||
}
|
||||
sessionData.updateWeaponAtSeatPosition(vehicle, seatNum)
|
||||
sessionLogic.mountResponse.updateWeaponAtSeatPosition(vehicle, seatNum)
|
||||
case None =>
|
||||
//observer: observe changes to external equipment
|
||||
oldWeapons.foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, unk1=0)) }
|
||||
|
|
@ -380,9 +380,9 @@ class SessionVehicleHandlers(
|
|||
|
||||
private def startPlayerSeatedInVehicle(vehicle: Vehicle): Unit = {
|
||||
val vehicle_guid = vehicle.GUID
|
||||
sessionData.playerActionsToCancel()
|
||||
sessionData.terminals.CancelAllProximityUnits()
|
||||
sessionData.vehicles.serverVehicleControlVelocity = Some(0)
|
||||
sessionLogic.actionsToCancel()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.vehicles.serverVehicleControlVelocity = Some(0)
|
||||
sendResponse(PlanetsideAttributeMessage(vehicle_guid, attribute_type=22, attribute_value=1L)) //mount points off
|
||||
sendResponse(PlanetsideAttributeMessage(player.GUID, attribute_type=21, vehicle_guid)) //ownership
|
||||
vehicle.MountPoints.find { case (_, mp) => mp.seatIndex == 0 }.collect {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
|||
import net.psforever.types.{BailType, DriveState, Vector3}
|
||||
|
||||
class VehicleOperations(
|
||||
val sessionData: SessionData,
|
||||
val sessionLogic: SessionLogic,
|
||||
avatarActor: typed.ActorRef[AvatarActor.Command],
|
||||
implicit val context: ActorContext
|
||||
) extends CommonSessionInterfacingFunctionality {
|
||||
|
|
@ -40,11 +40,11 @@ class VehicleOperations(
|
|||
GetVehicleAndSeat() match {
|
||||
case (Some(obj), Some(0)) =>
|
||||
//we're driving the vehicle
|
||||
sessionData.persist()
|
||||
sessionData.turnCounterFunc(player.GUID)
|
||||
sessionData.fallHeightTracker(pos.z)
|
||||
sessionLogic.persist()
|
||||
sessionLogic.turnCounterFunc(player.GUID)
|
||||
sessionLogic.general.fallHeightTracker(pos.z)
|
||||
if (obj.MountedIn.isEmpty) {
|
||||
sessionData.updateBlockMap(obj, pos)
|
||||
sessionLogic.updateBlockMap(obj, pos)
|
||||
}
|
||||
player.Position = pos //convenient
|
||||
if (obj.WeaponControlledFromSeat(0).isEmpty) {
|
||||
|
|
@ -87,7 +87,7 @@ class VehicleOperations(
|
|||
obj.Cloaked
|
||||
)
|
||||
)
|
||||
sessionData.squad.updateSquad()
|
||||
sessionLogic.squad.updateSquad()
|
||||
obj.zoneInteractions()
|
||||
case (None, _) =>
|
||||
//log.error(s"VehicleState: no vehicle $vehicle_guid found in zone")
|
||||
|
|
@ -99,7 +99,7 @@ class VehicleOperations(
|
|||
case _ => ;
|
||||
}
|
||||
if (player.death_by == -1) {
|
||||
sessionData.kickedByAdministration()
|
||||
sessionLogic.kickedByAdministration()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -123,11 +123,11 @@ class VehicleOperations(
|
|||
GetVehicleAndSeat() match {
|
||||
case (Some(obj), Some(0)) =>
|
||||
//we're driving the vehicle
|
||||
sessionData.persist()
|
||||
sessionData.turnCounterFunc(player.GUID)
|
||||
sessionLogic.persist()
|
||||
sessionLogic.turnCounterFunc(player.GUID)
|
||||
val (position, angle, velocity, notMountedState) = continent.GUID(obj.MountedIn) match {
|
||||
case Some(v: Vehicle) =>
|
||||
sessionData.updateBlockMap(obj, pos)
|
||||
sessionLogic.updateBlockMap(obj, pos)
|
||||
(pos, v.Orientation - Vector3.z(value = 90f) * Vehicles.CargoOrientation(obj).toFloat, v.Velocity, false)
|
||||
case _ =>
|
||||
(pos, ang, vel, true)
|
||||
|
|
@ -186,7 +186,7 @@ class VehicleOperations(
|
|||
unkA
|
||||
)
|
||||
)
|
||||
sessionData.squad.updateSquad()
|
||||
sessionLogic.squad.updateSquad()
|
||||
case (None, _) =>
|
||||
//log.error(s"VehicleState: no vehicle $vehicle_guid found in zone")
|
||||
//TODO placing a "not driving" warning here may trigger as we are disembarking the vehicle
|
||||
|
|
@ -197,13 +197,13 @@ class VehicleOperations(
|
|||
case _ => ;
|
||||
}
|
||||
if (player.death_by == -1) {
|
||||
sessionData.kickedByAdministration()
|
||||
sessionLogic.kickedByAdministration()
|
||||
}
|
||||
}
|
||||
|
||||
def handleChildObjectState(pkt: ChildObjectStateMessage): Unit = {
|
||||
val ChildObjectStateMessage(object_guid, pitch, yaw) = pkt
|
||||
val (o, tools) = sessionData.shooting.FindContainedWeapon
|
||||
val (o, tools) = sessionLogic.shooting.FindContainedWeapon
|
||||
//is COSM our primary upstream packet?
|
||||
(o match {
|
||||
case Some(mount: Mountable) => (o, mount.PassengerInSeat(player))
|
||||
|
|
@ -211,8 +211,8 @@ class VehicleOperations(
|
|||
}) match {
|
||||
case (None, None) | (_, None) | (Some(_: Vehicle), Some(0)) => ;
|
||||
case _ =>
|
||||
sessionData.persist()
|
||||
sessionData.turnCounterFunc(player.GUID)
|
||||
sessionLogic.persist()
|
||||
sessionLogic.turnCounterFunc(player.GUID)
|
||||
}
|
||||
//the majority of the following check retrieves information to determine if we are in control of the child
|
||||
tools.find { _.GUID == object_guid } match {
|
||||
|
|
@ -231,19 +231,19 @@ class VehicleOperations(
|
|||
}
|
||||
//TODO status condition of "playing getting out of vehicle to allow for late packets without warning
|
||||
if (player.death_by == -1) {
|
||||
sessionData.kickedByAdministration()
|
||||
sessionLogic.kickedByAdministration()
|
||||
}
|
||||
}
|
||||
|
||||
def handleVehicleSubState(pkt: VehicleSubStateMessage): Unit = {
|
||||
val VehicleSubStateMessage(vehicle_guid, _, pos, ang, vel, unk1, _) = pkt
|
||||
sessionData.validObject(vehicle_guid, decorator = "VehicleSubState") match {
|
||||
sessionLogic.validObject(vehicle_guid, decorator = "VehicleSubState") match {
|
||||
case Some(obj: Vehicle) =>
|
||||
import net.psforever.login.WorldSession.boolToInt
|
||||
obj.Position = pos
|
||||
obj.Orientation = ang
|
||||
obj.Velocity = vel
|
||||
sessionData.updateBlockMap(obj, pos)
|
||||
sessionLogic.updateBlockMap(obj, pos)
|
||||
obj.zoneInteractions()
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
|
|
@ -268,7 +268,7 @@ class VehicleOperations(
|
|||
|
||||
def handleMountVehicle(pkt: MountVehicleMsg): Unit = {
|
||||
val MountVehicleMsg(_, mountable_guid, entry_point) = pkt
|
||||
sessionData.validObject(mountable_guid, decorator = "MountVehicle").collect {
|
||||
sessionLogic.validObject(mountable_guid, decorator = "MountVehicle").collect {
|
||||
case obj: Mountable =>
|
||||
obj.Actor ! Mountable.TryMount(player, entry_point)
|
||||
case _ =>
|
||||
|
|
@ -283,7 +283,7 @@ class VehicleOperations(
|
|||
//common warning for this section
|
||||
if (player.GUID == player_guid) {
|
||||
//normally disembarking from a mount
|
||||
(sessionData.zoning.interstellarFerry.orElse(continent.GUID(player.VehicleSeated)) match {
|
||||
(sessionLogic.zoning.interstellarFerry.orElse(continent.GUID(player.VehicleSeated)) match {
|
||||
case out @ Some(obj: Vehicle) =>
|
||||
continent.GUID(obj.MountedIn) match {
|
||||
case Some(_: Vehicle) => None //cargo vehicle
|
||||
|
|
@ -300,7 +300,7 @@ class VehicleOperations(
|
|||
case Some(seat_num) =>
|
||||
obj.Actor ! Mountable.TryDismount(player, seat_num, bailType)
|
||||
//short-circuit the temporary channel for transferring between zones, the player is no longer doing that
|
||||
sessionData.zoning.interstellarFerry = None
|
||||
sessionLogic.zoning.interstellarFerry = None
|
||||
// Deconstruct the vehicle if the driver has bailed out and the vehicle is capable of flight
|
||||
//todo: implement auto landing procedure if the pilot bails but passengers are still present instead of deconstructing the vehicle
|
||||
//todo: continue flight path until aircraft crashes if no passengers present (or no passenger seats), then deconstruct.
|
||||
|
|
@ -327,8 +327,8 @@ class VehicleOperations(
|
|||
case Some(obj_guid) =>
|
||||
(
|
||||
(
|
||||
sessionData.validObject(obj_guid, decorator = "DismountVehicle/Vehicle"),
|
||||
sessionData.validObject(player_guid, decorator = "DismountVehicle/Player")
|
||||
sessionLogic.validObject(obj_guid, decorator = "DismountVehicle/Vehicle"),
|
||||
sessionLogic.validObject(player_guid, decorator = "DismountVehicle/Player")
|
||||
) match {
|
||||
case (vehicle @ Some(obj: Vehicle), tplayer) =>
|
||||
if (obj.MountedIn.isEmpty) (vehicle, tplayer) else (None, None)
|
||||
|
|
@ -508,7 +508,7 @@ class VehicleOperations(
|
|||
* `(None, None)`, otherwise (even if the vehicle can be determined)
|
||||
*/
|
||||
def GetKnownVehicleAndSeat(): (Option[Vehicle], Option[Int]) =
|
||||
GetMountableAndSeat(sessionData.zoning.interstellarFerry, player, continent) match {
|
||||
GetMountableAndSeat(sessionLogic.zoning.interstellarFerry, player, continent) match {
|
||||
case (Some(v: Vehicle), Some(seat)) => (Some(v), Some(seat))
|
||||
case _ => (None, None)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
|||
import scala.concurrent.Future
|
||||
import scala.concurrent.duration._
|
||||
//
|
||||
import net.psforever.actors.session.{AvatarActor, ChatActor, SessionActor}
|
||||
import net.psforever.actors.session.{AvatarActor, ChatActor}
|
||||
import net.psforever.login.WorldSession.{CountAmmunition, CountGrenades, FindAmmoBoxThatUses, FindEquipmentStock, FindToolThatUses, PutEquipmentInInventoryOrDrop, PutNewEquipmentInInventoryOrDrop, RemoveOldEquipmentFromInventory}
|
||||
import net.psforever.objects.avatar.scoring.EquipmentStat
|
||||
import net.psforever.objects.ballistics.{Projectile, ProjectileQuality}
|
||||
|
|
@ -42,10 +42,10 @@ import net.psforever.types.{ExoSuitType, PlanetSideGUID, Vector3}
|
|||
import net.psforever.util.Config
|
||||
|
||||
private[support] class WeaponAndProjectileOperations(
|
||||
val sessionData: SessionData,
|
||||
avatarActor: typed.ActorRef[AvatarActor.Command],
|
||||
chatActor: typed.ActorRef[ChatActor.Command],
|
||||
implicit val context: ActorContext
|
||||
val sessionLogic: SessionLogic,
|
||||
avatarActor: typed.ActorRef[AvatarActor.Command],
|
||||
chatActor: typed.ActorRef[ChatActor.Command],
|
||||
implicit val context: ActorContext
|
||||
) extends CommonSessionInterfacingFunctionality {
|
||||
var shooting: mutable.Set[PlanetSideGUID] = mutable.Set.empty //ChangeFireStateMessage_Start
|
||||
var prefire: mutable.Set[PlanetSideGUID] = mutable.Set.empty //if WeaponFireMessage precedes ChangeFireStateMessage_Start
|
||||
|
|
@ -111,8 +111,8 @@ private[support] class WeaponAndProjectileOperations(
|
|||
val WeaponLazeTargetPositionMessage(_, _, _) = pkt
|
||||
//do not need to handle the progress bar animation/state on the server
|
||||
//laze waypoint is requested by client upon completion (see SquadWaypointRequest)
|
||||
val purpose = if (sessionData.squad.squad_supplement_id > 0) {
|
||||
s" for ${player.Sex.possessive} squad (#${sessionData.squad.squad_supplement_id -1})"
|
||||
val purpose = if (sessionLogic.squad.squad_supplement_id > 0) {
|
||||
s" for ${player.Sex.possessive} squad (#${sessionLogic.squad.squad_supplement_id -1})"
|
||||
} else {
|
||||
" ..."
|
||||
}
|
||||
|
|
@ -128,7 +128,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
def handleChangeFireStateStart(pkt: ChangeFireStateMessage_Start)(implicit context: ActorContext): Unit = {
|
||||
val ChangeFireStateMessage_Start(item_guid) = pkt
|
||||
if (shooting.isEmpty) {
|
||||
sessionData.findEquipment(item_guid) match {
|
||||
sessionLogic.findEquipment(item_guid) match {
|
||||
case Some(tool: Tool) if player.VehicleSeated.isEmpty =>
|
||||
fireStateStartWhenPlayer(tool, item_guid)
|
||||
case Some(tool: Tool) =>
|
||||
|
|
@ -151,7 +151,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
prefire -= item_guid
|
||||
shootingStop += item_guid -> now
|
||||
shooting -= item_guid
|
||||
sessionData.findEquipment(item_guid) match {
|
||||
sessionLogic.findEquipment(item_guid) match {
|
||||
case Some(tool: Tool) if player.VehicleSeated.isEmpty =>
|
||||
fireStateStopWhenPlayer(tool, item_guid)
|
||||
case Some(tool: Tool) =>
|
||||
|
|
@ -169,8 +169,8 @@ private[support] class WeaponAndProjectileOperations(
|
|||
case _ =>
|
||||
log.warn(s"ChangeFireState_Stop: can not find $item_guid")
|
||||
}
|
||||
sessionData.progressBarUpdate.cancel()
|
||||
sessionData.progressBarValue = None
|
||||
sessionLogic.general.progressBarUpdate.cancel()
|
||||
sessionLogic.general.progressBarValue = None
|
||||
}
|
||||
|
||||
def handleReload(pkt: ReloadMessage): Unit = {
|
||||
|
|
@ -187,7 +187,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
|
||||
def handleChangeAmmo(pkt: ChangeAmmoMessage): Unit = {
|
||||
val ChangeAmmoMessage(item_guid, _) = pkt
|
||||
val (thing, equipment) = sessionData.findContainedEquipment()
|
||||
val (thing, equipment) = sessionLogic.findContainedEquipment()
|
||||
if (equipment.isEmpty) {
|
||||
log.warn(s"ChangeAmmo: either can not find $item_guid or the object found was not Equipment")
|
||||
} else {
|
||||
|
|
@ -216,7 +216,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
|
||||
def handleChangeFireMode(pkt: ChangeFireModeMessage): Unit = {
|
||||
val ChangeFireModeMessage(item_guid, _/*fire_mode*/) = pkt
|
||||
sessionData.findEquipment(item_guid) match {
|
||||
sessionLogic.findEquipment(item_guid) match {
|
||||
case Some(obj: PlanetSideGameObject with FireModeSwitch[_]) =>
|
||||
val originalModeIndex = obj.FireModeIndex
|
||||
if (obj match {
|
||||
|
|
@ -306,7 +306,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
(hit_info match {
|
||||
case Some(hitInfo) =>
|
||||
val hitPos = hitInfo.hit_pos
|
||||
sessionData.validObject(hitInfo.hitobject_guid, decorator = "Hit/hitInfo") match {
|
||||
sessionLogic.validObject(hitInfo.hitobject_guid, decorator = "Hit/hitInfo") match {
|
||||
case _ if projectile.profile == GlobalDefinitions.flail_projectile =>
|
||||
val radius = projectile.profile.DamageRadius * projectile.profile.DamageRadius
|
||||
val targets = Zone.findAllTargets(continent, player, hitPos, projectile.profile)
|
||||
|
|
@ -340,7 +340,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
) =>
|
||||
ResolveProjectileInteraction(proj, DamageResolution.Hit, target, hitPos).collect { resprojectile =>
|
||||
addShotsLanded(resprojectile.cause.attribution, shots = 1)
|
||||
sessionData.handleDealingDamage(target, resprojectile)
|
||||
sessionLogic.handleDealingDamage(target, resprojectile)
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
|
|
@ -371,23 +371,23 @@ private[support] class WeaponAndProjectileOperations(
|
|||
(DamageResolution.Splash, DamageResolution.Splash)
|
||||
}
|
||||
//direct_victim_uid
|
||||
sessionData.validObject(direct_victim_uid, decorator = "SplashHit/direct_victim") match {
|
||||
sessionLogic.validObject(direct_victim_uid, decorator = "SplashHit/direct_victim") match {
|
||||
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||
CheckForHitPositionDiscrepancy(projectile_guid, explosion_pos, target)
|
||||
ResolveProjectileInteraction(projectile, resolution1, target, target.Position).collect { resprojectile =>
|
||||
addShotsLanded(resprojectile.cause.attribution, shots = 1)
|
||||
sessionData.handleDealingDamage(target, resprojectile)
|
||||
sessionLogic.handleDealingDamage(target, resprojectile)
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
//other victims
|
||||
targets.foreach(elem => {
|
||||
sessionData.validObject(elem.uid, decorator = "SplashHit/other_victims") match {
|
||||
sessionLogic.validObject(elem.uid, decorator = "SplashHit/other_victims") match {
|
||||
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||
CheckForHitPositionDiscrepancy(projectile_guid, explosion_pos, target)
|
||||
ResolveProjectileInteraction(projectile, resolution2, target, explosion_pos).collect { resprojectile =>
|
||||
addShotsLanded(resprojectile.cause.attribution, shots = 1)
|
||||
sessionData.handleDealingDamage(target, resprojectile)
|
||||
sessionLogic.handleDealingDamage(target, resprojectile)
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
|
|
@ -421,13 +421,13 @@ private[support] class WeaponAndProjectileOperations(
|
|||
|
||||
def handleLashHit(pkt: LashMessage): Unit = {
|
||||
val LashMessage(_, _, victim_guid, projectile_guid, hit_pos, _) = pkt
|
||||
sessionData.validObject(victim_guid, decorator = "Lash") match {
|
||||
sessionLogic.validObject(victim_guid, decorator = "Lash") match {
|
||||
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
|
||||
CheckForHitPositionDiscrepancy(projectile_guid, hit_pos, target)
|
||||
ResolveProjectileInteraction(projectile_guid, DamageResolution.Lash, target, hit_pos).foreach {
|
||||
resprojectile =>
|
||||
addShotsLanded(resprojectile.cause.attribution, shots = 1)
|
||||
sessionData.handleDealingDamage(target, resprojectile)
|
||||
sessionLogic.handleDealingDamage(target, resprojectile)
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
|
|
@ -454,7 +454,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
None
|
||||
}).collect {
|
||||
case target: AutomatedTurret.Target =>
|
||||
sessionData.validObject(attackerGuid, decorator = "AIDamage/AutomatedTurret")
|
||||
sessionLogic.validObject(attackerGuid, decorator = "AIDamage/AutomatedTurret")
|
||||
.collect {
|
||||
case turret: AutomatedTurret if turret.Target.isEmpty =>
|
||||
turret.Actor ! AutomatedTurretBehavior.ConfirmShot(target)
|
||||
|
|
@ -468,10 +468,10 @@ private[support] class WeaponAndProjectileOperations(
|
|||
}
|
||||
.orElse {
|
||||
//occasionally, something that is not technically a turret's natural target may be attacked
|
||||
sessionData.validObject(targetGuid, decorator = "AIDamage/Target")
|
||||
sessionLogic.validObject(targetGuid, decorator = "AIDamage/Target")
|
||||
.collect {
|
||||
case target: PlanetSideServerObject with FactionAffinity with Vitality =>
|
||||
sessionData.validObject(attackerGuid, decorator = "AIDamage/Attacker")
|
||||
sessionLogic.validObject(attackerGuid, decorator = "AIDamage/Attacker")
|
||||
.collect {
|
||||
case turret: AutomatedTurret if turret.Target.nonEmpty =>
|
||||
//the turret must be shooting at something (else) first
|
||||
|
|
@ -587,11 +587,11 @@ private[support] class WeaponAndProjectileOperations(
|
|||
projectileGUID: PlanetSideGUID
|
||||
): (Option[PlanetSideGameObject with Container], Option[Tool]) = {
|
||||
if (player.ZoningRequest != Zoning.Method.None) {
|
||||
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_fire")
|
||||
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_fire")
|
||||
}
|
||||
if (player.isShielded) {
|
||||
// Cancel NC MAX shield if it's active
|
||||
sessionData.toggleMaxSpecialState(enable = false)
|
||||
sessionLogic.general.toggleMaxSpecialState(enable = false)
|
||||
}
|
||||
val (o, tools) = FindContainedWeapon
|
||||
val (_, enabledTools) = FindEnabledWeaponsToHandleWeaponFireAccountability(o, tools)
|
||||
|
|
@ -893,7 +893,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
case Some(_) =>
|
||||
stowFunc(previousBox)
|
||||
case None =>
|
||||
sessionData.normalItemDrop(player, continent)(previousBox)
|
||||
sessionLogic.general.normalItemDrop(player, continent)(previousBox)
|
||||
}
|
||||
AmmoBox.Split(previousBox) match {
|
||||
case Nil | List(_) => () //done (the former case is technically not possible)
|
||||
|
|
@ -919,7 +919,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
*/
|
||||
def FindDetectedProjectileTargets(targets: Iterable[PlanetSideGUID]): Iterable[String] = {
|
||||
targets
|
||||
.map { sessionData.validObject(_, decorator="FindDetectedProjectileTargets") }
|
||||
.map { sessionLogic.validObject(_, decorator="FindDetectedProjectileTargets") }
|
||||
.flatMap {
|
||||
case Some(obj: Vehicle) if !obj.Cloaked =>
|
||||
//TODO hint: vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.ProjectileAutoLockAwareness(mode))
|
||||
|
|
@ -1163,7 +1163,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
* the second value is an `Tool` object in the former
|
||||
*/
|
||||
def FindContainedWeapon: (Option[PlanetSideGameObject with Container], Set[Tool]) = {
|
||||
sessionData.findContainedEquipment() match {
|
||||
sessionLogic.findContainedEquipment() match {
|
||||
case (container, equipment) =>
|
||||
(container, equipment collect { case t: Tool => t })
|
||||
case _ =>
|
||||
|
|
@ -1207,11 +1207,11 @@ private[support] class WeaponAndProjectileOperations(
|
|||
//charge ammunition drain
|
||||
tool.FireMode match {
|
||||
case mode: ChargeFireModeDefinition =>
|
||||
sessionData.progressBarValue = Some(0f)
|
||||
sessionData.progressBarUpdate = context.system.scheduler.scheduleOnce(
|
||||
sessionLogic.general.progressBarValue = Some(0f)
|
||||
sessionLogic.general.progressBarUpdate = context.system.scheduler.scheduleOnce(
|
||||
(mode.Time + mode.DrainInterval) milliseconds,
|
||||
context.self,
|
||||
SessionActor.ProgressEvent(1f, () => {}, Tools.ChargeFireMode(player, tool), mode.DrainInterval)
|
||||
CommonMessages.ProgressEvent(1f, () => {}, Tools.ChargeFireMode(player, tool), mode.DrainInterval)
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
|
|
@ -1225,7 +1225,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
}
|
||||
|
||||
private def fireStateStartMountedMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
sessionData.findContainedEquipment()._1.collect {
|
||||
sessionLogic.findContainedEquipment()._1.collect {
|
||||
case turret: FacilityTurret if continent.map.cavern =>
|
||||
turret.Actor ! VanuSentry.ChangeFireStart
|
||||
}
|
||||
|
|
@ -1291,7 +1291,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
}
|
||||
|
||||
private def fireStateStopMountedMessages(itemGuid: PlanetSideGUID): Unit = {
|
||||
sessionData.findContainedEquipment()._1.collect {
|
||||
sessionLogic.findContainedEquipment()._1.collect {
|
||||
case turret: FacilityTurret if continent.map.cavern =>
|
||||
turret.Actor ! VanuSentry.ChangeFireStop
|
||||
}
|
||||
|
|
@ -1498,7 +1498,7 @@ private[support] class WeaponAndProjectileOperations(
|
|||
val hitPos = target.Position + Vector3.z(value = 1f)
|
||||
ResolveProjectileInteraction(proj, DamageResolution.Hit, target, hitPos).collect { resprojectile =>
|
||||
addShotsLanded(resprojectile.cause.attribution, shots = 1)
|
||||
sessionData.handleDealingDamage(target, resprojectile)
|
||||
sessionLogic.handleDealingDamage(target, resprojectile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1585,7 +1585,21 @@ private[support] class WeaponAndProjectileOperations(
|
|||
}.filter(p => Vector3.DistanceSquared(start, p) <= a)
|
||||
}
|
||||
|
||||
override protected[session] def stop(): Unit = {
|
||||
override protected[support] def actionsToCancel(): Unit = {
|
||||
shootingStart.clear()
|
||||
shootingStop.clear()
|
||||
(prefire ++ shooting).foreach { guid =>
|
||||
sendResponse(ChangeFireStateMessage_Stop(guid))
|
||||
continent.AvatarEvents ! AvatarServiceMessage(
|
||||
continent.id,
|
||||
AvatarAction.ChangeFireState_Stop(player.GUID, guid)
|
||||
)
|
||||
}
|
||||
prefire.clear()
|
||||
shooting.clear()
|
||||
}
|
||||
|
||||
override protected[support] def stop(): Unit = {
|
||||
if (player != null && player.HasGUID) {
|
||||
(prefire ++ shooting).foreach { guid =>
|
||||
//do I need to do this? (maybe)
|
||||
|
|
|
|||
|
|
@ -73,6 +73,11 @@ import net.psforever.util.{Config, DefinitionUtil}
|
|||
import net.psforever.zones.Zones
|
||||
|
||||
object ZoningOperations {
|
||||
private[support] final case class AvatarAwardMessageBundle(
|
||||
bundle: Iterable[Iterable[PlanetSideGamePacket]],
|
||||
delay: Long
|
||||
)
|
||||
|
||||
private final val zoningCountdownMessages: Seq[Int] = Seq(5, 10, 20)
|
||||
|
||||
def reportProgressionSystem(sessionActor: ActorRef): Unit = {
|
||||
|
|
@ -101,7 +106,7 @@ object ZoningOperations {
|
|||
}
|
||||
|
||||
class ZoningOperations(
|
||||
val sessionData: SessionData,
|
||||
val sessionLogic: SessionLogic,
|
||||
avatarActor: typed.ActorRef[AvatarActor.Command],
|
||||
galaxyService: ActorRef,
|
||||
cluster: typed.ActorRef[ICS.Command],
|
||||
|
|
@ -147,7 +152,7 @@ class ZoningOperations(
|
|||
CancelZoningProcessWithDescriptiveReason("cancel_use")
|
||||
if (spawn.deadState != DeadState.RespawnTime) {
|
||||
continent.Buildings.values.find(_.GUID == building_guid) match {
|
||||
case Some(wg: WarpGate) if wg.Active && (sessionData.vehicles.GetKnownVehicleAndSeat() match {
|
||||
case Some(wg: WarpGate) if wg.Active && (sessionLogic.vehicles.GetKnownVehicleAndSeat() match {
|
||||
case (Some(vehicle), _) =>
|
||||
wg.Definition.VehicleAllowance && !wg.Definition.NoWarp.contains(vehicle.Definition)
|
||||
case _ =>
|
||||
|
|
@ -202,7 +207,7 @@ class ZoningOperations(
|
|||
continent.VehicleEvents ! Service.Join(name)
|
||||
continent.VehicleEvents ! Service.Join(continentId)
|
||||
continent.VehicleEvents ! Service.Join(factionChannel)
|
||||
if (sessionData.connectionState != 100) configZone(continent)
|
||||
if (sessionLogic.connectionState != 100) configZone(continent)
|
||||
sendResponse(TimeOfDayMessage(1191182336))
|
||||
//custom
|
||||
sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list
|
||||
|
|
@ -487,7 +492,7 @@ class ZoningOperations(
|
|||
//the router won't work if it doesn't completely deploy
|
||||
sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deploying, 0, unk3=false, Vector3.Zero))
|
||||
sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deployed, 0, unk3=false, Vector3.Zero))
|
||||
sessionData.toggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent))
|
||||
sessionLogic.general.toggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent))
|
||||
}
|
||||
ServiceManager.serviceManager
|
||||
.ask(Lookup("hart"))(Timeout(2 seconds))
|
||||
|
|
@ -656,8 +661,8 @@ class ZoningOperations(
|
|||
//the following subscriptions last until character switch/logout
|
||||
galaxyService ! Service.Join("galaxy") //for galaxy-wide messages
|
||||
galaxyService ! Service.Join(s"${avatar.faction}") //for hotspots, etc.
|
||||
sessionData.squadService ! Service.Join(s"${avatar.faction}") //channel will be player.Faction
|
||||
sessionData.squadService ! Service.Join(s"${avatar.id}") //channel will be player.CharId (in order to work with packets)
|
||||
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)
|
||||
player.Zone match {
|
||||
case Zone.Nowhere =>
|
||||
RandomSanctuarySpawnPosition(player)
|
||||
|
|
@ -669,7 +674,7 @@ class ZoningOperations(
|
|||
session = session.copy(zone = zone)
|
||||
//the only zone-level event system subscription necessary before BeginZoningMessage (for persistence purposes)
|
||||
zone.AvatarEvents ! Service.Join(player.Name)
|
||||
sessionData.persist()
|
||||
sessionLogic.persist()
|
||||
oldZone.AvatarEvents ! Service.Leave()
|
||||
oldZone.LocalEvents ! Service.Leave()
|
||||
oldZone.VehicleEvents ! Service.Leave()
|
||||
|
|
@ -699,7 +704,7 @@ class ZoningOperations(
|
|||
loadConfZone = true
|
||||
val oldZone = session.zone
|
||||
session = session.copy(zone = foundZone)
|
||||
sessionData.persist()
|
||||
sessionLogic.persist()
|
||||
oldZone.AvatarEvents ! Service.Leave()
|
||||
oldZone.LocalEvents ! Service.Leave()
|
||||
oldZone.VehicleEvents ! Service.Leave()
|
||||
|
|
@ -709,9 +714,9 @@ class ZoningOperations(
|
|||
player.avatar = avatar
|
||||
interstellarFerry match {
|
||||
case Some(vehicle) if vehicle.PassengerInSeat(player).contains(0) =>
|
||||
TaskWorkflow.execute(sessionData.registerDrivenVehicle(vehicle, player))
|
||||
TaskWorkflow.execute(registerDrivenVehicle(vehicle, player))
|
||||
case _ =>
|
||||
TaskWorkflow.execute(sessionData.registerNewAvatar(player))
|
||||
TaskWorkflow.execute(registerNewAvatar(player))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -749,9 +754,9 @@ class ZoningOperations(
|
|||
}
|
||||
val previousZoningType = ztype
|
||||
CancelZoningProcess()
|
||||
sessionData.playerActionsToCancel()
|
||||
sessionData.terminals.CancelAllProximityUnits()
|
||||
sessionData.dropSpecialSlotItem()
|
||||
sessionLogic.actionsToCancel()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.general.dropSpecialSlotItem()
|
||||
continent.Population ! Zone.Population.Release(avatar)
|
||||
spawn.resolveZoningSpawnPointLoad(response, previousZoningType)
|
||||
}
|
||||
|
|
@ -859,13 +864,13 @@ class ZoningOperations(
|
|||
zoningStatus = Zoning.Status.Request
|
||||
beginZoningCountdown(() => {
|
||||
log.info(s"Good-bye, ${player.Name}")
|
||||
sessionData.immediateDisconnect()
|
||||
sessionLogic.immediateDisconnect()
|
||||
})
|
||||
}
|
||||
|
||||
def handleSetZone(zoneId: String, position: Vector3): Unit = {
|
||||
if (sessionData.vehicles.serverVehicleControlVelocity.isEmpty) {
|
||||
sessionData.playerActionsToCancel()
|
||||
if (sessionLogic.vehicles.serverVehicleControlVelocity.isEmpty) {
|
||||
sessionLogic.actionsToCancel()
|
||||
continent.GUID(player.VehicleSeated) match {
|
||||
case Some(vehicle: Vehicle) if vehicle.MountedIn.isEmpty =>
|
||||
vehicle.PassengerInSeat(player) match {
|
||||
|
|
@ -1143,13 +1148,13 @@ class ZoningOperations(
|
|||
//sync hack state
|
||||
amenity.Definition match {
|
||||
case GlobalDefinitions.capture_terminal =>
|
||||
sessionData.sendPlanetsideAttributeMessage(
|
||||
sessionLogic.general.sendPlanetsideAttributeMessage(
|
||||
amenity.GUID,
|
||||
PlanetsideAttributeEnum.ControlConsoleHackUpdate,
|
||||
HackCaptureActor.GetHackUpdateAttributeValue(amenity.asInstanceOf[CaptureTerminal], isResecured = false)
|
||||
)
|
||||
case _ =>
|
||||
sessionData.hackObject(amenity.GUID, 1114636288L, 8L) //generic hackable object
|
||||
sessionLogic.general.hackObject(amenity.GUID, 1114636288L, 8L) //generic hackable object
|
||||
}
|
||||
|
||||
// sync capture flags
|
||||
|
|
@ -1216,7 +1221,7 @@ class ZoningOperations(
|
|||
if (!zoneReload && zoneId.equals(continent.id)) {
|
||||
if (player.isBackpack) { // important! test the actor-wide player ref, not the parameter
|
||||
// respawning from unregistered player
|
||||
TaskWorkflow.execute(sessionData.registerAvatar(targetPlayer))
|
||||
TaskWorkflow.execute(registerAvatar(targetPlayer))
|
||||
} else {
|
||||
// move existing player; this is the one case where the original GUID is retained by the player
|
||||
context.self ! SessionActor.PlayerLoaded(targetPlayer)
|
||||
|
|
@ -1336,7 +1341,7 @@ class ZoningOperations(
|
|||
ICS.FindZone(_.id == zoneId, context.self)
|
||||
))
|
||||
} else {
|
||||
sessionData.unaccessContainer(vehicle)
|
||||
sessionLogic.general.unaccessContainer(vehicle)
|
||||
LoadZoneCommonTransferActivity()
|
||||
player.VehicleSeated = vehicle.GUID
|
||||
player.Continent = zoneId //forward-set the continent id to perform a test
|
||||
|
|
@ -1354,7 +1359,7 @@ class ZoningOperations(
|
|||
//unregister vehicle and driver whole + GiveWorld
|
||||
continent.Transport ! Zone.Vehicle.Despawn(vehicle)
|
||||
TaskWorkflow.execute(taskThenZoneChange(
|
||||
sessionData.unregisterDrivenVehicle(vehicle, player),
|
||||
unregisterDrivenVehicle(vehicle, player),
|
||||
ICS.FindZone(_.id == zoneId, context.self)
|
||||
))
|
||||
}
|
||||
|
|
@ -1480,9 +1485,9 @@ class ZoningOperations(
|
|||
// allow AMS, ANT and Router to remain deployed when owner leaves the zone
|
||||
vehicle.Definition match {
|
||||
case GlobalDefinitions.ams | GlobalDefinitions.ant | GlobalDefinitions.router
|
||||
=> sessionData.vehicles.ConditionalDriverVehicleControl(vehicle)
|
||||
=> sessionLogic.vehicles.ConditionalDriverVehicleControl(vehicle)
|
||||
|
||||
case _ => sessionData.vehicles.TotalDriverVehicleControl(vehicle)
|
||||
case _ => sessionLogic.vehicles.TotalDriverVehicleControl(vehicle)
|
||||
}
|
||||
|
||||
// remove owner
|
||||
|
|
@ -1492,12 +1497,12 @@ class ZoningOperations(
|
|||
}
|
||||
avatarActor ! AvatarActor.SetVehicle(None)
|
||||
}
|
||||
sessionData.removeBoomerTriggersFromInventory().foreach(obj => {
|
||||
spawn.removeBoomerTriggersFromInventory().foreach(obj => {
|
||||
TaskWorkflow.execute(GUIDTask.unregisterObject(continent.GUID, obj))
|
||||
})
|
||||
Deployables.Disown(continent, avatar, context.self)
|
||||
spawn.drawDeloyableIcon = spawn.RedrawDeployableIcons //important for when SetCurrentAvatar initializes the UI next zone
|
||||
sessionData.squad.squadSetup = sessionData.squad.ZoneChangeSquadSetup
|
||||
sessionLogic.squad.squadSetup = sessionLogic.squad.ZoneChangeSquadSetup
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1514,7 +1519,7 @@ class ZoningOperations(
|
|||
if (currentZone == Zones.sanctuaryZoneNumber(tplayer.Faction)) {
|
||||
log.error(s"RequestSanctuaryZoneSpawn: ${player.Name} is already in faction sanctuary zone.")
|
||||
sendResponse(DisconnectMessage("RequestSanctuaryZoneSpawn: player is already in sanctuary."))
|
||||
sessionData.immediateDisconnect()
|
||||
sessionLogic.immediateDisconnect()
|
||||
} else {
|
||||
continent.GUID(player.VehicleSeated) match {
|
||||
case Some(obj: Vehicle) if !obj.Destroyed =>
|
||||
|
|
@ -1547,8 +1552,8 @@ class ZoningOperations(
|
|||
def LoadZoneLaunchDroppod(zone: Zone, spawnPosition: Vector3): Unit = {
|
||||
log.info(s"${player.Name} is launching to ${zone.id} in ${player.Sex.possessive} droppod")
|
||||
CancelZoningProcess()
|
||||
sessionData.playerActionsToCancel()
|
||||
sessionData.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.actionsToCancel()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
//droppod action
|
||||
val droppod = Vehicle(GlobalDefinitions.droppod)
|
||||
droppod.GUID = PlanetSideGUID(0) //droppod is not registered, we must jury-rig this
|
||||
|
|
@ -1596,6 +1601,88 @@ class ZoningOperations(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct tasking that registers all aspects of a `Player` avatar
|
||||
* as if that player was already introduced and is just being renewed.
|
||||
* `Players` are complex objects that contain a variety of other register-able objects and each of these objects much be handled.
|
||||
* @param tplayer the avatar `Player`
|
||||
* @return a `TaskBundle` message
|
||||
*/
|
||||
private[session] def registerAvatar(tplayer: Player): TaskBundle = {
|
||||
TaskBundle(
|
||||
new StraightforwardTask() {
|
||||
private val localPlayer = tplayer
|
||||
private val localAnnounce = context.self
|
||||
|
||||
override def description(): String = s"register player avatar ${localPlayer.Name}"
|
||||
|
||||
def action(): Future[Any] = {
|
||||
localAnnounce ! SessionActor.PlayerLoaded(localPlayer)
|
||||
Future(true)
|
||||
}
|
||||
},
|
||||
List(GUIDTask.registerPlayer(continent.GUID, tplayer))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct tasking that registers all aspects of a `Player` avatar
|
||||
* as if that player is only just being introduced.
|
||||
* `Players` are complex objects that contain a variety of other register-able objects and each of these objects much be handled.
|
||||
* @param tplayer the avatar `Player`
|
||||
* @return a `TaskBundle` message
|
||||
*/
|
||||
private[session] def registerNewAvatar(tplayer: Player): TaskBundle = {
|
||||
TaskBundle(
|
||||
new StraightforwardTask() {
|
||||
private val localPlayer = tplayer
|
||||
private val localAnnounce = context.self
|
||||
|
||||
override def description(): String = s"register new player avatar ${localPlayer.Name}"
|
||||
|
||||
def action(): Future[Any] = {
|
||||
localAnnounce ! SessionActor.NewPlayerLoaded(localPlayer)
|
||||
Future(true)
|
||||
}
|
||||
},
|
||||
List(GUIDTask.registerAvatar(continent.GUID, tplayer))
|
||||
)
|
||||
}
|
||||
|
||||
private[session] def registerDrivenVehicle(vehicle: Vehicle, driver: Player): TaskBundle = {
|
||||
TaskBundle(
|
||||
new StraightforwardTask() {
|
||||
private val localVehicle = vehicle
|
||||
private val localDriver = driver
|
||||
private val localAnnounce = context.self
|
||||
|
||||
override def description(): String = s"register a ${localVehicle.Definition.Name} driven by ${localDriver.Name}"
|
||||
|
||||
def action(): Future[Any] = {
|
||||
localDriver.VehicleSeated = localVehicle.GUID
|
||||
Vehicles.Own(localVehicle, localDriver)
|
||||
localAnnounce ! SessionActor.NewPlayerLoaded(localDriver)
|
||||
Future(true)
|
||||
}
|
||||
},
|
||||
List(GUIDTask.registerAvatar(continent.GUID, driver), GUIDTask.registerVehicle(continent.GUID, vehicle))
|
||||
)
|
||||
}
|
||||
|
||||
private[session] def unregisterDrivenVehicle(vehicle: Vehicle, driver: Player): TaskBundle = {
|
||||
TaskBundle(
|
||||
new StraightforwardTask() {
|
||||
private val localVehicle = vehicle
|
||||
private val localDriver = driver
|
||||
|
||||
override def description(): String = s"unregister a ${localVehicle.Definition.Name} driven by ${localDriver.Name}"
|
||||
|
||||
def action(): Future[Any] = Future(true)
|
||||
},
|
||||
List(GUIDTask.unregisterAvatar(continent.GUID, driver), GUIDTask.unregisterVehicle(continent.GUID, vehicle))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this function to facilitate registering a droppod for a globally unique identifier
|
||||
* in the event that the user has instigated an instant action event to a destination within the current zone.<br>
|
||||
|
|
@ -1730,7 +1817,7 @@ class ZoningOperations(
|
|||
|
||||
def handleLoginInfoNowhere(name: String, from: ActorRef): Unit = {
|
||||
log.info(s"LoginInfo: player $name is considered a fresh character")
|
||||
sessionData.persistFunc = UpdatePersistence(from)
|
||||
sessionLogic.persistFunc = UpdatePersistence(from)
|
||||
deadState = DeadState.RespawnTime
|
||||
val tplayer = new Player(avatar)
|
||||
session = session.copy(player = tplayer)
|
||||
|
|
@ -1742,7 +1829,7 @@ class ZoningOperations(
|
|||
|
||||
def handleLoginInfoSomewhere(name: String, inZone: Zone, optionalSavedData: Option[Savedplayer], from: ActorRef): Unit = {
|
||||
log.info(s"LoginInfo: player $name is considered a fresh character")
|
||||
sessionData.persistFunc = UpdatePersistence(from)
|
||||
sessionLogic.persistFunc = UpdatePersistence(from)
|
||||
deadState = DeadState.RespawnTime
|
||||
session = session.copy(player = new Player(avatar))
|
||||
player.Zone = inZone
|
||||
|
|
@ -1836,7 +1923,7 @@ class ZoningOperations(
|
|||
|
||||
def handleLoginInfoRestore(name: String, inZone: Zone, pos: Vector3, from: ActorRef): Unit = {
|
||||
log.info(s"RestoreInfo: player $name is already logged in zone ${inZone.id}; rejoining that character")
|
||||
sessionData.persistFunc = UpdatePersistence(from)
|
||||
sessionLogic.persistFunc = UpdatePersistence(from)
|
||||
//tell the old WorldSessionActor to kill itself by using its own subscriptions against itself
|
||||
inZone.AvatarEvents ! AvatarServiceMessage(name, AvatarAction.TeardownConnection())
|
||||
spawn.switchAvatarStatisticsFieldToRefreshAfterRespawn()
|
||||
|
|
@ -1847,14 +1934,14 @@ class ZoningOperations(
|
|||
) match {
|
||||
case (_, Some(p)) if p.death_by == -1 =>
|
||||
//player is not allowed
|
||||
sessionData.kickedByAdministration()
|
||||
sessionLogic.kickedByAdministration()
|
||||
|
||||
case (Some(a), Some(p)) if p.isAlive =>
|
||||
//rejoin current avatar/player
|
||||
log.info(s"RestoreInfo: player $name is alive")
|
||||
deadState = DeadState.Alive
|
||||
session = session.copy(player = p, avatar = a)
|
||||
sessionData.persist()
|
||||
sessionLogic.persist()
|
||||
setupAvatarFunc = AvatarRejoin
|
||||
dropMedicalApplicators(p)
|
||||
avatarActor ! AvatarActor.ReplaceAvatar(a)
|
||||
|
|
@ -1865,7 +1952,7 @@ class ZoningOperations(
|
|||
log.info(s"RestoreInfo: player $name is dead")
|
||||
deadState = DeadState.Dead
|
||||
session = session.copy(player = p, avatar = a)
|
||||
sessionData.persist()
|
||||
sessionLogic.persist()
|
||||
dropMedicalApplicators(p)
|
||||
HandleReleaseAvatar(p, inZone)
|
||||
avatarActor ! AvatarActor.ReplaceAvatar(a)
|
||||
|
|
@ -1900,7 +1987,7 @@ class ZoningOperations(
|
|||
def handleLoginCanNot(name: String, reason: PlayerToken.DeniedLoginReason.Value): Unit = {
|
||||
log.warn(s"LoginInfo: $name is denied login for reason - $reason")
|
||||
reason match {
|
||||
case PlayerToken.DeniedLoginReason.Kicked => sessionData.kickedByAdministration()
|
||||
case PlayerToken.DeniedLoginReason.Kicked => sessionLogic.kickedByAdministration()
|
||||
case _ => sendResponse(DisconnectMessage("You will be logged out."))
|
||||
}
|
||||
}
|
||||
|
|
@ -1939,9 +2026,9 @@ class ZoningOperations(
|
|||
}
|
||||
val previousZoningType = ztype
|
||||
CancelZoningProcess()
|
||||
sessionData.playerActionsToCancel()
|
||||
sessionData.terminals.CancelAllProximityUnits()
|
||||
sessionData.dropSpecialSlotItem()
|
||||
sessionLogic.actionsToCancel()
|
||||
sessionLogic.terminals.CancelAllProximityUnits()
|
||||
sessionLogic.general.dropSpecialSlotItem()
|
||||
continent.Population ! Zone.Population.Release(avatar)
|
||||
resolveZoningSpawnPointLoad(response, previousZoningType)
|
||||
}
|
||||
|
|
@ -1954,8 +2041,8 @@ class ZoningOperations(
|
|||
val map = zone.map
|
||||
val mapName = map.name
|
||||
log.info(s"${tplayer.Name} has spawned into $id")
|
||||
sessionData.oldRefsMap.clear()
|
||||
sessionData.persist = UpdatePersistenceAndRefs
|
||||
sessionLogic.oldRefsMap.clear()
|
||||
sessionLogic.persist = UpdatePersistenceAndRefs
|
||||
tplayer.avatar = avatar
|
||||
session = session.copy(player = tplayer)
|
||||
avatarActor ! AvatarActor.CreateImplants()
|
||||
|
|
@ -1967,7 +2054,7 @@ class ZoningOperations(
|
|||
//important! the LoadMapMessage must be processed by the client before the avatar is created
|
||||
setupAvatarFunc()
|
||||
//interimUngunnedVehicle should have been setup by setupAvatarFunc, if it is applicable
|
||||
sessionData.turnCounterFunc = interimUngunnedVehicle match {
|
||||
sessionLogic.turnCounterFunc = interimUngunnedVehicle match {
|
||||
case Some(_) =>
|
||||
TurnCounterDuringInterimWhileInPassengerSeat
|
||||
case None if zoningType == Zoning.Method.Login || zoningType == Zoning.Method.Reset =>
|
||||
|
|
@ -1975,14 +2062,14 @@ class ZoningOperations(
|
|||
case None =>
|
||||
TurnCounterDuringInterim
|
||||
}
|
||||
sessionData.keepAliveFunc = NormalKeepAlive
|
||||
sessionLogic.keepAliveFunc = NormalKeepAlive
|
||||
if (zoningStatus == Zoning.Status.Deconstructing) {
|
||||
sessionData.stopDeconstructing()
|
||||
stopDeconstructing()
|
||||
}
|
||||
sessionData.avatarResponse.lastSeenStreamMessage.clear()
|
||||
sessionLogic.avatarResponse.lastSeenStreamMessage.clear()
|
||||
upstreamMessageCount = 0
|
||||
setAvatar = false
|
||||
sessionData.persist()
|
||||
sessionLogic.persist()
|
||||
} else {
|
||||
//look for different spawn point in same zone
|
||||
cluster ! ICS.GetNearbySpawnPoint(
|
||||
|
|
@ -2004,20 +2091,20 @@ class ZoningOperations(
|
|||
//try this spawn point
|
||||
setupAvatarFunc()
|
||||
//interimUngunnedVehicle should have been setup by setupAvatarFunc, if it is applicable
|
||||
sessionData.turnCounterFunc = interimUngunnedVehicle match {
|
||||
sessionLogic.turnCounterFunc = interimUngunnedVehicle match {
|
||||
case Some(_) =>
|
||||
TurnCounterDuringInterimWhileInPassengerSeat
|
||||
case None =>
|
||||
TurnCounterDuringInterim
|
||||
}
|
||||
sessionData.keepAliveFunc = NormalKeepAlive
|
||||
sessionLogic.keepAliveFunc = NormalKeepAlive
|
||||
if (zoningStatus == Zoning.Status.Deconstructing) {
|
||||
sessionData.stopDeconstructing()
|
||||
stopDeconstructing()
|
||||
}
|
||||
sessionData.avatarResponse.lastSeenStreamMessage.clear()
|
||||
sessionLogic.avatarResponse.lastSeenStreamMessage.clear()
|
||||
upstreamMessageCount = 0
|
||||
setAvatar = false
|
||||
sessionData.persist()
|
||||
sessionLogic.persist()
|
||||
} else {
|
||||
//look for different spawn point in same zone
|
||||
cluster ! ICS.GetNearbySpawnPoint(
|
||||
|
|
@ -2087,7 +2174,7 @@ class ZoningOperations(
|
|||
* @param zone na
|
||||
*/
|
||||
def HandleReleaseAvatar(tplayer: Player, zone: Zone): Unit = {
|
||||
sessionData.keepAliveFunc = sessionData.keepAlivePersistence
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
|
||||
tplayer.Release
|
||||
tplayer.VehicleSeated match {
|
||||
case None =>
|
||||
|
|
@ -2101,8 +2188,8 @@ class ZoningOperations(
|
|||
}
|
||||
|
||||
def handleSetPosition(position: Vector3): Unit = {
|
||||
if (sessionData.vehicles.serverVehicleControlVelocity.isEmpty) {
|
||||
sessionData.playerActionsToCancel()
|
||||
if (sessionLogic.vehicles.serverVehicleControlVelocity.isEmpty) {
|
||||
sessionLogic.actionsToCancel()
|
||||
continent.GUID(player.VehicleSeated) match {
|
||||
case Some(vehicle: Vehicle) if vehicle.MountedIn.isEmpty =>
|
||||
vehicle.PassengerInSeat(player) match {
|
||||
|
|
@ -2147,7 +2234,7 @@ class ZoningOperations(
|
|||
player.Armor = armor
|
||||
}
|
||||
player.death_by = math.min(player.death_by, 0)
|
||||
sessionData.vehicles.GetKnownVehicleAndSeat() match {
|
||||
sessionLogic.vehicles.GetKnownVehicleAndSeat() match {
|
||||
case (Some(vehicle: Vehicle), Some(seat: Int)) =>
|
||||
//if the vehicle is the cargo of another vehicle in this zone
|
||||
val carrierInfo = continent.GUID(vehicle.MountedIn) match {
|
||||
|
|
@ -2254,7 +2341,7 @@ class ZoningOperations(
|
|||
* Neither the player avatar nor the vehicle should be reconstructed before the next zone load operation
|
||||
* to avoid damaging the critical setup of this function.
|
||||
* @see `AccessContainer`
|
||||
* @see `UpdateWeaponAtSeatPosition`
|
||||
* @see `SessionMountHandlers.updateWeaponAtSeatPosition`
|
||||
* @param tplayer the player avatar seated in the vehicle's mount
|
||||
* @param vehicle the vehicle the player is riding
|
||||
* @param seat the mount index
|
||||
|
|
@ -2271,8 +2358,8 @@ class ZoningOperations(
|
|||
sendResponse(ObjectCreateDetailedMessage(pdef.ObjectId, pguid, pdata))
|
||||
if (seat == 0 || vehicle.WeaponControlledFromSeat(seat).nonEmpty) {
|
||||
sendResponse(ObjectAttachMessage(vguid, pguid, seat))
|
||||
sessionData.accessContainer(vehicle)
|
||||
sessionData.updateWeaponAtSeatPosition(vehicle, seat)
|
||||
sessionLogic.general.accessContainer(vehicle)
|
||||
sessionLogic.mountResponse.updateWeaponAtSeatPosition(vehicle, seat)
|
||||
} else {
|
||||
interimUngunnedVehicle = Some(vguid)
|
||||
interimUngunnedVehicleSeat = Some(seat)
|
||||
|
|
@ -2317,7 +2404,7 @@ class ZoningOperations(
|
|||
* to avoid damaging the critical setup of this function.
|
||||
*/
|
||||
def AvatarRejoin(): Unit = {
|
||||
sessionData.vehicles.GetKnownVehicleAndSeat() match {
|
||||
sessionLogic.vehicles.GetKnownVehicleAndSeat() match {
|
||||
case (Some(vehicle: Vehicle), Some(seat: Int)) =>
|
||||
//vehicle and driver/passenger
|
||||
val vdef = vehicle.Definition
|
||||
|
|
@ -2336,8 +2423,8 @@ class ZoningOperations(
|
|||
log.debug(s"AvatarRejoin: ${player.Name} - $pguid -> $pdata")
|
||||
if (seat == 0 || vehicle.WeaponControlledFromSeat(seat).nonEmpty) {
|
||||
sendResponse(ObjectAttachMessage(vguid, pguid, seat))
|
||||
sessionData.accessContainer(vehicle)
|
||||
sessionData.updateWeaponAtSeatPosition(vehicle, seat)
|
||||
sessionLogic.general.accessContainer(vehicle)
|
||||
sessionLogic.mountResponse.updateWeaponAtSeatPosition(vehicle, seat)
|
||||
} else {
|
||||
interimUngunnedVehicle = Some(vguid)
|
||||
interimUngunnedVehicleSeat = Some(seat)
|
||||
|
|
@ -2406,10 +2493,37 @@ class ZoningOperations(
|
|||
case Some(_) | None => ;
|
||||
}
|
||||
})
|
||||
sessionData.removeBoomerTriggersFromInventory().foreach(trigger => { sessionData.normalItemDrop(obj, continent)(trigger) })
|
||||
removeBoomerTriggersFromInventory().foreach(trigger => { sessionLogic.general.normalItemDrop(obj, continent)(trigger) })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search through the player's holsters and their inventory space
|
||||
* and remove all `BoomerTrigger` objects, both functionally and visually.
|
||||
* @return all discovered `BoomTrigger` objects
|
||||
*/
|
||||
def removeBoomerTriggersFromInventory(): List[BoomerTrigger] = {
|
||||
val events = continent.AvatarEvents
|
||||
val zoneId = continent.id
|
||||
(player.Inventory.Items ++ player.HolsterItems())
|
||||
.collect { case InventoryItem(obj: BoomerTrigger, index) =>
|
||||
player.Slot(index).Equipment = None
|
||||
continent.GUID(obj.Companion) match {
|
||||
case Some(mine: BoomerDeployable) => mine.Actor ! Deployable.Ownership(None)
|
||||
case _ => ()
|
||||
}
|
||||
if (player.VisibleSlots.contains(index)) {
|
||||
events ! AvatarServiceMessage(
|
||||
zoneId,
|
||||
AvatarAction.ObjectDelete(Service.defaultPlayerGUID, obj.GUID)
|
||||
)
|
||||
} else {
|
||||
sendResponse(ObjectDeleteMessage(obj.GUID, 0))
|
||||
}
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a player that has the characteristics of a corpse
|
||||
* so long as the player has items in their knapsack or their holsters.
|
||||
|
|
@ -2762,10 +2876,10 @@ class ZoningOperations(
|
|||
SessionActor.SetCurrentAvatar(player, max_attempts, attempt + max_attempts / 3)
|
||||
)
|
||||
} else {
|
||||
sessionData.keepAliveFunc = sessionData.vehicles.GetMountableAndSeat(None, player, continent) match {
|
||||
sessionLogic.keepAliveFunc = sessionLogic.vehicles.GetMountableAndSeat(None, player, continent) match {
|
||||
case (Some(v: Vehicle), Some(seatNumber))
|
||||
if seatNumber > 0 && v.WeaponControlledFromSeat(seatNumber).isEmpty =>
|
||||
sessionData.keepAlivePersistence
|
||||
sessionLogic.keepAlivePersistence
|
||||
case _ =>
|
||||
NormalKeepAlive
|
||||
}
|
||||
|
|
@ -2792,7 +2906,7 @@ class ZoningOperations(
|
|||
log.trace(s"HandleSetCurrentAvatar - ${tplayer.Name}")
|
||||
session = session.copy(player = tplayer)
|
||||
val guid = tplayer.GUID
|
||||
sessionData.updateDeployableUIElements(Deployables.InitializeDeployableUIElements(avatar))
|
||||
sessionLogic.general.updateDeployableUIElements(Deployables.InitializeDeployableUIElements(avatar))
|
||||
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 75, 0))
|
||||
sendResponse(SetCurrentAvatarMessage(guid, 0, 0))
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_EXPANSIONS, wideContents=true, "", "1 on", None)) //CC on //TODO once per respawn?
|
||||
|
|
@ -2851,7 +2965,7 @@ class ZoningOperations(
|
|||
|
||||
sendResponse(PlanetsideStringAttributeMessage(guid, 0, "Outfit Name"))
|
||||
//squad stuff (loadouts, assignment)
|
||||
sessionData.squad.squadSetup()
|
||||
sessionLogic.squad.squadSetup()
|
||||
//MapObjectStateBlockMessage and ObjectCreateMessage?
|
||||
//TacticsMessage?
|
||||
//change the owner on our deployables (re-draw the icons for our deployables too)
|
||||
|
|
@ -2875,7 +2989,7 @@ class ZoningOperations(
|
|||
case _ =>
|
||||
avatarActor ! AvatarActor.SetVehicle(None)
|
||||
}
|
||||
sessionData.vehicles.GetVehicleAndSeat() match {
|
||||
sessionLogic.vehicles.GetVehicleAndSeat() match {
|
||||
case (Some(vehicle), _) if vehicle.Definition == GlobalDefinitions.droppod =>
|
||||
//we're falling
|
||||
sendResponse(
|
||||
|
|
@ -2923,7 +3037,7 @@ class ZoningOperations(
|
|||
case _ => ;
|
||||
}
|
||||
interstellarFerryTopLevelGUID = None
|
||||
if (loadConfZone && sessionData.connectionState == 100) {
|
||||
if (loadConfZone && sessionLogic.connectionState == 100) {
|
||||
configZone(continent)
|
||||
loadConfZone = false
|
||||
}
|
||||
|
|
@ -2934,7 +3048,7 @@ class ZoningOperations(
|
|||
player.Actor ! Player.Die()
|
||||
} else {
|
||||
AvatarActor.savePlayerData(player)
|
||||
sessionData.displayCharSavedMsgThenRenewTimer(
|
||||
sessionLogic.general.displayCharSavedMsgThenRenewTimer(
|
||||
Config.app.game.savedMsg.short.fixed,
|
||||
Config.app.game.savedMsg.short.variable
|
||||
)
|
||||
|
|
@ -3167,7 +3281,7 @@ class ZoningOperations(
|
|||
def TurnCounterDuringInterim(guid: PlanetSideGUID): Unit = {
|
||||
upstreamMessageCount = 0
|
||||
if (player != null && player.HasGUID && player.GUID == guid && player.Zone == continent) {
|
||||
sessionData.turnCounterFunc = NormalTurnCounter
|
||||
sessionLogic.turnCounterFunc = NormalTurnCounter
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3201,15 +3315,15 @@ class ZoningOperations(
|
|||
case (Some(vehicle: Vehicle), Some(vguid), Some(seat)) =>
|
||||
//sit down
|
||||
sendResponse(ObjectAttachMessage(vguid, pguid, seat))
|
||||
sessionData.accessContainer(vehicle)
|
||||
sessionData.keepAliveFunc = sessionData.keepAlivePersistence
|
||||
sessionLogic.general.accessContainer(vehicle)
|
||||
sessionLogic.keepAliveFunc = sessionLogic.keepAlivePersistence
|
||||
case _ => ;
|
||||
//we can't find a vehicle? and we're still here? that's bad
|
||||
player.VehicleSeated = None
|
||||
}
|
||||
interimUngunnedVehicle = None
|
||||
interimUngunnedVehicleSeat = None
|
||||
sessionData.turnCounterFunc = NormalTurnCounter
|
||||
sessionLogic.turnCounterFunc = NormalTurnCounter
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
|
@ -3226,7 +3340,7 @@ class ZoningOperations(
|
|||
loginChatMessage.foreach { msg => sendResponse(ChatMsg(zoningChatMessageType, wideContents=false, "", msg, None)) }
|
||||
loginChatMessage.clear()
|
||||
CancelZoningProcess()
|
||||
sessionData.turnCounterFunc = NormalTurnCounter
|
||||
sessionLogic.turnCounterFunc = NormalTurnCounter
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -3403,7 +3517,7 @@ class ZoningOperations(
|
|||
context.system.scheduler.scheduleOnce(
|
||||
delay.milliseconds,
|
||||
context.self,
|
||||
SessionActor.AvatarAwardMessageBundle(xs, delay)
|
||||
ZoningOperations.AvatarAwardMessageBundle(xs, delay)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -3413,8 +3527,8 @@ class ZoningOperations(
|
|||
* Set to `persist` when (new) player is loaded.
|
||||
*/
|
||||
def UpdatePersistenceAndRefs(): Unit = {
|
||||
sessionData.persistFunc()
|
||||
sessionData.updateOldRefsMap()
|
||||
sessionLogic.persistFunc()
|
||||
sessionLogic.updateOldRefsMap()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -3425,6 +3539,34 @@ class ZoningOperations(
|
|||
def UpdatePersistence(persistRef: ActorRef)(): Unit = {
|
||||
persistRef ! AccountPersistenceService.Update(player.Name, continent, player.Position)
|
||||
}
|
||||
|
||||
def startDeconstructing(obj: SpawnTube): Unit = {
|
||||
log.info(s"${player.Name} is deconstructing at the ${obj.Owner.Definition.Name}'s spawns")
|
||||
avatar.implants.collect {
|
||||
case Some(implant) if implant.active && !implant.definition.Passive =>
|
||||
avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType)
|
||||
}
|
||||
if (player.ExoSuit != ExoSuitType.MAX) {
|
||||
player.Actor ! PlayerControl.ObjectHeld(Player.HandsDownSlot, updateMyHolsterArm = true)
|
||||
}
|
||||
nextSpawnPoint = Some(obj) //set fallback
|
||||
zoningStatus = Zoning.Status.Deconstructing
|
||||
player.allowInteraction = false
|
||||
if (player.death_by == 0) {
|
||||
player.death_by = 1
|
||||
}
|
||||
GoToDeploymentMap()
|
||||
}
|
||||
|
||||
def stopDeconstructing(): Unit = {
|
||||
zoningStatus = Zoning.Status.None
|
||||
player.death_by = math.min(player.death_by, 0)
|
||||
player.allowInteraction = true
|
||||
nextSpawnPoint.foreach { tube =>
|
||||
sendResponse(PlayerStateShiftMessage(ShiftState(0, tube.Position, tube.Orientation.z)))
|
||||
nextSpawnPoint = None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override protected[session] def stop(): Unit = {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,23 @@ object CommonMessages {
|
|||
final case class Hack(player: Player, obj: PlanetSideServerObject with Hackable, data: Option[Any] = None)
|
||||
final case class ClearHack()
|
||||
|
||||
/**
|
||||
* The message that progresses some form of user-driven activity with a certain eventual outcome
|
||||
* and potential feedback per cycle.
|
||||
* @param delta how much the progress value changes each tick, which will be treated as a percentage;
|
||||
* must be a positive value
|
||||
* @param completionAction a finalizing action performed once the progress reaches 100(%)
|
||||
* @param tickAction an action that is performed for each increase of progress
|
||||
* @param tickTime how long between each `tickAction` (ms);
|
||||
* defaults to 250 milliseconds
|
||||
*/
|
||||
final case class ProgressEvent(
|
||||
delta: Float,
|
||||
completionAction: () => Unit,
|
||||
tickAction: Float => Boolean,
|
||||
tickTime: Long = 250L
|
||||
)
|
||||
|
||||
/**
|
||||
* The message that initializes a process -
|
||||
* some form of user-driven activity with a certain eventual outcome and potential feedback per cycle.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue