extract common messages into a common handler endpoint; created an interface for handlers thus that all handler endpoints can be polled if a specific handler endpoint does not support a message

This commit is contained in:
Fate-JH 2026-03-24 16:37:58 -04:00
parent 7e1e0e3016
commit b39f95354a
21 changed files with 2391 additions and 2480 deletions

View file

@ -3,7 +3,7 @@ package net.psforever.actors.session
import akka.actor.{Actor, ActorRef, Cancellable, MDCContextAware, typed}
import net.psforever.actors.session.normal.NormalMode
import net.psforever.actors.session.support.ZoningOperations
import net.psforever.actors.session.support.{CommonHandlerFunctions, CommonHandlerLogic, HandlerFilter, ZoningOperations}
import net.psforever.objects.TurretDeployable
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.containable.Containable
@ -108,6 +108,21 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
private[this] var mode: PlayerMode = NormalMode
private[this] var logic: ModeLogic = _
private val commonHandlerLogic: CommonHandlerLogic = new CommonHandlerLogic(data, context)
private def listOfHandlers: Seq[CommonHandlerFunctions] = {
if (logic == null) {
List.empty
} else {
List(
logic.avatarResponse,
logic.local,
logic.vehicleResponse,
logic.galaxy,
commonHandlerLogic
)
}
}
override def postStop(): Unit = {
clientKeepAlive.cancel()
data.stop()
@ -175,21 +190,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
logic.squad.handle(reply, excluded)
case envelope: GenericResponseEnvelope =>
val GenericResponseEnvelope(toChannel, guid, reply) = envelope
envelope.stamp match {
case AvatarStamp =>
logic.avatarResponse.handle(toChannel, guid, reply)
case LocalStamp =>
logic.local.handle(toChannel, guid, reply)
case VehicleStamp =>
logic.vehicleResponse.handle(toChannel, guid, reply)
case GalaxyStamp =>
logic.galaxy.handle(reply)
case Undelivered =>
log.error(s"received a message's response that was not delivered by an event system - $reply")
case unknownStamp =>
log.error(s"received a message's response from an unknown event system - stamp: $unknownStamp")
}
handleGenericResponseEnvelope(envelope)
case Mountable.MountMessages(tplayer, reply) =>
logic.mountResponse.handle(tplayer, reply)
@ -375,6 +376,43 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
logic.general.handleReceiveDefaultMessage(default, sender)
}
private def handleGenericResponseEnvelope(envelope: GenericResponseEnvelope): Unit = {
val GenericResponseEnvelope(_, guid, reply) = envelope
//try use the stamp to match the specific handler
val primaryHandler = envelope.stamp match {
case Undelivered =>
log.error(s"received a message's response that was not processed by an event system - $reply")
CommonHandlerFunctions.HandleAnything
case AvatarStamp =>
logic.avatarResponse
case LocalStamp =>
logic.local
case VehicleStamp =>
logic.vehicleResponse
case GalaxyStamp =>
logic.galaxy
case unknownStamp =>
log.error(s"received a message's response from an unknown event system - stamp: $unknownStamp")
CommonHandlerFunctions.HandleNothing
}
//try the handler on the input message
lazy val alwaysAllowFilter = HandlerFilter.Allow(guid)
if (primaryHandler.handleWith(guid).isDefinedAt(reply)) {
primaryHandler.receive.apply(reply)
} else if (!primaryHandler.handleWith(alwaysAllowFilter).isDefinedAt(reply)) {
//check a list of all handlers for any potentially valid case
val potentiallyValidHandlers = listOfHandlers.filter(_.handleWith(alwaysAllowFilter).isDefinedAt(reply))
if (potentiallyValidHandlers.nonEmpty) {
potentiallyValidHandlers
.find(_.handleWith(guid).isDefinedAt(reply))
.foreach(_.receive.apply(reply))
//arrive here without processing input, the guard for a handler blocked a case; not gonna fault
} else {
log.error(s"received completely unhandled response message - $envelope")
}
}
}
private def handleGamePkt: PlanetSideGamePacket => Unit = {
case packet: ConnectToWorldRequestMessage =>
logic.general.handleConnectToWorldRequest(packet)

View file

@ -1,14 +1,15 @@
// Copyright (c) 2024 PSForever
package net.psforever.actors.session.normal
import akka.actor.Actor.Receive
import akka.actor.{ActorContext, ActorRef, typed}
import net.psforever.actors.session.AvatarActor
import net.psforever.actors.session.support.{GalaxyHandlerFunctions, SessionData, SessionGalaxyHandlers}
import net.psforever.packet.game.{BroadcastWarpgateUpdateMessage, FriendsResponse, HotSpotUpdateMessage, ZoneInfoMessage, ZonePopulationUpdateMessage, HotSpotInfo => PacketHotSpotInfo}
import net.psforever.services.base.envelope.MessageEnvelope
import net.psforever.services.base.message.{EventResponse, SendResponse}
import net.psforever.services.base.message.EventResponse
import net.psforever.services.galaxy.GalaxyAction
import net.psforever.types.{MemberAction, PlanetSideEmpire}
import net.psforever.types.{MemberAction, PlanetSideEmpire, PlanetSideGUID}
object GalaxyHandlerLogic {
def apply(ops: SessionGalaxyHandlers): GalaxyHandlerLogic = {
@ -34,62 +35,59 @@ class GalaxyHandlerLogic(val ops: SessionGalaxyHandlers, implicit val context: A
/* response handlers */
def handle(reply: EventResponse): Unit = {
reply match {
case GalaxyAction.HotSpotUpdate(zone_index, priority, hot_spot_info) =>
sendResponse(
HotSpotUpdateMessage(
zone_index,
priority,
hot_spot_info.map { spot => PacketHotSpotInfo(spot.DisplayLocation.x, spot.DisplayLocation.y, 40) }
)
override def handle(toChannel: String, guid: PlanetSideGUID, reply: EventResponse): Unit = {
receive.apply(reply)
}
def receive: Receive = {
case GalaxyAction.HotSpotUpdate(zone_index, priority, hot_spot_info) =>
sendResponse(
HotSpotUpdateMessage(
zone_index,
priority,
hot_spot_info.map { spot => PacketHotSpotInfo(spot.DisplayLocation.x, spot.DisplayLocation.y, 40) }
)
)
case GalaxyAction.MapUpdate(msg) =>
sendResponse(msg)
import net.psforever.actors.zone.ZoneActor
import net.psforever.zones.Zones
Zones.zones.find(_.Number == msg.continent_id) match {
case Some(zone) =>
zone.actor ! ZoneActor.BuildingInfoState(msg)
case None =>
}
case GalaxyAction.MapUpdate(msg) =>
sendResponse(msg)
import net.psforever.actors.zone.ZoneActor
import net.psforever.zones.Zones
Zones.zones.find(_.Number == msg.continent_id) match {
case Some(zone) =>
zone.actor ! ZoneActor.BuildingInfoState(msg)
case None =>
}
case GalaxyAction.UpdateBroadcastPrivileges(zoneId, gateMapId, fromFactions, toFactions) =>
val faction = player.Faction
val from = fromFactions.contains(faction)
val to = toFactions.contains(faction)
if (from && !to) {
sendResponse(BroadcastWarpgateUpdateMessage(zoneId, gateMapId, PlanetSideEmpire.NEUTRAL))
} else if (!from && to) {
sendResponse(BroadcastWarpgateUpdateMessage(zoneId, gateMapId, faction))
}
case GalaxyAction.UpdateBroadcastPrivileges(zoneId, gateMapId, fromFactions, toFactions) =>
val faction = player.Faction
val from = fromFactions.contains(faction)
val to = toFactions.contains(faction)
if (from && !to) {
sendResponse(BroadcastWarpgateUpdateMessage(zoneId, gateMapId, PlanetSideEmpire.NEUTRAL))
} else if (!from && to) {
sendResponse(BroadcastWarpgateUpdateMessage(zoneId, gateMapId, faction))
}
case GalaxyAction.FlagMapUpdate(msg) =>
sendResponse(msg)
case GalaxyAction.FlagMapUpdate(msg) =>
sendResponse(msg)
case GalaxyAction.TransferPassenger(_, temp_channel, vehicle, _, manifest) =>
sessionLogic.zoning.handleTransferPassenger(temp_channel, vehicle, manifest)
case GalaxyAction.TransferPassenger(_, temp_channel, vehicle, _, manifest) =>
sessionLogic.zoning.handleTransferPassenger(temp_channel, vehicle, manifest)
case GalaxyAction.LockedZoneUpdate(zone, time) =>
sendResponse(ZoneInfoMessage(zone.Number, empire_status=false, lock_time=time))
case GalaxyAction.LockedZoneUpdate(zone, time) =>
sendResponse(ZoneInfoMessage(zone.Number, empire_status=false, lock_time=time))
case GalaxyAction.UnlockedZoneUpdate(zone) =>
sendResponse(ZoneInfoMessage(zone.Number, empire_status=true, lock_time=0L))
val popBO = 0
val pop = zone.LivePlayers.distinctBy(_.CharId)
val popTR = pop.count(_.Faction == PlanetSideEmpire.TR)
val popNC = pop.count(_.Faction == PlanetSideEmpire.NC)
val popVS = pop.count(_.Faction == PlanetSideEmpire.VS)
sendResponse(ZonePopulationUpdateMessage(zone.Number, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO))
case GalaxyAction.UnlockedZoneUpdate(zone) =>
sendResponse(ZoneInfoMessage(zone.Number, empire_status=true, lock_time=0L))
val popBO = 0
val pop = zone.LivePlayers.distinctBy(_.CharId)
val popTR = pop.count(_.Faction == PlanetSideEmpire.TR)
val popNC = pop.count(_.Faction == PlanetSideEmpire.NC)
val popVS = pop.count(_.Faction == PlanetSideEmpire.VS)
sendResponse(ZonePopulationUpdateMessage(zone.Number, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO))
case GalaxyAction.LogStatusChange(name) if avatar.people.friend.exists(_.name.equals(name)) =>
avatarActor ! AvatarActor.MemberListRequest(MemberAction.UpdateFriend, name)
case SendResponse(msgs) =>
msgs.foreach(sendResponse)
case _ => ()
}
case GalaxyAction.LogStatusChange(name) if avatar.people.friend.exists(_.name.equals(name)) =>
avatarActor ! AvatarActor.MemberListRequest(MemberAction.UpdateFriend, name)
}
}

View file

@ -1,6 +1,7 @@
// Copyright (c) 2024 PSForever
package net.psforever.actors.session.normal
import akka.actor.Actor.Receive
import akka.actor.ActorContext
import net.psforever.actors.session.support.SpawnOperations.ActivityQueuedTask
import net.psforever.actors.session.support.{LocalHandlerFunctions, SessionData, SessionLocalHandlers, SpawnOperations}
@ -8,11 +9,11 @@ import net.psforever.objects.ce.Deployable
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.vehicles.MountableWeapons
import net.psforever.objects.{BoomerDeployable, ExplosiveDeployable, TelepadDeployable, Tool, TurretDeployable}
import net.psforever.packet.game.{ChatMsg, DeployableObjectsInfoMessage, GenericActionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HackMessage, HackState, HackState1, InventoryStateMessage, ObjectAttachMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, OrbitalShuttleTimeMsg, PadAndShuttlePair, PlanetsideAttributeMessage, ProximityTerminalUseMessage, SetEmpireMessage, TriggerEffectMessage, TriggerSoundMessage, TriggeredSound, VehicleStateMessage}
import net.psforever.services.base.message.{EventResponse, GenericObjectAction, ObjectDelete, PlanetsideAttribute, SendResponse, SetEmpire}
import net.psforever.packet.game.{ChatMsg, DeployableObjectsInfoMessage, GenericActionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HackMessage, HackState, HackState1, InventoryStateMessage, ObjectAttachMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, OrbitalShuttleTimeMsg, PadAndShuttlePair, PlanetsideAttributeMessage, ProximityTerminalUseMessage, TriggerEffectMessage, TriggerSoundMessage, TriggeredSound, VehicleStateMessage}
import net.psforever.services.base.message.{GenericObjectAction, PlanetsideAttribute, SendResponse}
import net.psforever.services.{InterstellarClusterService, Service}
import net.psforever.services.local.LocalAction
import net.psforever.types.{ChatMessageType, PlanetSideGUID, SpawnGroup}
import net.psforever.types.{ChatMessageType, SpawnGroup}
object LocalHandlerLogic {
def apply(ops: SessionLocalHandlers): LocalHandlerLogic = {
@ -35,238 +36,213 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act
/* response handlers */
/**
* na
* @param toChannel na
* @param guid na
* @param reply na
*/
def handle(toChannel: String, guid: PlanetSideGUID, reply: EventResponse): Unit = {
val resolvedPlayerGuid = if (player.HasGUID) {
player.GUID
} else {
Service.defaultPlayerGUID
}
val isNotSameTarget = resolvedPlayerGuid != guid
reply match {
case LocalAction.DeployableMapIcon(behavior, deployInfo) if isNotSameTarget =>
sendResponse(DeployableObjectsInfoMessage(behavior, deployInfo))
case LocalAction.DeployableUIFor(item) =>
sessionLogic.general.updateDeployableUIElements(avatar.deployables.UpdateUIElement(item))
case LocalAction.Detonate(dguid, _: BoomerDeployable) =>
sendResponse(TriggerEffectMessage(dguid, "detonate_boomer"))
sendResponse(PlanetsideAttributeMessage(dguid, attribute_type=29, attribute_value=1))
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalAction.Detonate(dguid, _: ExplosiveDeployable) =>
sendResponse(GenericObjectActionMessage(dguid, code=19))
sendResponse(PlanetsideAttributeMessage(dguid, attribute_type=29, attribute_value=1))
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalAction.Detonate(_, obj) =>
log.warn(s"LocalAction.Detonate: ${obj.Definition.Name} not configured to explode correctly")
case LocalAction.DoorOpens(_, door) if isNotSameTarget =>
val doorGuid = door.GUID
val pos = player.Position.xy
val range = ops.doorLoadRange()
val foundDoor = continent
.blockMap
.sector(pos, range)
.amenityList
.collect { case door: Door => door }
.find(_.GUID == doorGuid)
val doorExistsInRange: Boolean = foundDoor.nonEmpty
if (doorExistsInRange) {
sendResponse(GenericObjectStateMsg(doorGuid, state=16))
}
case LocalAction.DoorCloses(doorGuid) => //door closes for everyone
sendResponse(GenericObjectStateMsg(doorGuid, state=17))
case LocalAction.EliminateDeployable(obj: TurretDeployable, dguid, _, _) if obj.Destroyed =>
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalAction.EliminateDeployable(obj: TurretDeployable, dguid, pos, _) =>
obj.Destroyed = true
ops.DeconstructDeployable(
obj,
dguid,
pos,
obj.Orientation,
deletionType= if (obj.MountPoints.isEmpty) { 2 } else { 1 }
)
case LocalAction.EliminateDeployable(obj: ExplosiveDeployable, dguid, _, _)
if obj.Destroyed || obj.Jammed || obj.Health == 0 =>
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalAction.EliminateDeployable(obj: ExplosiveDeployable, dguid, pos, effect) =>
obj.Destroyed = true
ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
case LocalAction.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Active && obj.Destroyed =>
//if active, deactivate
obj.Active = false
ops.deactivateTelpadDeployableMessages(dguid)
//standard deployable elimination behavior
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalAction.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) if obj.Active =>
//if active, deactivate
obj.Active = false
ops.deactivateTelpadDeployableMessages(dguid)
//standard deployable elimination behavior
obj.Destroyed = true
ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2)
case LocalAction.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Destroyed =>
//standard deployable elimination behavior
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalAction.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) =>
//standard deployable elimination behavior
obj.Destroyed = true
ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2)
case LocalAction.EliminateDeployable(obj, dguid, _, _) if obj.Destroyed =>
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalAction.EliminateDeployable(obj, dguid, pos, effect) =>
obj.Destroyed = true
ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
case LocalAction.HackClear(targetGuid, unk1, unk2) =>
sendResponse(HackMessage(HackState1.Unk0, targetGuid, guid, progress=0, unk1.toFloat, HackState.HackCleared, unk2))
case LocalAction.HackObject(targetGuid, unk1, unk2) =>
sessionLogic.general.hackObject(targetGuid, unk1, unk2)
case PlanetsideAttribute(targetGuid, attributeType, attributeValue) =>
sessionLogic.general.sendPlanetsideAttributeMessage(targetGuid, attributeType, attributeValue)
case GenericObjectAction(targetGuid, actionNumber) =>
sendResponse(GenericObjectActionMessage(targetGuid, actionNumber))
case LocalAction.GenericActionMessage(actionNumber) =>
sendResponse(GenericActionMessage(actionNumber))
case LocalAction.LluSpawned(llu) =>
// Create LLU on client
sendResponse(ObjectCreateMessage(
llu.Definition.ObjectId,
llu.GUID,
llu.Definition.Packet.ConstructorData(llu).get
))
sendResponse(TriggerSoundMessage(TriggeredSound.LLUMaterialize, llu.Position, unk=20, volume=0.8000001f))
case LocalAction.LluDespawned(lluGuid, position) =>
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
sessionLogic.general.specialItemSlotGuid.collect { case guid if guid == lluGuid =>
sessionLogic.general.specialItemSlotGuid = None
player.Carrying = None
}
case ObjectDelete(objectGuid, unk) if isNotSameTarget =>
sendResponse(ObjectDeleteMessage(objectGuid, unk))
case LocalAction.ProximityTerminalEffect(object_guid, true) =>
sendResponse(ProximityTerminalUseMessage(Service.defaultPlayerGUID, object_guid, unk=true))
case LocalAction.ProximityTerminalEffect(objectGuid, false) =>
sendResponse(ProximityTerminalUseMessage(Service.defaultPlayerGUID, objectGuid, unk=false))
sessionLogic.terminals.ForgetAllProximityTerminals(objectGuid)
case LocalAction.RouterTelepadMessage(msg) =>
sendResponse(ChatMsg(ChatMessageType.UNK_229, wideContents=false, recipient="", msg, note=None))
case LocalAction.RouterTelepadTransport(passengerGuid, srcGuid, destGuid) =>
sessionLogic.general.useRouterTelepadEffect(passengerGuid, srcGuid, destGuid)
case SendResponse(msgs) =>
msgs.foreach {
case msg @ (m: GenericObjectActionMessage)
if (m.code == 58 || m.code == 60 || m.code == 61) && !sessionLogic.zoning.spawn.startEnqueueSquadMessages =>
// delay building virus alert if player is dead/respawning
sessionLogic.zoning.spawn.enqueueNewActivity(ActivityQueuedTask(
SpawnOperations.delaySendGenericObjectActionMessage(msg), 1)
)
case msg =>
sendResponse(msg)
}
case SetEmpire(objectGuid, empire) =>
sendResponse(SetEmpireMessage(objectGuid, empire))
case LocalAction.ShuttleEvent(ev) =>
val msg = OrbitalShuttleTimeMsg(
ev.u1,
ev.u2,
ev.t1,
ev.t2,
ev.t3,
pairs=ev.pairs.map { case ((a, b), c) => PadAndShuttlePair(a, b, c) }
)
sendResponse(msg)
case LocalAction.ShuttleDock(pguid, sguid, slot) =>
sendResponse(ObjectAttachMessage(pguid, sguid, slot))
case LocalAction.ShuttleUndock(pguid, sguid, pos, orient) =>
sendResponse(ObjectDetachMessage(pguid, sguid, pos, orient))
case LocalAction.ShuttleState(sguid, pos, orient, state) =>
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 LocalAction.ToggleTeleportSystem(router, systemPlan) =>
sessionLogic.general.toggleTeleportSystem(router, systemPlan)
case LocalAction.TriggerEffectAtLocation(targetGuid, effect, effectInfo, triggerLocation) =>
sendResponse(TriggerEffectMessage(targetGuid, effect, effectInfo, triggerLocation))
case LocalAction.TriggerSound(sound, pos, unk, volume) =>
sendResponse(TriggerSoundMessage(sound, pos, unk, volume))
case LocalAction.UpdateForceDomeStatus(buildingGuid, true) =>
sendResponse(GenericObjectActionMessage(buildingGuid, 11))
case LocalAction.UpdateForceDomeStatus(buildingGuid, false) =>
sendResponse(GenericObjectActionMessage(buildingGuid, 12))
case LocalAction.RechargeVehicleWeapon(vehicleGuid, weaponGuid) if resolvedPlayerGuid == guid =>
continent.GUID(vehicleGuid)
.collect { case vehicle: MountableWeapons => (vehicle, vehicle.PassengerInSeat(player)) }
.collect { case (vehicle, Some(seat_num)) => vehicle.WeaponControlledFromSeat(seat_num) }
.getOrElse(Set.empty)
.collect { case weapon: Tool if weapon.GUID == weaponGuid =>
sendResponse(InventoryStateMessage(weapon.AmmoSlot.Box.GUID, weapon.GUID, weapon.Magazine))
}
case LocalAction.ForceZoneChange(zone) =>
//todo we might be able to piggyback this for squad recalls later
if(session.zone eq zone) {
sessionLogic.zoning.zoneReload = true
zone.AvatarEvents ! Service.LeaveAll
zone.LocalEvents ! Service.LeaveAll
zone.VehicleEvents ! Service.LeaveAll
zone.AvatarEvents ! Service.Join(player.Name) //must manually restore this subscriptions
sessionLogic.zoning.spawn.handleNewPlayerLoaded(player) //will restart subscriptions and dispatch a LoadMapMessage
} else {
import akka.actor.typed.scaladsl.adapter._
sessionLogic.cluster ! InterstellarClusterService.GetRandomSpawnPoint(
zone.Number,
player.Faction,
Seq(SpawnGroup.Facility, SpawnGroup.Tower, SpawnGroup.AMS),
context.self
def receive: Receive = {
case SendResponse(msgs) =>
msgs.foreach {
case msg @ (m: GenericObjectActionMessage)
if (m.code == 58 || m.code == 60 || m.code == 61) && !sessionLogic.zoning.spawn.startEnqueueSquadMessages =>
// delay building virus alert if player is dead/respawning
sessionLogic.zoning.spawn.enqueueNewActivity(ActivityQueuedTask(
SpawnOperations.delaySendGenericObjectActionMessage(msg), 1)
)
case msg =>
sendResponse(msg)
}
case LocalAction.DeployableMapIcon(behavior, deployInfo) if filter.isNotSameTarget =>
sendResponse(DeployableObjectsInfoMessage(behavior, deployInfo))
case LocalAction.DeployableUIFor(item) =>
sessionLogic.general.updateDeployableUIElements(avatar.deployables.UpdateUIElement(item))
case LocalAction.Detonate(dguid, _: BoomerDeployable) =>
sendResponse(TriggerEffectMessage(dguid, "detonate_boomer"))
sendResponse(PlanetsideAttributeMessage(dguid, attribute_type=29, attribute_value=1))
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalAction.Detonate(dguid, _: ExplosiveDeployable) =>
sendResponse(GenericObjectActionMessage(dguid, code=19))
sendResponse(PlanetsideAttributeMessage(dguid, attribute_type=29, attribute_value=1))
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalAction.Detonate(_, obj) =>
log.warn(s"LocalAction.Detonate: ${obj.Definition.Name} not configured to explode correctly")
case LocalAction.DoorOpens(_, door) if filter.isNotSameTarget =>
val doorGuid = door.GUID
val pos = player.Position.xy
val range = ops.doorLoadRange()
val foundDoor = continent
.blockMap
.sector(pos, range)
.amenityList
.collect { case door: Door => door }
.find(_.GUID == doorGuid)
val doorExistsInRange: Boolean = foundDoor.nonEmpty
if (doorExistsInRange) {
sendResponse(GenericObjectStateMsg(doorGuid, state=16))
}
case LocalAction.DoorCloses(doorGuid) => //door closes for everyone
sendResponse(GenericObjectStateMsg(doorGuid, state=17))
case LocalAction.EliminateDeployable(obj: TurretDeployable, dguid, _, _) if obj.Destroyed =>
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalAction.EliminateDeployable(obj: TurretDeployable, dguid, pos, _) =>
obj.Destroyed = true
ops.DeconstructDeployable(
obj,
dguid,
pos,
obj.Orientation,
deletionType= if (obj.MountPoints.isEmpty) { 2 } else { 1 }
)
case LocalAction.EliminateDeployable(obj: ExplosiveDeployable, dguid, _, _)
if obj.Destroyed || obj.Jammed || obj.Health == 0 =>
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalAction.EliminateDeployable(obj: ExplosiveDeployable, dguid, pos, effect) =>
obj.Destroyed = true
ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
case LocalAction.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Active && obj.Destroyed =>
//if active, deactivate
obj.Active = false
ops.deactivateTelpadDeployableMessages(dguid)
//standard deployable elimination behavior
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalAction.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) if obj.Active =>
//if active, deactivate
obj.Active = false
ops.deactivateTelpadDeployableMessages(dguid)
//standard deployable elimination behavior
obj.Destroyed = true
ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2)
case LocalAction.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Destroyed =>
//standard deployable elimination behavior
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalAction.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) =>
//standard deployable elimination behavior
obj.Destroyed = true
ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2)
case LocalAction.EliminateDeployable(obj, dguid, _, _) if obj.Destroyed =>
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalAction.EliminateDeployable(obj, dguid, pos, effect) =>
obj.Destroyed = true
ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
case LocalAction.HackClear(targetGuid, unk1, unk2) =>
sendResponse(HackMessage(HackState1.Unk0, targetGuid, filter.resolvedPlayerGuid, progress=0, unk1.toFloat, HackState.HackCleared, unk2))
case LocalAction.HackObject(targetGuid, unk1, unk2) =>
sessionLogic.general.hackObject(targetGuid, unk1, unk2)
case PlanetsideAttribute(targetGuid, attributeType, attributeValue) =>
sessionLogic.general.sendPlanetsideAttributeMessage(targetGuid, attributeType, attributeValue)
case LocalAction.GenericActionMessage(actionNumber) =>
sendResponse(GenericActionMessage(actionNumber))
case LocalAction.LluSpawned(llu) =>
// Create LLU on client
sendResponse(ObjectCreateMessage(
llu.Definition.ObjectId,
llu.GUID,
llu.Definition.Packet.ConstructorData(llu).get
))
sendResponse(TriggerSoundMessage(TriggeredSound.LLUMaterialize, llu.Position, unk=20, volume=0.8000001f))
case LocalAction.LluDespawned(lluGuid, position) =>
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
sessionLogic.general.specialItemSlotGuid.collect { case guid if guid == lluGuid =>
sessionLogic.general.specialItemSlotGuid = None
player.Carrying = None
}
case LocalAction.ProximityTerminalEffect(object_guid, true) =>
sendResponse(ProximityTerminalUseMessage(Service.defaultPlayerGUID, object_guid, unk=true))
case LocalAction.ProximityTerminalEffect(objectGuid, false) =>
sendResponse(ProximityTerminalUseMessage(Service.defaultPlayerGUID, objectGuid, unk=false))
sessionLogic.terminals.ForgetAllProximityTerminals(objectGuid)
case LocalAction.RouterTelepadMessage(msg) =>
sendResponse(ChatMsg(ChatMessageType.UNK_229, wideContents=false, recipient="", msg, note=None))
case LocalAction.RouterTelepadTransport(passengerGuid, srcGuid, destGuid) =>
sessionLogic.general.useRouterTelepadEffect(passengerGuid, srcGuid, destGuid)
case LocalAction.ShuttleEvent(ev) =>
val msg = OrbitalShuttleTimeMsg(
ev.u1,
ev.u2,
ev.t1,
ev.t2,
ev.t3,
pairs=ev.pairs.map { case ((a, b), c) => PadAndShuttlePair(a, b, c) }
)
sendResponse(msg)
case LocalAction.ShuttleDock(pguid, sguid, slot) =>
sendResponse(ObjectAttachMessage(pguid, sguid, slot))
case LocalAction.ShuttleUndock(pguid, sguid, pos, orient) =>
sendResponse(ObjectDetachMessage(pguid, sguid, pos, orient))
case LocalAction.ShuttleState(sguid, pos, orient, state) =>
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 LocalAction.ToggleTeleportSystem(router, systemPlan) =>
sessionLogic.general.toggleTeleportSystem(router, systemPlan)
case LocalAction.TriggerEffectAtLocation(targetGuid, effect, effectInfo, triggerLocation) =>
sendResponse(TriggerEffectMessage(targetGuid, effect, effectInfo, triggerLocation))
case LocalAction.TriggerSound(sound, pos, unk, volume) =>
sendResponse(TriggerSoundMessage(sound, pos, unk, volume))
case LocalAction.UpdateForceDomeStatus(buildingGuid, true) =>
sendResponse(GenericObjectActionMessage(buildingGuid, 11))
case LocalAction.UpdateForceDomeStatus(buildingGuid, false) =>
sendResponse(GenericObjectActionMessage(buildingGuid, 12))
case LocalAction.RechargeVehicleWeapon(vehicleGuid, weaponGuid) if filter.isSameTarget /*resolvedPlayerGuid == guid*/ =>
continent.GUID(vehicleGuid)
.collect { case vehicle: MountableWeapons => (vehicle, vehicle.PassengerInSeat(player)) }
.collect { case (vehicle, Some(seat_num)) => vehicle.WeaponControlledFromSeat(seat_num) }
.getOrElse(Set.empty)
.collect { case weapon: Tool if weapon.GUID == weaponGuid =>
sendResponse(InventoryStateMessage(weapon.AmmoSlot.Box.GUID, weapon.GUID, weapon.Magazine))
}
case _ => ()
}
case LocalAction.ForceZoneChange(zone) =>
//todo we might be able to piggyback this for squad recalls later
if(session.zone eq zone) {
sessionLogic.zoning.zoneReload = true
zone.AvatarEvents ! Service.LeaveAll
zone.LocalEvents ! Service.LeaveAll
zone.VehicleEvents ! Service.LeaveAll
zone.AvatarEvents ! Service.Join(player.Name) //must manually restore this subscriptions
sessionLogic.zoning.spawn.handleNewPlayerLoaded(player) //will restart subscriptions and dispatch a LoadMapMessage
} else {
import akka.actor.typed.scaladsl.adapter._
sessionLogic.cluster ! InterstellarClusterService.GetRandomSpawnPoint(
zone.Number,
player.Faction,
Seq(SpawnGroup.Facility, SpawnGroup.Tower, SpawnGroup.AMS),
context.self
)
}
}
/* support functions */

View file

@ -1,6 +1,7 @@
// Copyright (c) 2024 PSForever
package net.psforever.actors.session.normal
import akka.actor.Actor.Receive
import akka.actor.{ActorContext, ActorRef, typed}
import net.psforever.actors.session.AvatarActor
import net.psforever.actors.session.support.{SessionData, SessionVehicleHandlers, VehicleHandlerFunctions}
@ -15,7 +16,7 @@ import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
import net.psforever.packet.game.{ChangeAmmoMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChatMsg, ChildObjectStateMessage, DeadState, DeployRequestMessage, DismountVehicleMsg, FrameVehicleStateMessage, GenericObjectActionMessage, HitHint, InventoryStateMessage, ObjectAttachMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, PlanetsideAttributeMessage, ReloadMessage, ServerVehicleOverrideMsg, VehicleStateMessage, WeaponDryFireMessage}
import net.psforever.services.Service
import net.psforever.services.base.envelope.GenericResponseEnvelope
import net.psforever.services.base.message.{ChangeAmmo, ChangeFireState_Start, ChangeFireState_Stop, EventResponse, GenericObjectAction, HintsAtAttacker, ObjectDelete, PlanetsideAttribute, ReloadTool, SendResponse, WeaponDryFire}
import net.psforever.services.base.message.{ChangeAmmo, ChangeFireState_Start, ChangeFireState_Stop, GenericObjectAction, HintsAtAttacker, ObjectDelete, PlanetsideAttribute, ReloadTool, SendResponse, WeaponDryFire}
import net.psforever.services.local.support.CaptureFlagManager
import net.psforever.services.vehicle.{VehicleAction, VehicleStamp}
import net.psforever.types.{BailType, ChatMessageType, PlanetSideGUID, Vector3}
@ -33,367 +34,305 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context:
private val galaxyService: ActorRef = ops.galaxyService
/**
* na
*
* @param toChannel na
* @param guid na
* @param reply na
*/
def handle(toChannel: String, guid: PlanetSideGUID, reply: EventResponse): Unit = {
val resolvedPlayerGuid = if (player.HasGUID) {
player.GUID
} else {
PlanetSideGUID(-1)
}
val isNotSameTarget = resolvedPlayerGuid != guid
reply match {
case VehicleAction.VehicleState(
vehicleGuid,
unk1,
pos,
orient,
vel,
unk2,
unk3,
unk4,
wheelDirection,
unk5,
unk6
) if isNotSameTarget && player.VehicleSeated.contains(vehicleGuid) =>
//player who is also in the vehicle (not driver)
sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, orient, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6))
player.Position = pos
player.Orientation = orient
player.Velocity = vel
sessionLogic.updateLocalBlockMap(pos)
//llu destruction check
if (player.Carrying.contains(SpecialCarry.CaptureFlag)) {
continent
.GUID(player.VehicleSeated)
.collect { case vehicle: Vehicle =>
CaptureFlagManager.ReasonToLoseFlagViolently(continent, sessionLogic.general.specialItemSlotGuid, vehicle)
}
}
case VehicleAction.VehicleState(
vehicleGuid,
unk1,
pos,
ang,
vel,
unk2,
unk3,
unk4,
wheelDirection,
unk5,
unk6
) if isNotSameTarget =>
//player who is watching the vehicle from the outside
sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, ang, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6))
case VehicleAction.ChildObjectState(objectGuid, pitch, yaw) if isNotSameTarget =>
sendResponse(ChildObjectStateMessage(objectGuid, pitch, yaw))
case VehicleAction.FrameVehicleState(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA)
if isNotSameTarget =>
sendResponse(FrameVehicleStateMessage(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA))
case ChangeFireState_Start(weaponGuid) if isNotSameTarget =>
sendResponse(ChangeFireStateMessage_Start(weaponGuid))
case ChangeFireState_Stop(weaponGuid) if isNotSameTarget =>
sendResponse(ChangeFireStateMessage_Stop(weaponGuid))
case ReloadTool(itemGuid) if isNotSameTarget =>
sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0))
case ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) if isNotSameTarget =>
sendResponse(ObjectDetachMessage(weapon_guid, previous_guid, Vector3.Zero, 0))
//TODO? sendResponse(ObjectDeleteMessage(previousAmmoGuid, 0))
sendResponse(
ObjectCreateMessage(
ammo_id,
ammo_guid,
ObjectCreateMessageParent(weapon_guid, weapon_slot),
ammo_data
)
)
sendResponse(ChangeAmmoMessage(weapon_guid, 1))
case WeaponDryFire(weaponGuid) if isNotSameTarget =>
continent.GUID(weaponGuid).collect {
case tool: Tool if tool.Magazine == 0 =>
// check that the magazine is still empty before sending WeaponDryFireMessage
// if it has been reloaded since then, other clients will not see it firing
sendResponse(WeaponDryFireMessage(weaponGuid))
}
case VehicleAction.DismountVehicle(bailType, wasKickedByDriver) if isNotSameTarget =>
sendResponse(DismountVehicleMsg(guid, bailType, wasKickedByDriver))
case VehicleAction.MountVehicle(vehicleGuid, seat) if isNotSameTarget =>
sendResponse(ObjectAttachMessage(vehicleGuid, guid, seat))
case VehicleAction.DeployRequest(objectGuid, state, unk1, unk2, pos) if isNotSameTarget =>
sendResponse(DeployRequestMessage(guid, objectGuid, state, unk1, unk2, pos))
case SendResponse(msgs) =>
msgs.foreach(sendResponse)
case VehicleAction.EquipmentCreatedInSlot(pkt) if isNotSameTarget =>
sendResponse(pkt)
case GenericObjectAction(objectGuid, action) if isNotSameTarget =>
sendResponse(GenericObjectActionMessage(objectGuid, action))
case HintsAtAttacker(sourceGuid) if player.isAlive =>
sendResponse(HitHint(sourceGuid, player.GUID))
case VehicleAction.InventoryState(obj, parentGuid, start, conData) if isNotSameTarget =>
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
val objGuid = obj.GUID
sendResponse(ObjectDeleteMessage(objGuid, unk1=0))
sendResponse(ObjectCreateDetailedMessage(
obj.Definition.ObjectId,
objGuid,
ObjectCreateMessageParent(parentGuid, start),
conData
))
case VehicleAction.KickPassenger(_, wasKickedByDriver, vehicleGuid) if resolvedPlayerGuid == guid =>
//seat number (first field) seems to be correct if passenger is kicked manually by driver
//but always seems to return 4 if user is kicked by mount permissions changing
sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver))
val typeOfRide = continent.GUID(vehicleGuid) match {
case Some(obj: Vehicle) =>
sessionLogic.general.unaccessContainer(obj)
s"the ${obj.Definition.Name}'s seat by ${obj.OwnerName.getOrElse("the pilot")}"
case _ =>
s"${player.Sex.possessive} ride"
}
log.info(s"${player.Name} has been kicked from $typeOfRide!")
player.WhichSide = OutsideOf
case VehicleAction.KickPassenger(_, wasKickedByDriver, _) =>
//seat number (first field) seems to be correct if passenger is kicked manually by driver
//but always seems to return 4 if user is kicked by mount permissions changing
sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver))
case VehicleAction.InventoryState2(objGuid, parentGuid, value) if isNotSameTarget =>
sendResponse(InventoryStateMessage(objGuid, unk=0, parentGuid, value))
case VehicleAction.LoadVehicle(vehicle, vtype, vguid, vdata) if isNotSameTarget =>
//this is not be suitable for vehicles with people who are seated in it before it spawns (if that is possible)
sendResponse(ObjectCreateMessage(vtype, vguid, vdata))
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
case ObjectDelete(itemGuid, _) if isNotSameTarget =>
sendResponse(ObjectDeleteMessage(itemGuid, unk1=0))
case VehicleAction.Ownership(vehicleGuid) if resolvedPlayerGuid == guid =>
//Only the player that owns this vehicle needs the ownership packet
avatarActor ! AvatarActor.SetVehicle(Some(vehicleGuid))
sendResponse(PlanetsideAttributeMessage(resolvedPlayerGuid, attribute_type=21, vehicleGuid))
case VehicleAction.LoseOwnership(_, vehicleGuid) =>
ops.announceAmsDecay(vehicleGuid,msg = "@ams_decaystarted")
case PlanetsideAttribute(vehicleGuid, attributeType, attributeValue) if isNotSameTarget =>
sendResponse(PlanetsideAttributeMessage(vehicleGuid, attributeType, attributeValue))
case VehicleAction.SeatPermissions(vehicleGuid, seatGroup, permission) if isNotSameTarget =>
sendResponse(PlanetsideAttributeMessage(vehicleGuid, seatGroup, permission))
case VehicleAction.StowCreatedEquipment(vehicleGuid, slot, itemType, itemGuid, itemData) if isNotSameTarget =>
//TODO prefer ObjectAttachMessage, but how to force ammo pools to update properly?
sendResponse(ObjectCreateDetailedMessage(itemType, itemGuid, ObjectCreateMessageParent(vehicleGuid, slot), itemData))
case VehicleAction.UnloadVehicle(_, vehicleGuid) =>
sendResponse(ObjectDeleteMessage(vehicleGuid, unk1=1))
if (sessionLogic.zoning.spawn.prevSpawnPoint.map(_.Owner).exists {
case ams: Vehicle =>
ams.GUID == vehicleGuid &&
ams.OwnerGuid.isEmpty
case _ =>
false
}) {
sessionLogic.zoning.spawn.prevSpawnPoint = None
sendResponse(ChatMsg(ChatMessageType.UNK_229, "@ams_decayed"))
}
case VehicleAction.UnstowEquipment(itemGuid) if isNotSameTarget =>
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
sendResponse(ObjectDeleteMessage(itemGuid, unk1=0))
case VehicleAction.UpdateAmsSpawnList(list) =>
sessionLogic.zoning.spawn.amsSpawnPoints = list.filter(tube => tube.Faction == player.Faction)
sessionLogic.zoning.spawn.DrawCurrentAmsSpawnPoint()
case VehicleAction.TransferPassengerChannel(oldChannel, tempChannel, vehicle, vehicleToDelete) if isNotSameTarget =>
sessionLogic.zoning.interstellarFerry = Some(vehicle)
sessionLogic.zoning.interstellarFerryTopLevelGUID = Some(vehicleToDelete)
continent.VehicleEvents ! Service.Leave(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 VehicleAction.KickCargo(vehicle, speed, delay)
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
sessionLogic.vehicles.ServerVehicleOverrideWithPacket(
vehicle,
ServerVehicleOverrideMsg(
lock_accelerator=true,
lock_wheel=true,
reverse=true,
unk4=false,
lock_vthrust=0,
strafe,
reverseSpeed,
unk8=Some(0)
)
)
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
val resp = GenericResponseEnvelope(
VehicleStamp,
toChannel,
PlanetSideGUID(0),
VehicleAction.KickCargo(vehicle, speed=0, delay)
)
context.system.scheduler.scheduleOnce(delay milliseconds, context.self, resp)
case VehicleAction.KickCargo(cargo, _, _)
if player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive =>
sessionLogic.vehicles.TotalDriverVehicleControl(cargo)
case VehicleAction.ChangeLoadout(target, oldWeapons, addedWeapons, oldInventory, newInventory)
if player.avatar.vehicle.contains(target) =>
//TODO when vehicle weapons can be changed without visual glitches, rewrite this
continent.GUID(target).collect { case vehicle: Vehicle =>
import net.psforever.login.WorldSession.boolToInt
//owner: must unregister old equipment, and register and install new equipment
(oldWeapons ++ oldInventory).foreach {
case (obj, eguid) =>
sendResponse(ObjectDeleteMessage(eguid, unk1=0))
TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj))
def receive: Receive = {
case VehicleAction.VehicleState(
vehicleGuid,
unk1,
pos,
orient,
vel,
unk2,
unk3,
unk4,
wheelDirection,
unk5,
unk6
) if filter.isNotSameTarget && player.VehicleSeated.contains(vehicleGuid) =>
//player who is also in the vehicle (not driver)
sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, orient, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6))
player.Position = pos
player.Orientation = orient
player.Velocity = vel
sessionLogic.updateLocalBlockMap(pos)
//llu destruction check
if (player.Carrying.contains(SpecialCarry.CaptureFlag)) {
continent
.GUID(player.VehicleSeated)
.collect { case vehicle: Vehicle =>
CaptureFlagManager.ReasonToLoseFlagViolently(continent, sessionLogic.general.specialItemSlotGuid, vehicle)
}
sessionLogic.general.applyPurchaseTimersBeforePackingLoadout(player, vehicle, addedWeapons ++ newInventory)
//jammer or unjamm new weapons based on vehicle status
val vehicleJammered = vehicle.Jammed
addedWeapons
.map { _.obj }
.collect {
case jamItem: JammableUnit if jamItem.Jammed != vehicleJammered =>
jamItem.Jammed = vehicleJammered
JammableMountedWeapons.JammedWeaponStatus(vehicle.Zone, jamItem, vehicleJammered)
}
changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory)
}
}
case VehicleAction.ChangeLoadout(target, oldWeapons, _, oldInventory, _)
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
(oldWeapons ++ oldInventory).foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, unk1=0)) }
changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory)
}
case VehicleAction.VehicleState(
vehicleGuid,
unk1,
pos,
ang,
vel,
unk2,
unk3,
unk4,
wheelDirection,
unk5,
unk6
) if filter.isNotSameTarget =>
//player who is watching the vehicle from the outside
sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, ang, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6))
case VehicleAction.ChangeLoadout(target, oldWeapons, _, oldInventory, _) =>
//TODO when vehicle weapons can be changed without visual glitches, rewrite this
continent.GUID(target).collect { case vehicle: Vehicle =>
changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory)
}
case VehicleAction.ChildObjectState(objectGuid, pitch, yaw) if filter.isNotSameTarget =>
sendResponse(ChildObjectStateMessage(objectGuid, pitch, yaw))
case VehicleSpawnPad.AttachToRails(vehicle, pad) =>
sendResponse(ObjectAttachMessage(pad.GUID, vehicle.GUID, slot=3))
case VehicleAction.FrameVehicleState(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA)
if filter.isNotSameTarget =>
sendResponse(FrameVehicleStateMessage(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA))
case VehicleSpawnPad.ConcealPlayer(playerGuid) =>
sendResponse(GenericObjectActionMessage(playerGuid, code=9))
case VehicleAction.DismountVehicle(bailType, wasKickedByDriver) if filter.isNotSameTarget =>
sendResponse(DismountVehicleMsg(filter.resolvedPlayerGuid, bailType, wasKickedByDriver))
case VehicleSpawnPad.DetachFromRails(vehicle, pad) =>
val padDefinition = pad.Definition
sendResponse(
ObjectDetachMessage(
pad.GUID,
vehicle.GUID,
pad.Position + Vector3.z(padDefinition.VehicleCreationZOffset),
pad.Orientation.z + padDefinition.VehicleCreationZOrientOffset
)
case VehicleAction.MountVehicle(vehicleGuid, seat) if filter.isNotSameTarget =>
sendResponse(ObjectAttachMessage(vehicleGuid, filter.resolvedPlayerGuid, seat))
case VehicleAction.DeployRequest(objectGuid, state, unk1, unk2, pos) if filter.isNotSameTarget =>
sendResponse(DeployRequestMessage(filter.resolvedPlayerGuid, objectGuid, state, unk1, unk2, pos))
case VehicleAction.EquipmentCreatedInSlot(pkt) if filter.isNotSameTarget =>
sendResponse(pkt)
case VehicleAction.InventoryState(obj, parentGuid, start, conData) if filter.isNotSameTarget =>
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
val objGuid = obj.GUID
sendResponse(ObjectDeleteMessage(objGuid, unk1=0))
sendResponse(ObjectCreateDetailedMessage(
obj.Definition.ObjectId,
objGuid,
ObjectCreateMessageParent(parentGuid, start),
conData
))
case VehicleAction.KickPassenger(_, wasKickedByDriver, vehicleGuid) if filter.isSameTarget /*resolvedPlayerGuid == guid*/ =>
//seat number (first field) seems to be correct if passenger is kicked manually by driver
//but always seems to return 4 if user is kicked by mount permissions changing
sendResponse(DismountVehicleMsg(filter.resolvedPlayerGuid, BailType.Kicked, wasKickedByDriver))
val typeOfRide = continent.GUID(vehicleGuid) match {
case Some(obj: Vehicle) =>
sessionLogic.general.unaccessContainer(obj)
s"the ${obj.Definition.Name}'s seat by ${obj.OwnerName.getOrElse("the pilot")}"
case _ =>
s"${player.Sex.possessive} ride"
}
log.info(s"${player.Name} has been kicked from $typeOfRide!")
player.WhichSide = OutsideOf
case VehicleAction.KickPassenger(_, wasKickedByDriver, _) =>
//seat number (first field) seems to be correct if passenger is kicked manually by driver
//but always seems to return 4 if user is kicked by mount permissions changing
sendResponse(DismountVehicleMsg(filter.resolvedPlayerGuid, BailType.Kicked, wasKickedByDriver))
case VehicleAction.InventoryState2(objGuid, parentGuid, value) if filter.isNotSameTarget =>
sendResponse(InventoryStateMessage(objGuid, unk=0, parentGuid, value))
case VehicleAction.LoadVehicle(vehicle, vtype, vguid, vdata) if filter.isNotSameTarget =>
//this is not be suitable for vehicles with people who are seated in it before it spawns (if that is possible)
sendResponse(ObjectCreateMessage(vtype, vguid, vdata))
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
case VehicleAction.Ownership(vehicleGuid) if filter.isSameTarget /*resolvedPlayerGuid == guid*/ =>
//Only the player that owns this vehicle needs the ownership packet
avatarActor ! AvatarActor.SetVehicle(Some(vehicleGuid))
sendResponse(PlanetsideAttributeMessage(filter.resolvedPlayerGuid, attribute_type=21, vehicleGuid))
case VehicleAction.LoseOwnership(_, vehicleGuid) =>
ops.announceAmsDecay(vehicleGuid,msg = "@ams_decaystarted")
case VehicleAction.SeatPermissions(vehicleGuid, seatGroup, permission) if filter.isNotSameTarget =>
sendResponse(PlanetsideAttributeMessage(vehicleGuid, seatGroup, permission))
case VehicleAction.StowCreatedEquipment(vehicleGuid, slot, itemType, itemGuid, itemData) if filter.isNotSameTarget =>
//TODO prefer ObjectAttachMessage, but how to force ammo pools to update properly?
sendResponse(ObjectCreateDetailedMessage(itemType, itemGuid, ObjectCreateMessageParent(vehicleGuid, slot), itemData))
case VehicleAction.UnloadVehicle(_, vehicleGuid) =>
sendResponse(ObjectDeleteMessage(vehicleGuid, unk1=1))
if (sessionLogic.zoning.spawn.prevSpawnPoint.map(_.Owner).exists {
case ams: Vehicle =>
ams.GUID == vehicleGuid &&
ams.OwnerGuid.isEmpty
case _ =>
false
}) {
sessionLogic.zoning.spawn.prevSpawnPoint = None
sendResponse(ChatMsg(ChatMessageType.UNK_229, "@ams_decayed"))
}
case VehicleAction.UnstowEquipment(itemGuid) if filter.isNotSameTarget =>
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
sendResponse(ObjectDeleteMessage(itemGuid, unk1=0))
case VehicleAction.UpdateAmsSpawnList(list) =>
sessionLogic.zoning.spawn.amsSpawnPoints = list.filter(tube => tube.Faction == player.Faction)
sessionLogic.zoning.spawn.DrawCurrentAmsSpawnPoint()
case VehicleAction.TransferPassengerChannel(oldChannel, tempChannel, vehicle, vehicleToDelete) if filter.isNotSameTarget =>
sessionLogic.zoning.interstellarFerry = Some(vehicle)
sessionLogic.zoning.interstellarFerryTopLevelGUID = Some(vehicleToDelete)
continent.VehicleEvents ! Service.Leave(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 VehicleAction.KickCargo(vehicle, speed, delay)
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
sessionLogic.vehicles.ServerVehicleOverrideWithPacket(
vehicle,
ServerVehicleOverrideMsg(
lock_accelerator=true,
lock_wheel=true,
reverse=true,
unk4=false,
lock_vthrust=0,
strafe,
reverseSpeed,
unk8=Some(0)
)
)
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
val resp = GenericResponseEnvelope(
VehicleStamp,
"",
PlanetSideGUID(0),
VehicleAction.KickCargo(vehicle, speed=0, delay)
)
context.system.scheduler.scheduleOnce(delay milliseconds, context.self, resp)
case VehicleSpawnPad.ResetSpawnPad(pad) =>
sendResponse(GenericObjectActionMessage(pad.GUID, code=23))
case VehicleAction.KickCargo(cargo, _, _)
if player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive =>
sessionLogic.vehicles.TotalDriverVehicleControl(cargo)
case VehicleSpawnPad.RevealPlayer(playerGuid) =>
sendResponse(GenericObjectActionMessage(playerGuid, code=10))
case VehicleSpawnPad.StartPlayerSeatedInVehicle(vehicle, _)
if player.VisibleSlots.contains(player.DrawnSlot) =>
player.DrawnSlot = Player.HandsDownSlot
startPlayerSeatedInVehicle(vehicle)
case VehicleSpawnPad.StartPlayerSeatedInVehicle(vehicle, _) =>
startPlayerSeatedInVehicle(vehicle)
case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle, _) =>
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
sessionLogic.vehicles.ServerVehicleOverrideWithPacket(
vehicle,
ServerVehicleOverrideMsg(
lock_accelerator=true,
lock_wheel=true,
reverse=true,
unk4=false,
lock_vthrust=1,
lock_strafe=0,
movement_speed=0,
unk8=Some(0)
)
)
sessionLogic.vehicles.serverVehicleControlVelocity = Some(0)
case VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, _) =>
val vdef = vehicle.Definition
sessionLogic.vehicles.ServerVehicleOverrideWithPacket(
vehicle,
ServerVehicleOverrideMsg(
lock_accelerator=true,
lock_wheel=true,
reverse=false,
unk4=false,
lock_vthrust=if (GlobalDefinitions.isFlightVehicle(vdef)) { 1 } else { 0 },
lock_strafe=0,
movement_speed=vdef.AutoPilotSpeed1,
unk8=Some(0)
)
)
case VehicleSpawnPad.ServerVehicleOverrideEnd(vehicle, _) =>
sessionLogic.vehicles.ServerVehicleOverrideStop(vehicle)
case VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, data) =>
val str = s"${data.getOrElse("The vehicle spawn pad where you placed your order is blocked.")}"
val msg = if (str.contains("@")) {
ChatMsg(ChatMessageType.UNK_229, str)
} else {
ChatMsg(ChatMessageType.CMT_OPEN, wideContents = true, recipient = "", str, note = None)
case VehicleAction.ChangeLoadout(target, oldWeapons, addedWeapons, oldInventory, newInventory)
if player.avatar.vehicle.contains(target) =>
//TODO when vehicle weapons can be changed without visual glitches, rewrite this
continent.GUID(target).collect { case vehicle: Vehicle =>
import net.psforever.login.WorldSession.boolToInt
//owner: must unregister old equipment, and register and install new equipment
(oldWeapons ++ oldInventory).foreach {
case (obj, eguid) =>
sendResponse(ObjectDeleteMessage(eguid, unk1=0))
TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj))
}
sendResponse(msg)
sessionLogic.general.applyPurchaseTimersBeforePackingLoadout(player, vehicle, addedWeapons ++ newInventory)
//jammer or unjamm new weapons based on vehicle status
val vehicleJammered = vehicle.Jammed
addedWeapons
.map { _.obj }
.collect {
case jamItem: JammableUnit if jamItem.Jammed != vehicleJammered =>
jamItem.Jammed = vehicleJammered
JammableMountedWeapons.JammedWeaponStatus(vehicle.Zone, jamItem, vehicleJammered)
}
changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory)
}
case VehicleSpawnPad.PeriodicReminder(_, data) =>
val (isType, flag, msg): (ChatMessageType, Boolean, String) = data match {
case Some(msg: String) if msg.startsWith("@") => (ChatMessageType.UNK_227, false, msg)
case Some(msg: String) => (ChatMessageType.CMT_OPEN, true, msg)
case _ => (ChatMessageType.CMT_OPEN, true, "Your vehicle order has been cancelled.")
}
sendResponse(ChatMsg(isType, flag, recipient="", msg, None))
case VehicleAction.ChangeLoadout(target, oldWeapons, _, oldInventory, _)
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
(oldWeapons ++ oldInventory).foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, unk1=0)) }
changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory)
}
case _ => ()
}
case VehicleAction.ChangeLoadout(target, oldWeapons, _, oldInventory, _) =>
//TODO when vehicle weapons can be changed without visual glitches, rewrite this
continent.GUID(target).collect { case vehicle: Vehicle =>
changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory)
}
case VehicleSpawnPad.AttachToRails(vehicle, pad) =>
sendResponse(ObjectAttachMessage(pad.GUID, vehicle.GUID, slot=3))
case VehicleSpawnPad.ConcealPlayer(playerGuid) =>
sendResponse(GenericObjectActionMessage(playerGuid, code=9))
case VehicleSpawnPad.DetachFromRails(vehicle, pad) =>
val padDefinition = pad.Definition
sendResponse(
ObjectDetachMessage(
pad.GUID,
vehicle.GUID,
pad.Position + Vector3.z(padDefinition.VehicleCreationZOffset),
pad.Orientation.z + padDefinition.VehicleCreationZOrientOffset
)
)
case VehicleSpawnPad.ResetSpawnPad(pad) =>
sendResponse(GenericObjectActionMessage(pad.GUID, code=23))
case VehicleSpawnPad.RevealPlayer(playerGuid) =>
sendResponse(GenericObjectActionMessage(playerGuid, code=10))
case VehicleSpawnPad.StartPlayerSeatedInVehicle(vehicle, _)
if player.VisibleSlots.contains(player.DrawnSlot) =>
player.DrawnSlot = Player.HandsDownSlot
startPlayerSeatedInVehicle(vehicle)
case VehicleSpawnPad.StartPlayerSeatedInVehicle(vehicle, _) =>
startPlayerSeatedInVehicle(vehicle)
case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle, _) =>
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
sessionLogic.vehicles.ServerVehicleOverrideWithPacket(
vehicle,
ServerVehicleOverrideMsg(
lock_accelerator=true,
lock_wheel=true,
reverse=true,
unk4=false,
lock_vthrust=1,
lock_strafe=0,
movement_speed=0,
unk8=Some(0)
)
)
sessionLogic.vehicles.serverVehicleControlVelocity = Some(0)
case VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, _) =>
val vdef = vehicle.Definition
sessionLogic.vehicles.ServerVehicleOverrideWithPacket(
vehicle,
ServerVehicleOverrideMsg(
lock_accelerator=true,
lock_wheel=true,
reverse=false,
unk4=false,
lock_vthrust=if (GlobalDefinitions.isFlightVehicle(vdef)) { 1 } else { 0 },
lock_strafe=0,
movement_speed=vdef.AutoPilotSpeed1,
unk8=Some(0)
)
)
case VehicleSpawnPad.ServerVehicleOverrideEnd(vehicle, _) =>
sessionLogic.vehicles.ServerVehicleOverrideStop(vehicle)
case VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, data) =>
val str = s"${data.getOrElse("The vehicle spawn pad where you placed your order is blocked.")}"
val msg = if (str.contains("@")) {
ChatMsg(ChatMessageType.UNK_229, str)
} else {
ChatMsg(ChatMessageType.CMT_OPEN, wideContents = true, recipient = "", str, note = None)
}
sendResponse(msg)
case VehicleSpawnPad.PeriodicReminder(_, data) =>
val (isType, flag, msg): (ChatMessageType, Boolean, String) = data match {
case Some(msg: String) if msg.startsWith("@") => (ChatMessageType.UNK_227, false, msg)
case Some(msg: String) => (ChatMessageType.CMT_OPEN, true, msg)
case _ => (ChatMessageType.CMT_OPEN, true, "Your vehicle order has been cancelled.")
}
sendResponse(ChatMsg(isType, flag, recipient="", msg, None))
}
private def changeLoadoutDeleteOldEquipment(

View file

@ -1,6 +1,7 @@
// Copyright (c) 2024 PSForever
package net.psforever.actors.session.spectator
import akka.actor.Actor.Receive
import akka.actor.ActorContext
import net.psforever.actors.session.support.{SessionData, SessionVehicleHandlers, VehicleHandlerFunctions}
import net.psforever.objects.{Tool, Vehicle, Vehicles}
@ -9,7 +10,7 @@ import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent
import net.psforever.packet.game.{ChangeAmmoMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChildObjectStateMessage, DeadState, DeployRequestMessage, DismountVehicleMsg, FrameVehicleStateMessage, GenericObjectActionMessage, InventoryStateMessage, ObjectAttachMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, PlanetsideAttributeMessage, ReloadMessage, ServerVehicleOverrideMsg, VehicleStateMessage, WeaponDryFireMessage}
import net.psforever.services.base.envelope.GenericResponseEnvelope
import net.psforever.services.base.message.{ChangeAmmo, ChangeFireState_Start, ChangeFireState_Stop, EventResponse, GenericObjectAction, ObjectDelete, PlanetsideAttribute, ReloadTool, SendResponse, WeaponDryFire}
import net.psforever.services.base.message.{ChangeAmmo, ChangeFireState_Start, ChangeFireState_Stop, GenericObjectAction, ObjectDelete, PlanetsideAttribute, ReloadTool, SendResponse, WeaponDryFire}
import net.psforever.services.vehicle.{VehicleAction, VehicleStamp}
import net.psforever.types.{BailType, PlanetSideGUID, Vector3}
@ -26,232 +27,173 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context:
//private val galaxyService: ActorRef = ops.galaxyService
/**
* na
*
* @param toChannel na
* @param guid na
* @param reply na
*/
def handle(toChannel: String, guid: PlanetSideGUID, reply: EventResponse): Unit = {
val resolvedPlayerGuid = if (player.HasGUID) {
player.GUID
} else {
PlanetSideGUID(-1)
}
val isNotSameTarget = resolvedPlayerGuid != guid
reply match {
case VehicleAction.VehicleState(
vehicleGuid,
unk1,
pos,
orient,
vel,
unk2,
unk3,
unk4,
wheelDirection,
unk5,
unk6
) if isNotSameTarget && player.VehicleSeated.contains(vehicleGuid) =>
//player who is also in the vehicle (not driver)
sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, orient, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6))
player.Position = pos
player.Orientation = orient
player.Velocity = vel
sessionLogic.updateLocalBlockMap(pos)
def receive: Receive = {
case VehicleAction.VehicleState(
vehicleGuid,
unk1,
pos,
orient,
vel,
unk2,
unk3,
unk4,
wheelDirection,
unk5,
unk6
) if filter.isNotSameTarget && player.VehicleSeated.contains(vehicleGuid) =>
//player who is also in the vehicle (not driver)
sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, orient, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6))
player.Position = pos
player.Orientation = orient
player.Velocity = vel
sessionLogic.updateLocalBlockMap(pos)
case VehicleAction.VehicleState(
vehicleGuid,
unk1,
pos,
ang,
vel,
unk2,
unk3,
unk4,
wheelDirection,
unk5,
unk6
) if isNotSameTarget =>
//player who is watching the vehicle from the outside
sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, ang, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6))
case VehicleAction.VehicleState(
vehicleGuid,
unk1,
pos,
ang,
vel,
unk2,
unk3,
unk4,
wheelDirection,
unk5,
unk6
) if filter.isNotSameTarget =>
//player who is watching the vehicle from the outside
sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, ang, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6))
case VehicleAction.ChildObjectState(objectGuid, pitch, yaw) if isNotSameTarget =>
sendResponse(ChildObjectStateMessage(objectGuid, pitch, yaw))
case VehicleAction.ChildObjectState(objectGuid, pitch, yaw) if filter.isNotSameTarget =>
sendResponse(ChildObjectStateMessage(objectGuid, pitch, yaw))
case VehicleAction.FrameVehicleState(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA)
if isNotSameTarget =>
sendResponse(FrameVehicleStateMessage(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA))
case VehicleAction.FrameVehicleState(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA)
if filter.isNotSameTarget =>
sendResponse(FrameVehicleStateMessage(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA))
case ChangeFireState_Start(weaponGuid) if isNotSameTarget =>
sendResponse(ChangeFireStateMessage_Start(weaponGuid))
case VehicleAction.DismountVehicle(bailType, wasKickedByDriver) if filter.isNotSameTarget =>
sendResponse(DismountVehicleMsg(filter.resolvedPlayerGuid, bailType, wasKickedByDriver))
case ChangeFireState_Stop(weaponGuid) if isNotSameTarget =>
sendResponse(ChangeFireStateMessage_Stop(weaponGuid))
case VehicleAction.MountVehicle(vehicleGuid, seat) if filter.isNotSameTarget =>
sendResponse(ObjectAttachMessage(vehicleGuid, filter.resolvedPlayerGuid, seat))
case ReloadTool(itemGuid) if isNotSameTarget =>
sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0))
case VehicleAction.DeployRequest(objectGuid, state, unk1, unk2, pos) if filter.isNotSameTarget =>
sendResponse(DeployRequestMessage(filter.resolvedPlayerGuid, objectGuid, state, unk1, unk2, pos))
case ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) if isNotSameTarget =>
sendResponse(ObjectDetachMessage(weapon_guid, previous_guid, Vector3.Zero, 0))
//TODO? sendResponse(ObjectDeleteMessage(previousAmmoGuid, 0))
sendResponse(
ObjectCreateMessage(
ammo_id,
ammo_guid,
ObjectCreateMessageParent(weapon_guid, weapon_slot),
ammo_data
)
case VehicleAction.EquipmentCreatedInSlot(pkt) if filter.isNotSameTarget =>
sendResponse(pkt)
case VehicleAction.InventoryState(obj, parentGuid, start, conData) if filter.isNotSameTarget =>
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
val objGuid = obj.GUID
sendResponse(ObjectDeleteMessage(objGuid, unk1=0))
sendResponse(ObjectCreateDetailedMessage(
obj.Definition.ObjectId,
objGuid,
ObjectCreateMessageParent(parentGuid, start),
conData
))
case VehicleAction.KickPassenger(_, wasKickedByDriver, vehicleGuid) if filter.isSameTarget =>
//seat number (first field) seems to be correct if passenger is kicked manually by driver
//but always seems to return 4 if user is kicked by mount permissions changing
sendResponse(DismountVehicleMsg(filter.resolvedPlayerGuid, BailType.Kicked, wasKickedByDriver))
continent.GUID(vehicleGuid) match {
case Some(obj: Vehicle) =>
sessionLogic.general.unaccessContainer(obj)
case _ => ()
}
case VehicleAction.KickPassenger(_, wasKickedByDriver, _) =>
//seat number (first field) seems to be correct if passenger is kicked manually by driver
//but always seems to return 4 if user is kicked by mount permissions changing
sendResponse(DismountVehicleMsg(filter.resolvedPlayerGuid, BailType.Kicked, wasKickedByDriver))
case VehicleAction.InventoryState2(objGuid, parentGuid, value) if filter.isNotSameTarget =>
sendResponse(InventoryStateMessage(objGuid, unk=0, parentGuid, value))
case VehicleAction.LoadVehicle(vehicle, vtype, vguid, vdata) if filter.isNotSameTarget =>
//this is not be suitable for vehicles with people who are seated in it before it spawns (if that is possible)
sendResponse(ObjectCreateMessage(vtype, vguid, vdata))
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
case VehicleAction.SeatPermissions(vehicleGuid, seatGroup, permission) if filter.isNotSameTarget =>
sendResponse(PlanetsideAttributeMessage(vehicleGuid, seatGroup, permission))
case VehicleAction.UnloadVehicle(_, vehicleGuid) =>
sendResponse(ObjectDeleteMessage(vehicleGuid, unk1=1))
case VehicleAction.UnstowEquipment(itemGuid) if filter.isNotSameTarget =>
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
sendResponse(ObjectDeleteMessage(itemGuid, unk1=0))
case VehicleAction.UpdateAmsSpawnList(list) =>
sessionLogic.zoning.spawn.amsSpawnPoints = list.filter(tube => tube.Faction == player.Faction)
sessionLogic.zoning.spawn.DrawCurrentAmsSpawnPoint()
case VehicleAction.KickCargo(vehicle, speed, delay)
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
sessionLogic.vehicles.ServerVehicleOverrideWithPacket(
vehicle,
ServerVehicleOverrideMsg(
lock_accelerator=true,
lock_wheel=true,
reverse=true,
unk4=false,
lock_vthrust=0,
strafe,
reverseSpeed,
unk8=Some(0)
)
sendResponse(ChangeAmmoMessage(weapon_guid, 1))
)
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
val resp = GenericResponseEnvelope(
VehicleStamp,
"",
PlanetSideGUID(0),
VehicleAction.KickCargo(vehicle, speed=0, delay)
)
context.system.scheduler.scheduleOnce(delay milliseconds, context.self, resp)
case WeaponDryFire(weaponGuid) if isNotSameTarget =>
continent.GUID(weaponGuid).collect {
case tool: Tool if tool.Magazine == 0 =>
// check that the magazine is still empty before sending WeaponDryFireMessage
// if it has been reloaded since then, other clients will not see it firing
sendResponse(WeaponDryFireMessage(weaponGuid))
}
case VehicleAction.KickCargo(cargo, _, _)
if player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive =>
sessionLogic.vehicles.TotalDriverVehicleControl(cargo)
case VehicleAction.DismountVehicle(bailType, wasKickedByDriver) if isNotSameTarget =>
sendResponse(DismountVehicleMsg(guid, bailType, wasKickedByDriver))
case VehicleSpawnPad.AttachToRails(vehicle, pad) =>
sendResponse(ObjectAttachMessage(pad.GUID, vehicle.GUID, slot=3))
case VehicleAction.MountVehicle(vehicleGuid, seat) if isNotSameTarget =>
sendResponse(ObjectAttachMessage(vehicleGuid, guid, seat))
case VehicleSpawnPad.ConcealPlayer(playerGuid) =>
sendResponse(GenericObjectActionMessage(playerGuid, code=9))
case VehicleAction.DeployRequest(objectGuid, state, unk1, unk2, pos) if isNotSameTarget =>
sendResponse(DeployRequestMessage(guid, objectGuid, state, unk1, unk2, pos))
case SendResponse(msgs) =>
msgs.foreach(sendResponse)
case VehicleAction.EquipmentCreatedInSlot(pkt) if isNotSameTarget =>
sendResponse(pkt)
case GenericObjectAction(objectGuid, action) if isNotSameTarget =>
sendResponse(GenericObjectActionMessage(objectGuid, action))
case VehicleAction.InventoryState(obj, parentGuid, start, conData) if isNotSameTarget =>
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
val objGuid = obj.GUID
sendResponse(ObjectDeleteMessage(objGuid, unk1=0))
sendResponse(ObjectCreateDetailedMessage(
obj.Definition.ObjectId,
objGuid,
ObjectCreateMessageParent(parentGuid, start),
conData
))
case VehicleAction.KickPassenger(_, wasKickedByDriver, vehicleGuid) if resolvedPlayerGuid == guid =>
//seat number (first field) seems to be correct if passenger is kicked manually by driver
//but always seems to return 4 if user is kicked by mount permissions changing
sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver))
continent.GUID(vehicleGuid) match {
case Some(obj: Vehicle) =>
sessionLogic.general.unaccessContainer(obj)
case _ => ()
}
case VehicleAction.KickPassenger(_, wasKickedByDriver, _) =>
//seat number (first field) seems to be correct if passenger is kicked manually by driver
//but always seems to return 4 if user is kicked by mount permissions changing
sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver))
case VehicleAction.InventoryState2(objGuid, parentGuid, value) if isNotSameTarget =>
sendResponse(InventoryStateMessage(objGuid, unk=0, parentGuid, value))
case VehicleAction.LoadVehicle(vehicle, vtype, vguid, vdata) if isNotSameTarget =>
//this is not be suitable for vehicles with people who are seated in it before it spawns (if that is possible)
sendResponse(ObjectCreateMessage(vtype, vguid, vdata))
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
case ObjectDelete(itemGuid, _) if isNotSameTarget =>
sendResponse(ObjectDeleteMessage(itemGuid, unk1=0))
case PlanetsideAttribute(vehicleGuid, attributeType, attributeValue) if isNotSameTarget =>
sendResponse(PlanetsideAttributeMessage(vehicleGuid, attributeType, attributeValue))
case VehicleAction.SeatPermissions(vehicleGuid, seatGroup, permission) if isNotSameTarget =>
sendResponse(PlanetsideAttributeMessage(vehicleGuid, seatGroup, permission))
case VehicleAction.UnloadVehicle(_, vehicleGuid) =>
sendResponse(ObjectDeleteMessage(vehicleGuid, unk1=1))
case VehicleAction.UnstowEquipment(itemGuid) if isNotSameTarget =>
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
sendResponse(ObjectDeleteMessage(itemGuid, unk1=0))
case VehicleAction.UpdateAmsSpawnList(list) =>
sessionLogic.zoning.spawn.amsSpawnPoints = list.filter(tube => tube.Faction == player.Faction)
sessionLogic.zoning.spawn.DrawCurrentAmsSpawnPoint()
case VehicleAction.KickCargo(vehicle, speed, delay)
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
sessionLogic.vehicles.ServerVehicleOverrideWithPacket(
vehicle,
ServerVehicleOverrideMsg(
lock_accelerator=true,
lock_wheel=true,
reverse=true,
unk4=false,
lock_vthrust=0,
strafe,
reverseSpeed,
unk8=Some(0)
)
case VehicleSpawnPad.DetachFromRails(vehicle, pad) =>
val padDefinition = pad.Definition
sendResponse(
ObjectDetachMessage(
pad.GUID,
vehicle.GUID,
pad.Position + Vector3.z(padDefinition.VehicleCreationZOffset),
pad.Orientation.z + padDefinition.VehicleCreationZOrientOffset
)
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
val resp = GenericResponseEnvelope(
VehicleStamp,
toChannel,
PlanetSideGUID(0),
VehicleAction.KickCargo(vehicle, speed=0, delay)
)
context.system.scheduler.scheduleOnce(delay milliseconds, context.self, resp)
)
case VehicleAction.KickCargo(cargo, _, _)
if player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive =>
sessionLogic.vehicles.TotalDriverVehicleControl(cargo)
case VehicleSpawnPad.ResetSpawnPad(pad) =>
sendResponse(GenericObjectActionMessage(pad.GUID, code=23))
case VehicleSpawnPad.AttachToRails(vehicle, pad) =>
sendResponse(ObjectAttachMessage(pad.GUID, vehicle.GUID, slot=3))
case VehicleSpawnPad.RevealPlayer(playerGuid) =>
sendResponse(GenericObjectActionMessage(playerGuid, code=10))
case VehicleSpawnPad.ConcealPlayer(playerGuid) =>
sendResponse(GenericObjectActionMessage(playerGuid, code=9))
case VehicleSpawnPad.ServerVehicleOverrideEnd(vehicle, _) =>
sessionLogic.vehicles.ServerVehicleOverrideStop(vehicle)
case VehicleSpawnPad.DetachFromRails(vehicle, pad) =>
val padDefinition = pad.Definition
sendResponse(
ObjectDetachMessage(
pad.GUID,
vehicle.GUID,
pad.Position + Vector3.z(padDefinition.VehicleCreationZOffset),
pad.Orientation.z + padDefinition.VehicleCreationZOrientOffset
)
)
case VehicleSpawnPad.ResetSpawnPad(pad) =>
sendResponse(GenericObjectActionMessage(pad.GUID, code=23))
case VehicleSpawnPad.RevealPlayer(playerGuid) =>
sendResponse(GenericObjectActionMessage(playerGuid, code=10))
case VehicleSpawnPad.ServerVehicleOverrideEnd(vehicle, _) =>
sessionLogic.vehicles.ServerVehicleOverrideStop(vehicle)
case VehicleAction.ChangeLoadout(target, oldWeapons, _, oldInventory, _) =>
//TODO when vehicle weapons can be changed without visual glitches, rewrite this
continent.GUID(target).collect { case vehicle: Vehicle =>
changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory)
}
case _ => ()
}
case VehicleAction.ChangeLoadout(target, oldWeapons, _, oldInventory, _) =>
//TODO when vehicle weapons can be changed without visual glitches, rewrite this
continent.GUID(target).collect { case vehicle: Vehicle =>
changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory)
}
}
private def changeLoadoutDeleteOldEquipment(

View file

@ -0,0 +1,105 @@
// Copyright (c) 2026 PSForever
package net.psforever.actors.session.support
import akka.actor.Actor.Receive
import net.psforever.objects.Player
import net.psforever.services.Service
import net.psforever.services.base.message.EventResponse
import net.psforever.types.PlanetSideGUID
trait HandlerFilter {
def resolvedPlayerGuid: PlanetSideGUID
def isNotSameTarget: Boolean
def isSameTarget: Boolean = !isNotSameTarget
}
class HandlerFilterClass(guid1: PlanetSideGUID, guid2: PlanetSideGUID) extends HandlerFilter {
val resolvedPlayerGuid: PlanetSideGUID = guid2
val isNotSameTarget: Boolean = resolvedPlayerGuid != guid1
}
object HandlerFilter {
def apply(guid1: PlanetSideGUID, guid2: PlanetSideGUID): HandlerFilter = {
new HandlerFilterClass(guid1, guid2)
}
def apply(guid: PlanetSideGUID, player: Player): HandlerFilter = {
this(guid, if (player != null && player.HasGUID) {
player.GUID
} else {
Service.defaultPlayerGUID
})
}
final val NeverAllow: HandlerFilter = new HandlerFilter {
def resolvedPlayerGuid: PlanetSideGUID = PlanetSideGUID(-1)
def isNotSameTarget: Boolean = false
override def isSameTarget: Boolean = false
}
final def Allow(guid: PlanetSideGUID): HandlerFilter = new HandlerFilter {
def resolvedPlayerGuid: PlanetSideGUID = guid
def isNotSameTarget: Boolean = true
override def isSameTarget: Boolean = true
}
}
trait CommonHandlerFunctionsBase {
/**
* na
* @param toChannel na
* @param guid na
* @param reply na
*/
def handle(toChannel: String, guid: PlanetSideGUID, reply: EventResponse): Unit
def handleWith(guid: PlanetSideGUID): Receive
def handleWith(filter: HandlerFilter): Receive
def receive: Receive
}
trait CommonHandlerFunctions extends CommonHandlerFunctionsBase {
_: CommonSessionInterfacingFunctionality =>
protected var filter: HandlerFilter = HandlerFilter.NeverAllow
/**
* na
* @param toChannel na
* @param guid na
* @param reply na
*/
def handle(toChannel: String, guid: PlanetSideGUID, reply: EventResponse): Unit = {
filter = HandlerFilter(guid, player)
receive.apply(reply)
}
def handleWith(guid: PlanetSideGUID): Receive = {
filter = HandlerFilter(guid, player)
receive
}
def handleWith(giveFilter: HandlerFilter): Receive = {
filter = giveFilter
receive
}
def receive: Receive
}
object CommonHandlerFunctions {
val HandleNothing: CommonHandlerFunctionsBase = new CommonHandlerFunctionsBase {
def handle(toChannel: String, guid: PlanetSideGUID, reply: EventResponse): Unit = { }
def handleWith(guid: PlanetSideGUID): Receive = receive
def handleWith(filter: HandlerFilter): Receive = receive
def receive: Receive = { case _: CommonHandlerFunctions => () }
}
val HandleAnything: CommonHandlerFunctionsBase = new CommonHandlerFunctionsBase {
def handle(toChannel: String, guid: PlanetSideGUID, reply: EventResponse): Unit = receive.apply(reply)
def handleWith(guid: PlanetSideGUID): Receive = receive
def handleWith(filter: HandlerFilter): Receive = receive
def receive: Receive = { case _ => () }
}
}

View file

@ -0,0 +1,67 @@
// Copyright (c) 2026 PSForever
package net.psforever.actors.session.support
import akka.actor.Actor.Receive
import akka.actor.ActorContext
import net.psforever.objects.Tool
import net.psforever.packet.game.{ChangeAmmoMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, GenericObjectActionMessage, HitHint, ObjectDeleteMessage, PlanetsideAttributeMessage, ReloadMessage, SetEmpireMessage, WeaponDryFireMessage}
import net.psforever.services.base.message.{ChangeAmmo, ChangeFireState_Start, ChangeFireState_Stop, ConcealPlayer, GenericObjectAction, HintsAtAttacker, ObjectDelete, PlanetsideAttribute, ReloadTool, SendResponse, SetEmpire, WeaponDryFire}
class CommonHandlerLogic(val sessionLogic: SessionData, implicit val context: ActorContext)
extends CommonSessionInterfacingFunctionality with CommonHandlerFunctions {
def receive: Receive = {
case PlanetsideAttribute(target_guid, attributeType, attributeValue)
if filter.isNotSameTarget =>
sendResponse(PlanetsideAttributeMessage(target_guid, attributeType, attributeValue))
case GenericObjectAction(objectGuid, actionCode)
if filter.isNotSameTarget =>
sendResponse(GenericObjectActionMessage(objectGuid, actionCode))
case ObjectDelete(itemGuid, unk)
if filter.isNotSameTarget =>
sendResponse(ObjectDeleteMessage(itemGuid, unk))
case ChangeFireState_Start(weaponGuid)
if filter.isNotSameTarget =>
sendResponse(ChangeFireStateMessage_Start(weaponGuid))
case ChangeFireState_Stop(weaponGuid)
if filter.isNotSameTarget =>
sendResponse(ChangeFireStateMessage_Stop(weaponGuid))
case ReloadTool(itemGuid)
if filter.isNotSameTarget =>
sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0))
case ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data)
if filter.isNotSameTarget =>
sessionLogic.avatarResponse.changeAmmoProcedure(weapon_guid, previous_guid, ammo_id, ammo_guid, weapon_slot, ammo_data)
sendResponse(ChangeAmmoMessage(weapon_guid, 1))
case WeaponDryFire(weaponGuid)
if filter.isNotSameTarget =>
continent.GUID(weaponGuid).collect {
case tool: Tool if tool.Magazine == 0 =>
// check that the magazine is still empty before sending WeaponDryFireMessage
// if it has been reloaded since then, other clients will not see it firing
sendResponse(WeaponDryFireMessage(weaponGuid))
}
case HintsAtAttacker(sourceGuid)
if player.isAlive =>
sendResponse(HitHint(sourceGuid, filter.resolvedPlayerGuid))
sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg")
case SetEmpire(objectGuid, faction)
if filter.isNotSameTarget =>
sendResponse(SetEmpireMessage(objectGuid, faction))
case ConcealPlayer(_) =>
sendResponse(GenericObjectActionMessage(filter.resolvedPlayerGuid, code=9))
case SendResponse(msgs) =>
msgs.foreach(sendResponse)
}
}

View file

@ -10,7 +10,7 @@ import net.psforever.objects.zones.exp
import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarStamp}
import net.psforever.services.base.envelope.{GenericResponseEnvelope, MessageEnvelope}
import net.psforever.services.base.message.{EventResponse, SendResponse}
import net.psforever.services.base.message.SendResponse
import net.psforever.services.chat.OutfitChannel
import scala.collection.mutable
@ -21,10 +21,8 @@ import net.psforever.packet.game._
import net.psforever.types._
import net.psforever.util.Config
trait AvatarHandlerFunctions extends CommonSessionInterfacingFunctionality {
trait AvatarHandlerFunctions extends CommonSessionInterfacingFunctionality with CommonHandlerFunctions {
val ops: SessionAvatarHandlers
def handle(toChannel: String, guid: PlanetSideGUID, reply: EventResponse): Unit
}
class SessionAvatarHandlers(
@ -37,7 +35,7 @@ class SessionAvatarHandlers(
mutable.LongMap[SessionAvatarHandlers.LastUpstream]()
private[session] val hidingPlayerRandomizer = new scala.util.Random
def changeAmmoProcedures(
def changeAmmoProcedure(
weaponGuid: PlanetSideGUID,
previousAmmoGuid: PlanetSideGUID,
ammoTypeId: Int,
@ -46,7 +44,7 @@ class SessionAvatarHandlers(
ammoData: ConstructorData
): Unit = {
sendResponse(ObjectDetachMessage(weaponGuid, previousAmmoGuid, Vector3.Zero, 0))
//TODO? sendResponse(ObjectDeleteMessage(previousAmmoGuid, 0))
sendResponse(ObjectDeleteMessage(previousAmmoGuid, 0))
sendResponse(
ObjectCreateMessage(
ammoTypeId,

View file

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

View file

@ -7,17 +7,14 @@ import net.psforever.objects.ce.Deployable
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects.serverobject.interior.Sidedness
import net.psforever.packet.game.{GenericObjectActionMessage, ObjectDeleteMessage, PlanetsideAttributeMessage, TriggerEffectMessage}
import net.psforever.services.base.message.EventResponse
import net.psforever.types.{PlanetSideGUID, Vector3}
trait LocalHandlerFunctions extends CommonSessionInterfacingFunctionality {
trait LocalHandlerFunctions extends CommonSessionInterfacingFunctionality with CommonHandlerFunctions {
def ops: SessionLocalHandlers
def handleTurretDeployableIsDismissed(obj: TurretDeployable): Unit
def handleDeployableIsDismissed(obj: Deployable): Unit
def handle(toChannel: String, guid: PlanetSideGUID, reply: EventResponse): Unit
}
class SessionLocalHandlers(

View file

@ -5,13 +5,10 @@ import akka.actor.{ActorContext, ActorRef, typed}
import net.psforever.actors.session.AvatarActor
import net.psforever.objects.Vehicle
import net.psforever.packet.game.ChatMsg
import net.psforever.services.base.message.EventResponse
import net.psforever.types.{ChatMessageType, DriveState, PlanetSideGUID}
trait VehicleHandlerFunctions extends CommonSessionInterfacingFunctionality {
trait VehicleHandlerFunctions extends CommonSessionInterfacingFunctionality with CommonHandlerFunctions {
def ops: SessionVehicleHandlers
def handle(toChannel: String, guid: PlanetSideGUID, reply: EventResponse): Unit
}
class SessionVehicleHandlers(

View file

@ -2195,7 +2195,7 @@ class ZoningOperations(
log.info(s"RestoreInfo: player $name is already logged in zone ${inZone.id}; rejoining that character")
sessionLogic.persistFunc = UpdatePersistence(from)
//tell the old WorldSessionActor to kill itself by using its own subscriptions against itself
inZone.AvatarEvents ! MessageEnvelope(name, AvatarAction.TeardownConnection())
inZone.AvatarEvents ! MessageEnvelope(name, AvatarAction.TeardownConnection)
spawn.switchAvatarStatisticsFieldToRefreshAfterRespawn()
//find and reload previous player
(

View file

@ -279,7 +279,6 @@ object Vehicles {
if (GlobalDefinitions.isBattleFrameVehicle(target.Definition) && target.Seat(0).isDefined) {
zone.LocalEvents ! MessageEnvelope(
zoneid,
PlanetSideGUID(-1),
GenericObjectAction(target.GUID, GenericObjectActionEnum.BFRShieldsDown.id)
)
zone.LocalEvents ! MessageEnvelope(

View file

@ -109,7 +109,6 @@ class ResourceSiloControl(resourceSilo: ResourceSilo)
val zone = building.Zone
building.Zone.AvatarEvents ! MessageEnvelope(
zone.id,
building.GUID,
PlanetsideAttribute(building.GUID, 47, if (resourceSilo.LowNtuWarningOn) 1 else 0)
)
}
@ -133,7 +132,6 @@ class ResourceSiloControl(resourceSilo: ResourceSilo)
)
zone.AvatarEvents ! MessageEnvelope(
zone.id,
resourceSilo.GUID,
PlanetsideAttribute(resourceSilo.GUID, 45, resourceSilo.CapacitorDisplay)
)
building.Actor ! BuildingActor.MapUpdate()

View file

@ -201,5 +201,5 @@ object AvatarAction {
final case class RemoveFromOutfitChat(outfit_id: Long) extends SelfRespondingEvent
final case class TeardownConnection() extends SelfRespondingEvent
final case object TeardownConnection extends SelfRespondingEvent
}

View file

@ -5,7 +5,6 @@ import akka.actor.{ActorContext, ActorRef, Props}
import net.psforever.objects.guid.{GUIDTask, TaskBundle}
import net.psforever.objects.Player
import net.psforever.types.{ExoSuitType, PlanetSideGUID}
import net.psforever.services.Service
import net.psforever.services.avatar.AvatarAction.Release
import net.psforever.services.base.envelope.MessageEnvelope
import net.psforever.services.base.{EventServiceSupport, GenericSupportEnvelope, GenericSupportEnvelopeOnly}
@ -37,7 +36,7 @@ final case class ReleaseEnvelope(
object ReleaseEnvelope {
def apply(channel: String, actionMessage: Release): ReleaseEnvelope =
ReleaseEnvelope(channel, Service.defaultPlayerGUID, actionMessage)
ReleaseEnvelope(channel, actionMessage.player.GUID, actionMessage)
}
final case class CorpseEnvelope(supportMessage: Any)

View file

@ -63,7 +63,6 @@ class CaptureFlagManager(zone: Zone) extends Actor {
// Override CC message when looked at
zone.LocalEvents ! MessageEnvelope(
zone.id,
PlanetSideGUID(-1),
GenericObjectAction(
capture_terminal.GUID,
GenericObjectActionEnum.FlagSpawned.id

View file

@ -246,7 +246,7 @@ class HackCaptureActor extends Actor {
// Notify all clients that CC has had its hack state changed
terminal.Zone.LocalEvents ! MessageEnvelope(
terminal.Zone.id,
PlanetSideGUID(-1),
PlanetSideGUID(-1), /*what is this?, says the person who wrote it*/
PlanetsideAttribute(
terminal.GUID,
PlanetsideAttributeEnum.ControlConsoleHackUpdate.id,