Code Style Improvements 2 (#1050)

* changes to the session actor handler classes

* further changes to session actor handler classes

* extending the range of voice emote penetration

* rollback of changes to SessionGalaxyHandlers to preserve passenger zoning behavior
This commit is contained in:
Fate-JH 2023-03-16 14:05:21 -04:00 committed by GitHub
parent 7e899e9ef3
commit 53e3f9a08d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 1239 additions and 1079 deletions

View file

@ -5,13 +5,6 @@ import akka.actor.typed.{ActorRef, Behavior, PostStop, SupervisorStrategy}
import akka.actor.typed.receptionist.Receptionist import akka.actor.typed.receptionist.Receptionist
import akka.actor.typed.scaladsl.{ActorContext, Behaviors, StashBuffer} import akka.actor.typed.scaladsl.{ActorContext, Behaviors, StashBuffer}
import akka.actor.typed.scaladsl.adapter._ import akka.actor.typed.scaladsl.adapter._
import net.psforever.objects.avatar.{Shortcut => AvatarShortcut}
import net.psforever.objects.definition.ImplantDefinition
import net.psforever.packet.game.{CreateShortcutMessage, Shortcut}
import net.psforever.packet.game.objectcreate.DrawnSlot
import net.psforever.types.ChatMessageType.{CMT_GMOPEN, UNK_227}
import net.psforever.types.{ExperienceType, ImplantType}
import scala.collection.mutable import scala.collection.mutable
import scala.concurrent.ExecutionContextExecutor import scala.concurrent.ExecutionContextExecutor
import scala.concurrent.duration._ import scala.concurrent.duration._
@ -19,18 +12,20 @@ import scala.concurrent.duration._
import net.psforever.actors.zone.BuildingActor import net.psforever.actors.zone.BuildingActor
import net.psforever.login.WorldSession import net.psforever.login.WorldSession
import net.psforever.objects.{Default, Player, Session} import net.psforever.objects.{Default, Player, Session}
import net.psforever.objects.avatar.{BattleRank, Certification, CommandRank} import net.psforever.objects.avatar.{BattleRank, Certification, CommandRank, Shortcut => AvatarShortcut}
import net.psforever.objects.definition.ImplantDefinition
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
import net.psforever.objects.serverobject.structures.{Amenity, Building} import net.psforever.objects.serverobject.structures.{Amenity, Building}
import net.psforever.objects.serverobject.turret.{FacilityTurret, TurretUpgrade, WeaponTurrets} import net.psforever.objects.serverobject.turret.{FacilityTurret, TurretUpgrade, WeaponTurrets}
import net.psforever.objects.zones.Zoning import net.psforever.objects.zones.Zoning
import net.psforever.packet.game.{ChatMsg, DeadState, RequestDestroyMessage, ZonePopulationUpdateMessage} import net.psforever.packet.game.objectcreate.DrawnSlot
import net.psforever.packet.game.{ChatMsg, CreateShortcutMessage, DeadState, RequestDestroyMessage, Shortcut, ZonePopulationUpdateMessage}
import net.psforever.services.{CavernRotationService, InterstellarClusterService} import net.psforever.services.{CavernRotationService, InterstellarClusterService}
import net.psforever.services.chat.ChatService import net.psforever.services.chat.ChatService
import net.psforever.services.chat.ChatService.ChatChannel import net.psforever.services.chat.ChatService.ChatChannel
import net.psforever.types.ChatMessageType.UNK_229 import net.psforever.types.ChatMessageType.{CMT_GMOPEN, UNK_227, UNK_229}
import net.psforever.types.{ChatMessageType, Cosmetic, PlanetSideEmpire, PlanetSideGUID, Vector3} import net.psforever.types.{ChatMessageType, Cosmetic, ExperienceType, ImplantType, PlanetSideEmpire, PlanetSideGUID, Vector3}
import net.psforever.util.{Config, PointOfInterest} import net.psforever.util.{Config, PointOfInterest}
import net.psforever.zones.Zones import net.psforever.zones.Zones
@ -998,9 +993,8 @@ class ChatActor(
sessionActor ! SessionActor.SendResponse(message) sessionActor ! SessionActor.SendResponse(message)
case CMT_VOICE => case CMT_VOICE =>
if ( if (
session.zone == fromSession.zone && (session.zone == fromSession.zone || message.contents.startsWith("SH")) && /*tactical squad voice macro*/
Vector3.DistanceSquared(session.player.Position, fromSession.player.Position) < 625 || Vector3.DistanceSquared(session.player.Position, fromSession.player.Position) < 1600
message.contents.startsWith("SH") // tactical squad voice macro
) { ) {
val name = fromSession.avatar.name val name = fromSession.avatar.name
if (!session.avatar.people.ignored.exists { f => f.name.equals(name) } || if (!session.avatar.people.ignored.exists { f => f.name.equals(name) } ||

View file

@ -283,8 +283,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
sessionFuncs.vehicles.handleCanNotChangeDeployment(obj, state, reason) sessionFuncs.vehicles.handleCanNotChangeDeployment(obj, state, reason)
/* rare messages */ /* rare messages */
case ProximityUnit.StopAction(term, target) => case ProximityUnit.StopAction(term, _) =>
sessionFuncs.terminals.LocalStopUsingProximityUnit(term, target) sessionFuncs.terminals.LocalStopUsingProximityUnit(term)
case SessionActor.Suicide() => case SessionActor.Suicide() =>
sessionFuncs.suicide(sessionFuncs.player) sessionFuncs.suicide(sessionFuncs.player)

View file

@ -3,6 +3,8 @@ package net.psforever.actors.session.support
import akka.actor.typed.scaladsl.adapter._ import akka.actor.typed.scaladsl.adapter._
import akka.actor.{ActorContext, typed} import akka.actor.{ActorContext, typed}
import net.psforever.services.Service
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._ import scala.concurrent.duration._
// //
@ -29,172 +31,349 @@ class SessionAvatarHandlers(
chatActor: typed.ActorRef[ChatActor.Command], chatActor: typed.ActorRef[ChatActor.Command],
implicit val context: ActorContext implicit val context: ActorContext
) extends CommonSessionInterfacingFunctionality { ) extends CommonSessionInterfacingFunctionality {
private[support] var lastSeenStreamMessage: Array[Long] = Array.fill[Long](65535)(elem=0L) //TODO player characters only exist within a certain range of GUIDs for a given zone; this is overkill
private[support] var lastSeenStreamMessage: Array[SessionAvatarHandlers.LastUpstream] = {
SessionAvatarHandlers.blankUpstreamMessages(65535)
}
private[this] val hidingPlayerRandomizer = new scala.util.Random
/** /**
* na * na
*
* @param toChannel na * @param toChannel na
* @param guid na * @param guid na
* @param reply na * @param reply na
*/ */
def handle(toChannel: String, guid: PlanetSideGUID, reply: AvatarResponse.Response): Unit = { def handle(toChannel: String, guid: PlanetSideGUID, reply: AvatarResponse.Response): Unit = {
val tplayer_guid = val resolvedPlayerGuid = if (player != null && player.HasGUID) {
if (player != null && player.HasGUID) player.GUID player.GUID
else PlanetSideGUID(0) } else {
Service.defaultPlayerGUID
}
val isNotSameTarget = resolvedPlayerGuid != guid
val isSameTarget = !isNotSameTarget
reply match { reply match {
/* special messages */
case AvatarResponse.TeardownConnection() => case AvatarResponse.TeardownConnection() =>
log.trace(s"ending ${player.Name}'s old session by event system request (relog)") log.trace(s"ending ${player.Name}'s old session by event system request (relog)")
context.stop(context.self) context.stop(context.self)
case AvatarResponse.SendResponse(msg) => /* really common messages (very frequently, every life) */
sendResponse(msg) case pstate @ AvatarResponse.PlayerState(
pos,
case AvatarResponse.SendResponseTargeted(target_guid, msg) => vel,
if (tplayer_guid == target_guid) { yaw,
sendResponse(msg) pitch,
yawUpper,
_,
isCrouching,
isJumping,
jumpThrust,
isCloaking,
spectating,
_
) if isNotSameTarget =>
val now = System.currentTimeMillis()
val (location, time, distanceSq): (Vector3, Long, Float) = if (spectating) {
val r2 = 2 + hidingPlayerRandomizer.nextInt(4000).toFloat
(Vector3(r2, r2, 1f), 0L, 0f)
} else {
val before = lastSeenStreamMessage(guid.guid).time
val dist = Vector3.DistanceSquared(player.Position, pos)
(pos, now - before, dist)
} }
if (distanceSq < 302500 || time > 5000) { // Render distance seems to be approx 525m. Reduce update rate at ~550m to be safe
case AvatarResponse.Revive(target_guid) =>
if (tplayer_guid == target_guid) {
log.info(s"No time for rest, ${player.Name}. Back on your feet!")
sessionData.zoning.spawn.reviveTimer.cancel()
sessionData.zoning.spawn.deadState = DeadState.Alive
player.Revive
val health = player.Health
sendResponse(PlanetsideAttributeMessage(target_guid, 0, health))
sendResponse(AvatarDeadStateMessage(DeadState.Alive, 0, 0, player.Position, player.Faction, unk5=true))
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.PlanetsideAttributeToAll(target_guid, 0, health)
)
}
case AvatarResponse.ArmorChanged(suit, subtype) =>
if (tplayer_guid != guid) {
sendResponse(ArmorChangedMessage(guid, suit, subtype))
}
case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) =>
if (tplayer_guid != guid) {
sendResponse(ObjectDetachMessage(weapon_guid, previous_guid, Vector3.Zero, 0))
sendResponse( sendResponse(
ObjectCreateMessage( PlayerStateMessage(
ammo_id, guid,
ammo_guid, location,
ObjectCreateMessageParent(weapon_guid, weapon_slot), vel,
ammo_data yaw,
pitch,
yawUpper,
timestamp = 0,
isCrouching,
isJumping,
jumpThrust,
isCloaking
) )
) )
sendResponse(ChangeAmmoMessage(weapon_guid, 1)) lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstate), now)
} }
case AvatarResponse.ChangeFireMode(item_guid, mode) => case AvatarResponse.ObjectHeld(slot, _)
if (tplayer_guid != guid) { if isSameTarget && player.VisibleSlots.contains(slot) =>
sendResponse(ChangeFireModeMessage(item_guid, mode)) 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)
} }
case AvatarResponse.ChangeFireState_Start(weapon_guid) => case AvatarResponse.ObjectHeld(slot, _)
if (tplayer_guid != guid) { if isSameTarget && slot > -1 =>
sendResponse(ChangeFireStateMessage_Start(weapon_guid)) sendResponse(ObjectHeldMessage(guid, slot, unk1=true))
}
case AvatarResponse.ChangeFireState_Stop(weapon_guid) => case AvatarResponse.ObjectHeld(_, _)
if (tplayer_guid != guid) { if isSameTarget => ()
sendResponse(ChangeFireStateMessage_Stop(weapon_guid))
}
case AvatarResponse.ConcealPlayer() => case AvatarResponse.ObjectHeld(_, previousSlot) =>
sendResponse(GenericObjectActionMessage(guid, 9)) sendResponse(ObjectHeldMessage(guid, previousSlot, unk1=false))
case AvatarResponse.EnvironmentalDamage(_, _, _) => case AvatarResponse.ChangeFireState_Start(weaponGuid) if isNotSameTarget =>
sendResponse(ChangeFireStateMessage_Start(weaponGuid))
case AvatarResponse.ChangeFireState_Stop(weaponGuid) if isNotSameTarget =>
sendResponse(ChangeFireStateMessage_Stop(weaponGuid))
case AvatarResponse.LoadPlayer(pkt) if isNotSameTarget =>
sendResponse(pkt)
case AvatarResponse.EquipmentInHand(pkt) if isNotSameTarget =>
sendResponse(pkt)
case AvatarResponse.PlanetsideAttribute(attributeType, attributeValue) if isNotSameTarget =>
sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue))
case AvatarResponse.PlanetsideAttributeToAll(attributeType, attributeValue) =>
sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue))
case AvatarResponse.PlanetsideAttributeSelf(attributeType, attributeValue) if isSameTarget =>
sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue))
case AvatarResponse.GenericObjectAction(objectGuid, actionCode) if isNotSameTarget =>
sendResponse(GenericObjectActionMessage(objectGuid, actionCode))
case AvatarResponse.HitHint(sourceGuid) if player.isAlive =>
sendResponse(HitHint(sourceGuid, guid))
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg") sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg")
//TODO damage marker?
case AvatarResponse.DestroyDisplay(killer, victim, method, unk)
if killer.CharId == avatar.id && killer.Faction != victim.Faction =>
// TODO Temporary thing that should go somewhere else and use proper xp values
sendResponse(sessionData.destroyDisplayMessage(killer, victim, method, unk))
avatarActor ! AvatarActor.AwardBep((1000 * Config.app.game.bepRate).toLong, ExperienceType.Normal)
avatarActor ! AvatarActor.AwardCep((100 * Config.app.game.cepRate).toLong)
case AvatarResponse.Destroy(victim, killer, weapon, pos) => case AvatarResponse.Destroy(victim, killer, weapon, pos) =>
// guid = victim // killer = killer ;) // guid = victim // killer = killer
sendResponse(DestroyMessage(victim, killer, weapon, pos)) sendResponse(DestroyMessage(victim, killer, weapon, pos))
case AvatarResponse.DestroyDisplay(killer, victim, method, unk) => case AvatarResponse.DestroyDisplay(killer, victim, method, unk) =>
sendResponse(sessionData.destroyDisplayMessage(killer, victim, method, unk)) sendResponse(sessionData.destroyDisplayMessage(killer, victim, method, unk))
// TODO Temporary thing that should go somewhere else and use proper xp values
if (killer.CharId == avatar.id && killer.Faction != victim.Faction) { case AvatarResponse.TerminalOrderResult(terminalGuid, action, result)
avatarActor ! AvatarActor.AwardBep((1000 * Config.app.game.bepRate).toLong, ExperienceType.Normal) if result && (action == TransactionType.Buy || action == TransactionType.Loadout) =>
avatarActor ! AvatarActor.AwardCep((100 * Config.app.game.cepRate).toLong) sendResponse(ItemTransactionResultMessage(terminalGuid, action, result))
sessionData.terminals.lastTerminalOrderFulfillment = true
AvatarActor.savePlayerData(player)
sessionData.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
case AvatarResponse.ChangeExosuit(
target,
armor,
exosuit,
subtype,
_,
maxhand,
oldHolsters,
holsters,
oldInventory,
inventory,
drop,
delete
) if resolvedPlayerGuid == target =>
sendResponse(ArmorChangedMessage(target, exosuit, subtype))
sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor))
//happening to this player
//cleanup
sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=false))
(oldHolsters ++ oldInventory ++ delete).foreach {
case (_, dguid) => sendResponse(ObjectDeleteMessage(dguid, unk1=0))
}
//functionally delete
delete.foreach { case (obj, _) => TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) }
//redraw
if (maxhand) {
TaskWorkflow.execute(HoldNewEquipmentUp(player)(
Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)),
0
))
}
//draw free hand
player.FreeHand.Equipment.foreach { obj =>
val definition = obj.Definition
sendResponse(
ObjectCreateDetailedMessage(
definition.ObjectId,
obj.GUID,
ObjectCreateMessageParent(target, Player.FreeHandSlot),
definition.Packet.DetailedConstructorData(obj).get
)
)
}
//draw holsters and inventory
(holsters ++ inventory).foreach {
case InventoryItem(obj, index) =>
val definition = obj.Definition
sendResponse(
ObjectCreateDetailedMessage(
definition.ObjectId,
obj.GUID,
ObjectCreateMessageParent(target, index),
definition.Packet.DetailedConstructorData(obj).get
)
)
}
DropLeftovers(player)(drop)
case AvatarResponse.ChangeExosuit(target, armor, exosuit, subtype, slot, _, oldHolsters, holsters, _, _, _, delete) =>
sendResponse(ArmorChangedMessage(target, exosuit, subtype))
sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor))
//happening to some other player
sendResponse(ObjectHeldMessage(target, slot, unk1 = false))
//cleanup
(oldHolsters ++ delete).foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) }
//draw holsters
holsters.foreach {
case InventoryItem(obj, index) =>
val definition = obj.Definition
sendResponse(
ObjectCreateMessage(
definition.ObjectId,
obj.GUID,
ObjectCreateMessageParent(target, index),
definition.Packet.ConstructorData(obj).get
)
)
} }
case AvatarResponse.DropItem(pkt) => case AvatarResponse.ChangeLoadout(
if (tplayer_guid != guid) { target,
sendResponse(pkt) armor,
exosuit,
subtype,
_,
maxhand,
oldHolsters,
holsters,
oldInventory,
inventory,
drops
) if resolvedPlayerGuid == target =>
sendResponse(ArmorChangedMessage(target, exosuit, subtype))
sendResponse(PlanetsideAttributeMessage(target, attribute_type = 4, armor))
//happening to this player
sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=true))
//cleanup
(oldHolsters ++ oldInventory).foreach {
case (obj, objGuid) =>
sendResponse(ObjectDeleteMessage(objGuid, unk1=0))
TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj))
} }
//redraw
case AvatarResponse.EquipmentInHand(pkt) => if (maxhand) {
if (tplayer_guid != guid) { TaskWorkflow.execute(HoldNewEquipmentUp(player)(
sendResponse(pkt) Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)),
slot = 0
))
} }
sessionData.applyPurchaseTimersBeforePackingLoadout(player, player, holsters ++ inventory)
DropLeftovers(player)(drops)
case AvatarResponse.GenericObjectAction(object_guid, action_code) => case AvatarResponse.ChangeLoadout(target, armor, exosuit, subtype, slot, _, oldHolsters, _, _, _, _) =>
if (tplayer_guid != guid) { //redraw handled by callbacks
sendResponse(GenericObjectActionMessage(object_guid, action_code)) sendResponse(ArmorChangedMessage(target, exosuit, subtype))
} sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor))
//happening to some other player
sendResponse(ObjectHeldMessage(target, slot, unk1=false))
//cleanup
oldHolsters.foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) }
case AvatarResponse.HitHint(source_guid) => case AvatarResponse.UseKit(kguid, kObjId) =>
if (player.isAlive) { sendResponse(
sendResponse(HitHint(source_guid, guid)) UseItemMessage(
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg") resolvedPlayerGuid,
} kguid,
resolvedPlayerGuid,
unk2 = 4294967295L,
unk3 = false,
unk4 = Vector3.Zero,
unk5 = Vector3.Zero,
unk6 = 126,
unk7 = 0, //sequence time?
unk8 = 137,
kObjId
)
)
sendResponse(ObjectDeleteMessage(kguid, unk1=0))
case AvatarResponse.DropSpecialItem() => case AvatarResponse.KitNotUsed(_, "") =>
sessionData.dropSpecialSlotItem() sessionData.kitToBeUsed = None
case AvatarResponse.KitNotUsed(_, msg) =>
sessionData.kitToBeUsed = None
sendResponse(ChatMsg(ChatMessageType.UNK_225, msg))
case AvatarResponse.SendResponse(msg) =>
sendResponse(msg)
case AvatarResponse.SendResponseTargeted(targetGuid, msg) if resolvedPlayerGuid == targetGuid =>
sendResponse(msg)
/* common messages (maybe once every respawn) */
case AvatarResponse.Reload(itemGuid) if isNotSameTarget =>
sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0))
case AvatarResponse.Killed(mount) => case AvatarResponse.Killed(mount) =>
val cause = (player.LastDamage match { //log and chat messages
case Some(reason) => (Some(reason), reason.adversarial) val cause = player.LastDamage.flatMap { damage =>
case None => (None, None) damage.interaction.cause match {
}) match { case cause: ExplodingEntityReason if cause.entity.isInstanceOf[VehicleSpawnPad] =>
case (_, Some(adversarial)) => adversarial.attacker.Name //also, @SVCP_Killed_TooCloseToPadOnCreate^n~ or "... within n meters of pad ..."
case (Some(reason), None) => s"a ${reason.interaction.cause.getClass.getSimpleName}" sendResponse(ChatMsg(ChatMessageType.UNK_227, "@SVCP_Killed_OnPadOnCreate"))
case _ => s"an unfortunate circumstance (probably ${player.Sex.pronounObject} own fault)" case _ => ()
}
log.info(s"${player.Name} has died, killed by $cause")
val respawnTimer = 300.seconds
//drop free hand item
player.FreeHand.Equipment match {
case Some(item) =>
DropEquipmentFromInventory(player)(item)
case None => ;
}
sessionData.dropSpecialSlotItem()
sessionData.toggleMaxSpecialState(enable = false)
if (player.LastDamage match {
case Some(damage) => damage.interaction.cause match {
case cause: ExplodingEntityReason => cause.entity.isInstanceOf[VehicleSpawnPad]
case _ => false
} }
case None => false damage match {
}) { case damage if damage.adversarial.nonEmpty => Some(damage.adversarial.get.attacker.Name)
//also, @SVCP_Killed_TooCloseToPadOnCreate^n~ or "... within n meters of pad ..." case damage => Some(s"a ${damage.interaction.cause.getClass.getSimpleName}")
sendResponse(ChatMsg(ChatMessageType.UNK_227, wideContents=false, "", "@SVCP_Killed_OnPadOnCreate", None)) }
} }.getOrElse { s"an unfortunate circumstance (probably ${player.Sex.pronounObject} own fault)" }
sessionData.keepAliveFunc = sessionData.zoning.NormalKeepAlive log.info(s"${player.Name} has died, killed by $cause")
sessionData.zoning.zoningStatus = Zoning.Status.None
sessionData.zoning.spawn.deadState = DeadState.Dead
continent.GUID(mount) match {
case Some(obj: Vehicle) =>
sessionData.vehicles.ConditionalDriverVehicleControl(obj)
sessionData.vehicles.serverVehicleControlVelocity = None
sessionData.unaccessContainer(obj)
case _ => ;
}
sessionData.playerActionsToCancel()
sessionData.terminals.CancelAllProximityUnits()
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel")
if (sessionData.shooting.shotsWhileDead > 0) { if (sessionData.shooting.shotsWhileDead > 0) {
log.warn( log.warn(
s"KillPlayer/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 ${sessionData.shooting.shotsWhileDead} rounds while character was dead on server"
) )
sessionData.shooting.shotsWhileDead = 0 sessionData.shooting.shotsWhileDead = 0
} }
sessionData.zoning.CancelZoningProcessWithDescriptiveReason(msg = "cancel")
sessionData.renewCharSavedTimer(fixedLen = 1800L, varLen = 0L)
//player state changes
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
continent.GUID(mount).collect { case obj: Vehicle =>
sessionData.vehicles.ConditionalDriverVehicleControl(obj)
sessionData.vehicles.serverVehicleControlVelocity = None
sessionData.unaccessContainer(obj)
}
sessionData.playerActionsToCancel()
sessionData.terminals.CancelAllProximityUnits()
AvatarActor.savePlayerLocation(player)
//respawn
val respawnTimer = 300.seconds
sessionData.zoning.spawn.reviveTimer.cancel() sessionData.zoning.spawn.reviveTimer.cancel()
if (player.death_by == 0) { if (player.death_by == 0) {
sessionData.zoning.spawn.reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer) { sessionData.zoning.spawn.reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer) {
@ -208,340 +387,122 @@ class SessionAvatarHandlers(
} else { } else {
sessionData.zoning.spawn.HandleReleaseAvatar(player, continent) sessionData.zoning.spawn.HandleReleaseAvatar(player, continent)
} }
AvatarActor.savePlayerLocation(player)
sessionData.renewCharSavedTimer(fixedLen = 1800L, varLen = 0L)
case AvatarResponse.LoadPlayer(pkt) => case AvatarResponse.Release(tplayer) if isNotSameTarget =>
if (tplayer_guid != guid) { sessionData.zoning.spawn.DepictPlayerAsCorpse(tplayer)
sendResponse(pkt)
}
case AvatarResponse.LoadProjectile(pkt) => case AvatarResponse.Revive(revivalTargetGuid) if resolvedPlayerGuid == revivalTargetGuid =>
if (tplayer_guid != guid) { log.info(s"No time for rest, ${player.Name}. Back on your feet!")
sendResponse(pkt) sessionData.zoning.spawn.reviveTimer.cancel()
} sessionData.zoning.spawn.deadState = DeadState.Alive
player.Revive
val health = player.Health
sendResponse(PlanetsideAttributeMessage(revivalTargetGuid, attribute_type=0, health))
sendResponse(AvatarDeadStateMessage(DeadState.Alive, timer_max=0, timer=0, player.Position, player.Faction, unk5=true))
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.PlanetsideAttributeToAll(revivalTargetGuid, attribute_type=0, health)
)
case AvatarResponse.ObjectDelete(item_guid, unk) => /* uncommon messages (utility, or once in a while) */
if (tplayer_guid != guid) { case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data)
sendResponse(ObjectDeleteMessage(item_guid, unk)) if isNotSameTarget =>
} sendResponse(ObjectDetachMessage(weapon_guid, previous_guid, Vector3.Zero, 0))
sendResponse(
ObjectCreateMessage(
ammo_id,
ammo_guid,
ObjectCreateMessageParent(weapon_guid, weapon_slot),
ammo_data
)
)
sendResponse(ChangeAmmoMessage(weapon_guid, 1))
case AvatarResponse.ObjectHeld(slot, previousSlot) => case AvatarResponse.ChangeFireMode(itemGuid, mode) if isNotSameTarget =>
if (tplayer_guid == guid) { sendResponse(ChangeFireModeMessage(itemGuid, mode))
if (slot > -1) {
sendResponse(ObjectHeldMessage(guid, slot, unk1 = true)) case AvatarResponse.ConcealPlayer() =>
//Stop using proximity terminals if player unholsters a weapon sendResponse(GenericObjectActionMessage(guid, code=9))
if (player.VisibleSlots.contains(slot)) {
continent.GUID(sessionData.terminals.usingMedicalTerminal) match { case AvatarResponse.EnvironmentalDamage(_, _, _) =>
case Some(term: Terminal with ProximityUnit) => //TODO damage marker?
sessionData.terminals.StopUsingProximityUnit(term) sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg")
case _ => ;
} case AvatarResponse.DropItem(pkt) if isNotSameTarget =>
} sendResponse(pkt)
}
} else { case AvatarResponse.ObjectDelete(itemGuid, unk) if isNotSameTarget =>
sendResponse(ObjectHeldMessage(guid, previousSlot, unk1 = false)) sendResponse(ObjectDeleteMessage(itemGuid, unk))
}
/* rare messages */
case AvatarResponse.SetEmpire(objectGuid, faction) if isNotSameTarget =>
sendResponse(SetEmpireMessage(objectGuid, faction))
case AvatarResponse.DropSpecialItem() =>
sessionData.dropSpecialSlotItem()
case AvatarResponse.OxygenState(player, vehicle) => case AvatarResponse.OxygenState(player, vehicle) =>
sendResponse( sendResponse(OxygenStateMessage(
OxygenStateMessage( DrowningTarget(player.guid, player.progress, player.state),
DrowningTarget(player.guid, player.progress, player.state), vehicle.flatMap { vinfo => Some(DrowningTarget(vinfo.guid, vinfo.progress, vinfo.state)) }
vehicle match { ))
case Some(vinfo) => Some(DrowningTarget(vinfo.guid, vinfo.progress, vinfo.state))
case None => None
}
)
)
case AvatarResponse.PlanetsideAttribute(attribute_type, attribute_value) => case AvatarResponse.LoadProjectile(pkt) if isNotSameTarget =>
if (tplayer_guid != guid) { sendResponse(pkt)
sendResponse(PlanetsideAttributeMessage(guid, attribute_type, attribute_value))
}
case AvatarResponse.PlanetsideAttributeToAll(attribute_type, attribute_value) => case AvatarResponse.ProjectileState(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid) if isNotSameTarget =>
sendResponse(PlanetsideAttributeMessage(guid, attribute_type, attribute_value)) sendResponse(ProjectileStateMessage(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid))
case AvatarResponse.PlanetsideAttributeSelf(attribute_type, attribute_value) => case AvatarResponse.ProjectileExplodes(projectileGuid, projectile) =>
if (tplayer_guid == guid) {
sendResponse(PlanetsideAttributeMessage(guid, attribute_type, attribute_value))
}
case AvatarResponse.PlayerState(
pos,
vel,
yaw,
pitch,
yaw_upper,
_,
is_crouching,
is_jumping,
jump_thrust,
is_cloaking,
spectating,
_
) =>
if (tplayer_guid != guid) {
val now = System.currentTimeMillis()
val (location, time, distanceSq): (Vector3, Long, Float) = if (spectating) {
val r = new scala.util.Random
val r1 = 2 + r.nextInt(30).toFloat
val r2 = 2 + r.nextInt(4000).toFloat
(Vector3(r2, r2, r1), 0L, 0f)
} else {
val before = lastSeenStreamMessage(guid.guid)
val dist = Vector3.DistanceSquared(player.Position, pos)
(pos, now - before, dist)
}
if (distanceSq < 302500 || time > 5000) { // Render distance seems to be approx 525m. Reduce update rate at ~550m to be safe
sendResponse(
PlayerStateMessage(
guid,
location,
vel,
yaw,
pitch,
yaw_upper,
timestamp = 0,
is_crouching,
is_jumping,
jump_thrust,
is_cloaking
)
)
lastSeenStreamMessage(guid.guid) = now
}
}
case AvatarResponse.ProjectileExplodes(projectile_guid, projectile) =>
sendResponse( sendResponse(
ProjectileStateMessage( ProjectileStateMessage(
projectile_guid, projectileGuid,
projectile.Position, projectile.Position,
Vector3.Zero, shot_vel = Vector3.Zero,
projectile.Orientation, projectile.Orientation,
0, sequence_num=0,
end=true, end=true,
PlanetSideGUID(0) hit_target_guid=PlanetSideGUID(0)
) )
) )
sendResponse(ObjectDeleteMessage(projectile_guid, 2)) sendResponse(ObjectDeleteMessage(projectileGuid, unk1=2))
case AvatarResponse.ProjectileAutoLockAwareness(mode) => case AvatarResponse.ProjectileAutoLockAwareness(mode) =>
sendResponse(GenericActionMessage(mode)) sendResponse(GenericActionMessage(mode))
case AvatarResponse.ProjectileState(projectile_guid, shot_pos, shot_vel, shot_orient, seq, end, target_guid) => case AvatarResponse.PutDownFDU(target) if isNotSameTarget =>
if (tplayer_guid != guid) { sendResponse(GenericObjectActionMessage(target, code=53))
sendResponse(ProjectileStateMessage(projectile_guid, shot_pos, shot_vel, shot_orient, seq, end, target_guid))
}
case AvatarResponse.PutDownFDU(target) => case AvatarResponse.StowEquipment(target, slot, item) if isNotSameTarget =>
if (tplayer_guid != guid) { val definition = item.Definition
sendResponse(GenericObjectActionMessage(target, 53))
}
case AvatarResponse.Release(tplayer) =>
if (tplayer_guid != guid) {
sessionData.zoning.spawn.DepictPlayerAsCorpse(tplayer)
}
case AvatarResponse.Reload(item_guid) =>
if (tplayer_guid != guid) {
sendResponse(ReloadMessage(item_guid, 1, 0))
}
case AvatarResponse.SetEmpire(object_guid, faction) =>
if (tplayer_guid != guid) {
sendResponse(SetEmpireMessage(object_guid, faction))
}
case AvatarResponse.StowEquipment(target, slot, item) =>
if (tplayer_guid != guid) {
val definition = item.Definition
sendResponse(
ObjectCreateDetailedMessage(
definition.ObjectId,
item.GUID,
ObjectCreateMessageParent(target, slot),
definition.Packet.DetailedConstructorData(item).get
)
)
}
case AvatarResponse.WeaponDryFire(weapon_guid) =>
if (tplayer_guid != guid) {
continent.GUID(weapon_guid) match {
case Some(tool: Tool) =>
// check that the magazine is still empty before sending WeaponDryFireMessage
// if it has been reloaded since then, other clients not see it firing
if (tool.Magazine == 0) {
sendResponse(WeaponDryFireMessage(weapon_guid))
}
case Some(_) =>
sendResponse(WeaponDryFireMessage(weapon_guid))
case None => ;
}
}
case AvatarResponse.TerminalOrderResult(terminal_guid, action, result) =>
sendResponse(ItemTransactionResultMessage(terminal_guid, action, result))
sessionData.terminals.lastTerminalOrderFulfillment = true
if (result &&
(action == TransactionType.Buy || action == TransactionType.Loadout)) {
AvatarActor.savePlayerData(player)
sessionData.renewCharSavedTimer(
Config.app.game.savedMsg.interruptedByAction.fixed,
Config.app.game.savedMsg.interruptedByAction.variable
)
}
case AvatarResponse.ChangeExosuit(
target,
armor,
exosuit,
subtype,
slot,
maxhand,
old_holsters,
holsters,
old_inventory,
inventory,
drop,
delete
) =>
sendResponse(ArmorChangedMessage(target, exosuit, subtype))
sendResponse(PlanetsideAttributeMessage(target, 4, armor))
if (tplayer_guid == target) {
//happening to this player
//cleanup
sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=false))
(old_holsters ++ old_inventory ++ delete).foreach {
case (_, dguid) => sendResponse(ObjectDeleteMessage(dguid, 0))
}
//functionally delete
delete.foreach { case (obj, _) => TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) }
//redraw
if (maxhand) {
TaskWorkflow.execute(HoldNewEquipmentUp(player)(
Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)),
0
))
}
//draw free hand
player.FreeHand.Equipment match {
case Some(obj) =>
val definition = obj.Definition
sendResponse(
ObjectCreateDetailedMessage(
definition.ObjectId,
obj.GUID,
ObjectCreateMessageParent(target, Player.FreeHandSlot),
definition.Packet.DetailedConstructorData(obj).get
)
)
case None => ;
}
//draw holsters and inventory
(holsters ++ inventory).foreach {
case InventoryItem(obj, index) =>
val definition = obj.Definition
sendResponse(
ObjectCreateDetailedMessage(
definition.ObjectId,
obj.GUID,
ObjectCreateMessageParent(target, index),
definition.Packet.DetailedConstructorData(obj).get
)
)
}
DropLeftovers(player)(drop)
} else {
//happening to some other player
sendResponse(ObjectHeldMessage(target, slot, unk1=false))
//cleanup
(old_holsters ++ delete).foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, 0)) }
//draw holsters
holsters.foreach {
case InventoryItem(obj, index) =>
val definition = obj.Definition
sendResponse(
ObjectCreateMessage(
definition.ObjectId,
obj.GUID,
ObjectCreateMessageParent(target, index),
definition.Packet.ConstructorData(obj).get
)
)
}
}
case AvatarResponse.ChangeLoadout(
target,
armor,
exosuit,
subtype,
slot,
maxhand,
old_holsters,
holsters,
old_inventory,
inventory,
drops
) =>
sendResponse(ArmorChangedMessage(target, exosuit, subtype))
sendResponse(PlanetsideAttributeMessage(target, 4, armor))
if (tplayer_guid == target) {
//happening to this player
sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=true))
//cleanup
(old_holsters ++ old_inventory).foreach {
case (obj, objGuid) =>
sendResponse(ObjectDeleteMessage(objGuid, 0))
TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj))
}
//redraw
if (maxhand) {
TaskWorkflow.execute(HoldNewEquipmentUp(player)(
Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)),
slot = 0
))
}
sessionData.applyPurchaseTimersBeforePackingLoadout(player, player, holsters ++ inventory)
DropLeftovers(player)(drops)
} else {
//happening to some other player
sendResponse(ObjectHeldMessage(target, slot, unk1=false))
//cleanup
old_holsters.foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, 0)) }
//redraw handled by callback
}
case AvatarResponse.UseKit(kguid, kObjId) =>
sendResponse( sendResponse(
UseItemMessage( ObjectCreateDetailedMessage(
tplayer_guid, definition.ObjectId,
kguid, item.GUID,
tplayer_guid, ObjectCreateMessageParent(target, slot),
4294967295L, definition.Packet.DetailedConstructorData(item).get
unk3=false,
Vector3.Zero,
Vector3.Zero,
126,
0, //sequence time?
137,
kObjId
) )
) )
sendResponse(ObjectDeleteMessage(kguid, 0))
case AvatarResponse.KitNotUsed(_, "") => case AvatarResponse.WeaponDryFire(weaponGuid) if isNotSameTarget =>
sessionData.kitToBeUsed = None 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 not see it firing
sendResponse(WeaponDryFireMessage(weaponGuid))
case _ =>
sendResponse(WeaponDryFireMessage(weaponGuid))
}
case AvatarResponse.KitNotUsed(_, msg) => case _ => ()
sessionData.kitToBeUsed = None
sendResponse(ChatMsg(ChatMessageType.UNK_225, wideContents=false, "", msg, None))
case _ => ;
} }
} }
} }
object SessionAvatarHandlers {
private[support] case class LastUpstream(msg: Option[AvatarResponse.PlayerState], time: Long)
private[support] def blankUpstreamMessages(n: Int): Array[LastUpstream] = {
Array.fill[SessionAvatarHandlers.LastUpstream](n)(elem = LastUpstream(None, 0L))
}
}

View file

@ -257,10 +257,7 @@ class SessionData(
} }
case None => () case None => ()
} }
val wepInHand: Boolean = player.Slot(player.DrawnSlot).Equipment match { val eagleEye: Boolean = canSeeReallyFar
case Some(item) => item.Definition == GlobalDefinitions.bolt_driver
case _ => false
}
continent.AvatarEvents ! AvatarServiceMessage( continent.AvatarEvents ! AvatarServiceMessage(
continent.id, continent.id,
AvatarAction.PlayerState( AvatarAction.PlayerState(
@ -276,7 +273,7 @@ class SessionData(
jumpThrust, jumpThrust,
isCloaking, isCloaking,
player.spectator, player.spectator,
wepInHand eagleEye
) )
) )
squad.updateSquad() squad.updateSquad()
@ -2910,6 +2907,23 @@ class SessionData(
heightLast = zHeight heightLast = zHeight
} }
def canSeeReallyFar: Boolean = {
findEquipment().exists {
case weapon: Tool
if weapon.Size == EquipmentSize.Rifle &&
(weapon.Projectile ne GlobalDefinitions.no_projectile) &&
player.Crouching &&
player.avatar
.implants
.exists { p =>
p.collect { implant => implant.definition.implantType == ImplantType.RangeMagnifier && implant.initialized }.nonEmpty
} =>
true
case item =>
item.Definition == GlobalDefinitions.bolt_driver || item.Definition == GlobalDefinitions.heavy_sniper
}
}
def displayCharSavedMsgThenRenewTimer(fixedLen: Long, varLen: Long): Unit = { def displayCharSavedMsgThenRenewTimer(fixedLen: Long, varLen: Long): Unit = {
charSaved() charSaved()
renewCharSavedTimer(fixedLen, varLen) renewCharSavedTimer(fixedLen, varLen)

View file

@ -113,3 +113,111 @@ class SessionGalaxyHandlers(
} }
} }
} }
/*package net.psforever.actors.session.support
import akka.actor.{ActorContext, ActorRef, typed}
import scala.concurrent.duration._
//
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.services.galaxy.GalaxyResponse
import net.psforever.types.{MemberAction, PlanetSideEmpire}
class SessionGalaxyHandlers(
val sessionData: SessionData,
avatarActor: typed.ActorRef[AvatarActor.Command],
galaxyService: ActorRef,
implicit val context: ActorContext
) extends CommonSessionInterfacingFunctionality {
def handle(reply: GalaxyResponse.Response): Unit = {
reply match {
case GalaxyResponse.HotSpotUpdate(zoneIndex, priority, hotSpotInfo) =>
sendResponse(
HotSpotUpdateMessage(
zoneIndex,
priority,
hotSpotInfo.map { spot => PacketHotSpotInfo(spot.DisplayLocation.x, spot.DisplayLocation.y, 40) }
)
)
case GalaxyResponse.MapUpdate(msg) =>
sendResponse(msg)
case GalaxyResponse.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 GalaxyResponse.FlagMapUpdate(msg) =>
sendResponse(msg)
case GalaxyResponse.TransferPassenger(tempChannel, vehicle, _, manifest) =>
val playerName = player.Name
log.debug(s"TransferPassenger: $playerName received the summons to transfer to ${vehicle.Zone.id} ...")
manifest.passengers
.find { _.name.equals(playerName) }
.collect {
case entry if vehicle.Seats(entry.mount).occupant.isEmpty =>
player.VehicleSeated = None
vehicle.Seats(entry.mount).mount(player)
player.VehicleSeated = vehicle.GUID
Some(vehicle)
case entry if vehicle.Seats(entry.mount).occupant.contains(player) =>
Some(vehicle)
case entry =>
log.warn(
s"TransferPassenger: $playerName tried to mount seat ${entry.mount} during summoning, but it was already occupied, and ${player.Sex.pronounSubject} was rebuked"
)
None
}.orElse {
manifest.cargo.find { _.name.equals(playerName) }.flatMap { entry =>
vehicle.CargoHolds(entry.mount).occupant.collect {
case cargo if cargo.Seats(0).occupants.exists(_.Name.equals(playerName)) => cargo
}
}
} match {
case Some(v: Vehicle) =>
galaxyService ! Service.Leave(Some(tempChannel)) //temporary vehicle-specific channel (see above)
sessionData.zoning.spawn.deadState = DeadState.Release
sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, player.Faction, unk5=true))
sessionData.zoning.interstellarFerry = Some(v) //on the other continent and registered to that continent's GUID system
sessionData.zoning.spawn.LoadZonePhysicalSpawnPoint(v.Continent, v.Position, v.Orientation, 1 seconds, None)
case _ =>
sessionData.zoning.interstellarFerry match {
case None =>
galaxyService ! Service.Leave(Some(tempChannel)) //no longer being transferred between zones
sessionData.zoning.interstellarFerryTopLevelGUID = None
case Some(_) => ;
//wait patiently
}
}
case GalaxyResponse.LockedZoneUpdate(zone, time) =>
sendResponse(ZoneInfoMessage(zone.Number, empire_status=false, lock_time=time))
case GalaxyResponse.UnlockedZoneUpdate(zone) =>
sendResponse(ZoneInfoMessage(zone.Number, empire_status=true, lock_time=0L))
val popBO = 0
val popTR = zone.Players.count(_.faction == PlanetSideEmpire.TR)
val popNC = zone.Players.count(_.faction == PlanetSideEmpire.NC)
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) } =>
avatarActor ! AvatarActor.MemberListRequest(MemberAction.UpdateFriend, name)
case GalaxyResponse.SendResponse(msg) =>
sendResponse(msg)
case _ => ()
}
}
}*/

View file

@ -7,6 +7,7 @@ import net.psforever.objects.vehicles.MountableWeapons
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
import net.psforever.packet.game._ import net.psforever.packet.game._
import net.psforever.services.Service
import net.psforever.services.local.LocalResponse import net.psforever.services.local.LocalResponse
import net.psforever.types.{ChatMessageType, PlanetSideGUID, Vector3} import net.psforever.types.{ChatMessageType, PlanetSideGUID, Vector3}
@ -21,99 +22,108 @@ class SessionLocalHandlers(
* @param reply na * @param reply na
*/ */
def handle(toChannel: String, guid: PlanetSideGUID, reply: LocalResponse.Response): Unit = { def handle(toChannel: String, guid: PlanetSideGUID, reply: LocalResponse.Response): Unit = {
val tplayer_guid = if (player.HasGUID) { player.GUID } val resolvedPlayerGuid = if (player.HasGUID) {
else { PlanetSideGUID(0) } player.GUID
} else {
Service.defaultPlayerGUID
}
val isNotSameTarget = resolvedPlayerGuid != guid
reply match { reply match {
case LocalResponse.DeployableMapIcon(behavior, deployInfo) => case LocalResponse.DeployableMapIcon(behavior, deployInfo) if isNotSameTarget =>
if (tplayer_guid != guid) { sendResponse(DeployableObjectsInfoMessage(behavior, deployInfo))
sendResponse(DeployableObjectsInfoMessage(behavior, deployInfo))
}
case LocalResponse.DeployableUIFor(item) => case LocalResponse.DeployableUIFor(item) =>
sessionData.updateDeployableUIElements(avatar.deployables.UpdateUIElement(item)) sessionData.updateDeployableUIElements(avatar.deployables.UpdateUIElement(item))
case LocalResponse.Detonate(dguid, _: BoomerDeployable) => case LocalResponse.Detonate(dguid, _: BoomerDeployable) =>
sendResponse(TriggerEffectMessage(dguid, "detonate_boomer")) sendResponse(TriggerEffectMessage(dguid, "detonate_boomer"))
sendResponse(PlanetsideAttributeMessage(dguid, 29, 1)) sendResponse(PlanetsideAttributeMessage(dguid, attribute_type=29, attribute_value=1))
sendResponse(ObjectDeleteMessage(dguid, 0)) sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalResponse.Detonate(dguid, _: ExplosiveDeployable) => case LocalResponse.Detonate(dguid, _: ExplosiveDeployable) =>
sendResponse(GenericObjectActionMessage(dguid, 19)) sendResponse(GenericObjectActionMessage(dguid, code=19))
sendResponse(PlanetsideAttributeMessage(dguid, 29, 1)) sendResponse(PlanetsideAttributeMessage(dguid, attribute_type=29, attribute_value=1))
sendResponse(ObjectDeleteMessage(dguid, 0)) sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalResponse.Detonate(_, obj) => case LocalResponse.Detonate(_, obj) =>
log.warn(s"LocalResponse.Detonate: ${obj.Definition.Name} not configured to explode correctly") log.warn(s"LocalResponse.Detonate: ${obj.Definition.Name} not configured to explode correctly")
case LocalResponse.DoorOpens(door_guid) => case LocalResponse.DoorOpens(doorGuid) if isNotSameTarget =>
if (tplayer_guid != guid) { sendResponse(GenericObjectStateMsg(doorGuid, state=16))
sendResponse(GenericObjectStateMsg(door_guid, 16))
}
case LocalResponse.DoorCloses(door_guid) => //door closes for everyone case LocalResponse.DoorCloses(doorGuid) => //door closes for everyone
sendResponse(GenericObjectStateMsg(door_guid, 17)) sendResponse(GenericObjectStateMsg(doorGuid, state=17))
case LocalResponse.EliminateDeployable(obj: TurretDeployable, dguid, _, _) if obj.Destroyed =>
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalResponse.EliminateDeployable(obj: TurretDeployable, dguid, pos, _) => case LocalResponse.EliminateDeployable(obj: TurretDeployable, dguid, pos, _) =>
if (obj.Destroyed) { obj.Destroyed = true
sendResponse(ObjectDeleteMessage(dguid, 0)) DeconstructDeployable(
} else { obj,
obj.Destroyed = true dguid,
DeconstructDeployable( pos,
obj, obj.Orientation,
dguid, deletionType= if (obj.MountPoints.isEmpty) { 2 } else { 1 }
pos, )
obj.Orientation,
if (obj.MountPoints.isEmpty) 2 else 1 case LocalResponse.EliminateDeployable(obj: ExplosiveDeployable, dguid, _, _)
) if obj.Destroyed || obj.Jammed || obj.Health == 0 =>
} sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalResponse.EliminateDeployable(obj: ExplosiveDeployable, dguid, pos, effect) => case LocalResponse.EliminateDeployable(obj: ExplosiveDeployable, dguid, pos, effect) =>
if (obj.Destroyed || obj.Jammed || obj.Health == 0) { obj.Destroyed = true
sendResponse(ObjectDeleteMessage(dguid, 0)) DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
} else {
obj.Destroyed = true case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Active && obj.Destroyed =>
DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect) //if active, deactivate
} obj.Active = false
sendResponse(GenericObjectActionMessage(dguid, code=29))
sendResponse(GenericObjectActionMessage(dguid, code=30))
//standard deployable elimination behavior
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) if obj.Active =>
//if active, deactivate
obj.Active = false
sendResponse(GenericObjectActionMessage(dguid, code=29))
sendResponse(GenericObjectActionMessage(dguid, code=30))
//standard deployable elimination behavior
obj.Destroyed = true
DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2)
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Destroyed =>
//standard deployable elimination behavior
sendResponse(ObjectDeleteMessage(dguid, unk1=0))
case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) => case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) =>
//if active, deactivate
if (obj.Active) {
obj.Active = false
sendResponse(GenericObjectActionMessage(dguid, 29))
sendResponse(GenericObjectActionMessage(dguid, 30))
}
//standard deployable elimination behavior //standard deployable elimination behavior
if (obj.Destroyed) { obj.Destroyed = true
sendResponse(ObjectDeleteMessage(dguid, 0)) DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2)
} else {
obj.Destroyed = true case LocalResponse.EliminateDeployable(obj, dguid, _, _) if obj.Destroyed =>
DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType = 2) sendResponse(ObjectDeleteMessage(dguid, unk1=0))
}
case LocalResponse.EliminateDeployable(obj, dguid, pos, effect) => case LocalResponse.EliminateDeployable(obj, dguid, pos, effect) =>
if (obj.Destroyed) { obj.Destroyed = true
sendResponse(ObjectDeleteMessage(dguid, 0)) DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
} else {
obj.Destroyed = true
DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
}
case LocalResponse.SendHackMessageHackCleared(target_guid, unk1, unk2) => case LocalResponse.SendHackMessageHackCleared(targetGuid, unk1, unk2) =>
sendResponse(HackMessage(0, target_guid, guid, 0, unk1, HackState.HackCleared, unk2)) sendResponse(HackMessage(unk1=0, targetGuid, guid, progress=0, unk1, HackState.HackCleared, unk2))
case LocalResponse.HackObject(target_guid, unk1, unk2) => case LocalResponse.HackObject(targetGuid, unk1, unk2) =>
HackObject(target_guid, unk1, unk2) HackObject(targetGuid, unk1, unk2)
case LocalResponse.SendPlanetsideAttributeMessage(target_guid, attribute_number, attribute_value) => case LocalResponse.PlanetsideAttribute(targetGuid, attributeType, attributeValue) =>
SendPlanetsideAttributeMessage(target_guid, attribute_number, attribute_value) SendPlanetsideAttributeMessage(targetGuid, attributeType, attributeValue)
case LocalResponse.SendGenericObjectActionMessage(target_guid, action_number) => case LocalResponse.GenericObjectAction(targetGuid, actionNumber) =>
sendResponse(GenericObjectActionMessage(target_guid, action_number)) sendResponse(GenericObjectActionMessage(targetGuid, actionNumber))
case LocalResponse.SendGenericActionMessage(action_number) => case LocalResponse.GenericActionMessage(actionNumber) =>
sendResponse(GenericActionMessage(action_number)) sendResponse(GenericActionMessage(actionNumber))
case LocalResponse.SendChatMsg(msg) => case LocalResponse.ChatMessage(msg) =>
sendResponse(msg) sendResponse(msg)
case LocalResponse.SendPacket(packet) => case LocalResponse.SendPacket(packet) =>
@ -121,49 +131,43 @@ class SessionLocalHandlers(
case LocalResponse.LluSpawned(llu) => case LocalResponse.LluSpawned(llu) =>
// Create LLU on client // Create LLU on client
sendResponse( sendResponse(ObjectCreateMessage(
ObjectCreateMessage( llu.Definition.ObjectId,
llu.Definition.ObjectId, llu.GUID,
llu.GUID, llu.Definition.Packet.ConstructorData(llu).get
llu.Definition.Packet.ConstructorData(llu).get ))
) sendResponse(TriggerSoundMessage(TriggeredSound.LLUMaterialize, llu.Position, unk=20, volume=0.8000001f))
)
sendResponse(TriggerSoundMessage(TriggeredSound.LLUMaterialize, llu.Position, unk = 20, 0.8000001f))
case LocalResponse.LluDespawned(lluGuid, position) => case LocalResponse.LluDespawned(lluGuid, position) =>
sendResponse(TriggerSoundMessage(TriggeredSound.LLUDeconstruct, position, unk = 20, 0.8000001f)) sendResponse(TriggerSoundMessage(TriggeredSound.LLUDeconstruct, position, unk=20, volume=0.8000001f))
sendResponse(ObjectDeleteMessage(lluGuid, 0)) sendResponse(ObjectDeleteMessage(lluGuid, unk1=0))
// If the player was holding the LLU, remove it from their tracked special item slot // If the player was holding the LLU, remove it from their tracked special item slot
sessionData.specialItemSlotGuid match { sessionData.specialItemSlotGuid.collect { case guid if guid == lluGuid =>
case Some(guid) if guid == lluGuid => sessionData.specialItemSlotGuid = None
sessionData.specialItemSlotGuid = None player.Carrying = None
player.Carrying = None
case _ => ()
} }
case LocalResponse.ObjectDelete(object_guid, unk) => case LocalResponse.ObjectDelete(objectGuid, unk) if isNotSameTarget =>
if (tplayer_guid != guid) { sendResponse(ObjectDeleteMessage(objectGuid, unk))
sendResponse(ObjectDeleteMessage(object_guid, unk))
}
case LocalResponse.ProximityTerminalEffect(object_guid, true) => case LocalResponse.ProximityTerminalEffect(object_guid, true) =>
sendResponse(ProximityTerminalUseMessage(PlanetSideGUID(0), object_guid, unk=true)) sendResponse(ProximityTerminalUseMessage(Service.defaultPlayerGUID, object_guid, unk=true))
case LocalResponse.ProximityTerminalEffect(object_guid, false) => case LocalResponse.ProximityTerminalEffect(objectGuid, false) =>
sendResponse(ProximityTerminalUseMessage(PlanetSideGUID(0), object_guid, unk=false)) sendResponse(ProximityTerminalUseMessage(Service.defaultPlayerGUID, objectGuid, unk=false))
sessionData.terminals.ForgetAllProximityTerminals(object_guid) sessionData.terminals.ForgetAllProximityTerminals(objectGuid)
case LocalResponse.RouterTelepadMessage(msg) => case LocalResponse.RouterTelepadMessage(msg) =>
sendResponse(ChatMsg(ChatMessageType.UNK_229, wideContents=false, "", msg, None)) sendResponse(ChatMsg(ChatMessageType.UNK_229, wideContents=false, recipient="", msg, note=None))
case LocalResponse.RouterTelepadTransport(passenger_guid, src_guid, dest_guid) => case LocalResponse.RouterTelepadTransport(passengerGuid, srcGuid, destGuid) =>
sessionData.useRouterTelepadEffect(passenger_guid, src_guid, dest_guid) sessionData.useRouterTelepadEffect(passengerGuid, srcGuid, destGuid)
case LocalResponse.SendResponse(msg) => case LocalResponse.SendResponse(msg) =>
sendResponse(msg) sendResponse(msg)
case LocalResponse.SetEmpire(object_guid, empire) => case LocalResponse.SetEmpire(objectGuid, empire) =>
sendResponse(SetEmpireMessage(object_guid, empire)) sendResponse(SetEmpireMessage(objectGuid, empire))
case LocalResponse.ShuttleEvent(ev) => case LocalResponse.ShuttleEvent(ev) =>
val msg = OrbitalShuttleTimeMsg( val msg = OrbitalShuttleTimeMsg(
@ -172,7 +176,7 @@ class SessionLocalHandlers(
ev.t1, ev.t1,
ev.t2, ev.t2,
ev.t3, ev.t3,
ev.pairs.map { case ((a, b), c) => PadAndShuttlePair(a, b, c) } pairs=ev.pairs.map { case ((a, b), c) => PadAndShuttlePair(a, b, c) }
) )
sendResponse(msg) sendResponse(msg)
@ -183,42 +187,32 @@ class SessionLocalHandlers(
sendResponse(ObjectDetachMessage(pguid, sguid, pos, orient)) sendResponse(ObjectDetachMessage(pguid, sguid, pos, orient))
case LocalResponse.ShuttleState(sguid, pos, orient, state) => case LocalResponse.ShuttleState(sguid, pos, orient, state) =>
sendResponse(VehicleStateMessage(sguid, 0, pos, orient, None, Some(state), 0, 0, 15, is_decelerating=false, is_cloaked=false)) 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, system_plan) => case LocalResponse.ToggleTeleportSystem(router, systemPlan) =>
sessionData.toggleTeleportSystem(router, system_plan) sessionData.toggleTeleportSystem(router, systemPlan)
case LocalResponse.TriggerEffect(target_guid, effect, effectInfo, triggerLocation) => case LocalResponse.TriggerEffect(targetGuid, effect, effectInfo, triggerLocation) =>
sendResponse(TriggerEffectMessage(target_guid, effect, effectInfo, triggerLocation)) sendResponse(TriggerEffectMessage(targetGuid, effect, effectInfo, triggerLocation))
case LocalResponse.TriggerSound(sound, pos, unk, volume) => case LocalResponse.TriggerSound(sound, pos, unk, volume) =>
sendResponse(TriggerSoundMessage(sound, pos, unk, volume)) sendResponse(TriggerSoundMessage(sound, pos, unk, volume))
case LocalResponse.UpdateForceDomeStatus(building_guid, activated) => case LocalResponse.UpdateForceDomeStatus(buildingGuid, true) =>
if (activated) { sendResponse(GenericObjectActionMessage(buildingGuid, 11))
sendResponse(GenericObjectActionMessage(building_guid, 11))
} else {
sendResponse(GenericObjectActionMessage(building_guid, 12))
}
case LocalResponse.RechargeVehicleWeapon(vehicle_guid, weapon_guid) => case LocalResponse.UpdateForceDomeStatus(buildingGuid, false) =>
if (tplayer_guid == guid) { sendResponse(GenericObjectActionMessage(buildingGuid, 12))
continent.GUID(vehicle_guid) match {
case Some(vehicle: MountableWeapons) => case LocalResponse.RechargeVehicleWeapon(vehicleGuid, weaponGuid) if resolvedPlayerGuid == guid =>
vehicle.PassengerInSeat(player) match { continent.GUID(vehicleGuid)
case Some(seat_num: Int) => .collect { case vehicle: MountableWeapons => (vehicle, vehicle.PassengerInSeat(player)) }
vehicle.WeaponControlledFromSeat(seat_num) foreach { .collect { case (vehicle, Some(seat_num)) => vehicle.WeaponControlledFromSeat(seat_num) }
case weapon: Tool if weapon.GUID == weapon_guid => .collect { case weapon: Tool if weapon.GUID == weaponGuid =>
sendResponse(InventoryStateMessage(weapon.AmmoSlot.Box.GUID, weapon.GUID, weapon.Magazine)) sendResponse(InventoryStateMessage(weapon.AmmoSlot.Box.GUID, weapon.GUID, weapon.Magazine))
case _ => ;
}
case _ => ;
}
case _ => ;
} }
}
case _ => ; case _ => ()
} }
} }
@ -244,25 +238,25 @@ class SessionLocalHandlers(
/** /**
* na * na
* @param target_guid na * @param targetGuid na
* @param unk1 na * @param unk1 na
* @param unk2 na * @param unk2 na
*/ */
def HackObject(target_guid: PlanetSideGUID, unk1: Long, unk2: Long): Unit = { def HackObject(targetGuid: PlanetSideGUID, unk1: Long, unk2: Long): Unit = {
sendResponse(HackMessage(0, target_guid, PlanetSideGUID(0), 100, unk1, HackState.Hacked, unk2)) sendResponse(HackMessage(unk1=0, targetGuid, player_guid=Service.defaultPlayerGUID, progress=100, unk1, HackState.Hacked, unk2))
} }
/** /**
* Send a PlanetsideAttributeMessage packet to the client * Send a PlanetsideAttributeMessage packet to the client
* @param target_guid The target of the attribute * @param targetGuid The target of the attribute
* @param attribute_number The attribute number * @param attributeType The attribute number
* @param attribute_value The attribute value * @param attributeValue The attribute value
*/ */
def SendPlanetsideAttributeMessage( def SendPlanetsideAttributeMessage(
target_guid: PlanetSideGUID, targetGuid: PlanetSideGUID,
attribute_number: PlanetsideAttributeEnum, attributeType: PlanetsideAttributeEnum,
attribute_value: Long attributeValue: Long
): Unit = { ): Unit = {
sendResponse(PlanetsideAttributeMessage(target_guid, attribute_number, attribute_value)) sendResponse(PlanetsideAttributeMessage(targetGuid, attributeType, attributeValue))
} }
} }

View file

@ -31,176 +31,241 @@ class SessionMountHandlers(
*/ */
def handle(tplayer: Player, reply: Mountable.Exchange): Unit = { def handle(tplayer: Player, reply: Mountable.Exchange): Unit = {
reply match { reply match {
case Mountable.CanMount(obj: ImplantTerminalMech, seat_number, _) => case Mountable.CanMount(obj: ImplantTerminalMech, seatNumber, _) =>
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
log.info(s"${player.Name} mounts an implant terminal") log.info(s"${player.Name} mounts an implant terminal")
sessionData.terminals.CancelAllProximityUnits() sessionData.terminals.CancelAllProximityUnits()
MountingAction(tplayer, obj, seat_number) MountingAction(tplayer, obj, seatNumber)
sessionData.keepAliveFunc = sessionData.keepAlivePersistence sessionData.keepAliveFunc = sessionData.keepAlivePersistence
case Mountable.CanMount(obj: Vehicle, seat_number, _) if obj.Definition == GlobalDefinitions.orbital_shuttle => case Mountable.CanMount(obj: Vehicle, seatNumber, _)
if obj.Definition == GlobalDefinitions.orbital_shuttle =>
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount") sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
log.info(s"${player.Name} mounts the orbital shuttle") log.info(s"${player.Name} mounts the orbital shuttle")
sessionData.terminals.CancelAllProximityUnits() sessionData.terminals.CancelAllProximityUnits()
MountingAction(tplayer, obj, seat_number) MountingAction(tplayer, obj, seatNumber)
sessionData.keepAliveFunc = sessionData.keepAlivePersistence sessionData.keepAliveFunc = sessionData.keepAlivePersistence
case Mountable.CanMount(obj: Vehicle, seat_number, _) => case Mountable.CanMount(obj: Vehicle, seatNumber, _)
if obj.Definition == GlobalDefinitions.ant =>
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount") sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
log.info(s"${player.Name} mounts the ${obj.Definition.Name} in ${ log.info(s"${player.Name} mounts the driver seat of the ${obj.Definition.Name}")
obj.SeatPermissionGroup(seat_number) match {
case Some(AccessPermissionGroup.Driver) => "the driver seat"
case Some(seatType) => s"a $seatType seat (#$seat_number)"
case None => "a seat"
}
}")
val obj_guid: PlanetSideGUID = obj.GUID val obj_guid: PlanetSideGUID = obj.GUID
sessionData.terminals.CancelAllProximityUnits() sessionData.terminals.CancelAllProximityUnits()
sendResponse(PlanetsideAttributeMessage(obj_guid, 0, obj.Health)) sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields)) sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields))
if (obj.Definition == GlobalDefinitions.ant) { sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=45, obj.NtuCapacitorScaled))
sendResponse(PlanetsideAttributeMessage(obj_guid, 45, obj.NtuCapacitorScaled)) sendResponse(GenericObjectActionMessage(obj_guid, code=11))
} MountingAction(tplayer, obj, seatNumber)
if (obj.Definition.MaxCapacitor > 0) {
sendResponse(PlanetsideAttributeMessage(obj_guid, 113, obj.Capacitor))
}
if (seat_number == 0) {
if (obj.Definition == GlobalDefinitions.quadstealth) {
//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, 11))
} else if (obj.WeaponControlledFromSeat(seat_number).isEmpty) {
sessionData.keepAliveFunc = sessionData.keepAlivePersistence
}
sessionData.accessContainer(obj)
sessionData.updateWeaponAtSeatPosition(obj, seat_number)
MountingAction(tplayer, obj, seat_number)
case Mountable.CanMount(obj: FacilityTurret, seat_number, _) => case Mountable.CanMount(obj: Vehicle, seatNumber, _)
if obj.Definition == GlobalDefinitions.quadstealth =>
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount") sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
if (!obj.isUpgrading) { log.info(s"${player.Name} mounts the driver seat of the ${obj.Definition.Name}")
log.info(s"${player.Name} mounts the ${obj.Definition.Name}") val obj_guid: PlanetSideGUID = obj.GUID
if (obj.Definition == GlobalDefinitions.vanu_sentry_turret) { sessionData.terminals.CancelAllProximityUnits()
obj.Zone.LocalEvents ! LocalServiceMessage(obj.Zone.id, LocalAction.SetEmpire(obj.GUID, player.Faction)) sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health))
} sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields))
sendResponse(PlanetsideAttributeMessage(obj.GUID, 0, obj.Health)) //exclusive to the wraith, cloak state matches the cloak state of the driver
sessionData.updateWeaponAtSeatPosition(obj, seat_number) //phantasm doesn't uncloak if the driver is uncloaked and no other vehicle cloaks
MountingAction(tplayer, obj, seat_number) obj.Cloaked = tplayer.Cloaked
} else { sendResponse(GenericObjectActionMessage(obj_guid, code=11))
log.warn( sessionData.accessContainer(obj)
s"MountVehicleMsg: ${tplayer.Name} wants to mount turret ${obj.GUID.guid}, but needs to wait until it finishes updating" MountingAction(tplayer, obj, seatNumber)
)
}
case Mountable.CanMount(obj: PlanetSideGameObject with WeaponTurret, seat_number, _) => case Mountable.CanMount(obj: Vehicle, seatNumber, _)
if seatNumber == 0 && obj.Definition.MaxCapacitor > 0 =>
sessionData.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()
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)
MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
if seatNumber == 0 =>
sessionData.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()
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)
MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
if obj.Definition.MaxCapacitor > 0 =>
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
log.info(s"${player.Name} mounts ${
obj.SeatPermissionGroup(seatNumber) match {
case Some(seatType) => s"a $seatType seat (#$seatNumber)"
case None => "a seat"
}
} of the ${obj.Definition.Name}")
val obj_guid: PlanetSideGUID = obj.GUID
sessionData.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
MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Vehicle, seatNumber, _) =>
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
log.info(s"${player.Name} mounts the ${
obj.SeatPermissionGroup(seatNumber) match {
case Some(seatType) => s"a $seatType seat (#$seatNumber)"
case None => "a seat"
}
} of the ${obj.Definition.Name}")
val obj_guid: PlanetSideGUID = obj.GUID
sessionData.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
MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: FacilityTurret, seatNumber, _)
if obj.Definition == GlobalDefinitions.vanu_sentry_turret =>
sessionData.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)
MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: FacilityTurret, seatNumber, _)
if !obj.isUpgrading =>
sessionData.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)
MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: FacilityTurret, _, _) =>
sessionData.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 WeaponTurret, seatNumber, _) =>
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount") sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
log.info(s"${player.Name} mounts the ${obj.Definition.asInstanceOf[BasicDefinition].Name}") log.info(s"${player.Name} mounts the ${obj.Definition.asInstanceOf[BasicDefinition].Name}")
sendResponse(PlanetsideAttributeMessage(obj.GUID, 0, obj.Health)) sendResponse(PlanetsideAttributeMessage(obj.GUID, attribute_type=0, obj.Health))
sessionData.updateWeaponAtSeatPosition(obj, seat_number) sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
MountingAction(tplayer, obj, seat_number) MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Mountable, _, _) => case Mountable.CanMount(obj: Mountable, _, _) =>
log.warn(s"MountVehicleMsg: $obj is some mountable object and nothing will happen for ${player.Name}") log.warn(s"MountVehicleMsg: $obj is some mountable object and nothing will happen for ${player.Name}")
case Mountable.CanDismount(obj: ImplantTerminalMech, seat_num, _) => case Mountable.CanDismount(obj: ImplantTerminalMech, seatNum, _) =>
log.info(s"${tplayer.Name} dismounts the implant terminal") log.info(s"${tplayer.Name} dismounts the implant terminal")
DismountAction(tplayer, obj, seat_num) DismountAction(tplayer, obj, seatNum)
case Mountable.CanDismount(obj: Vehicle, seat_num, mount_point) case Mountable.CanDismount(obj: Vehicle, _, mountPoint)
if obj.Definition == GlobalDefinitions.orbital_shuttle => if obj.Definition == GlobalDefinitions.orbital_shuttle && obj.MountedIn.nonEmpty =>
//dismount to hart lobby
val pguid = player.GUID val pguid = player.GUID
if (obj.MountedIn.nonEmpty) { log.info(s"${tplayer.Name} dismounts the orbital shuttle into the lobby")
//dismount to hart lobby val sguid = obj.GUID
log.info(s"${tplayer.Name} dismounts the orbital shuttle into the lobby") val (pos, zang) = Vehicles.dismountShuttle(obj, mountPoint)
val sguid = obj.GUID tplayer.Position = pos
val (pos, zang) = Vehicles.dismountShuttle(obj, mount_point) sendResponse(DelayedPathMountMsg(pguid, sguid, u1=60, u2=true))
tplayer.Position = pos continent.LocalEvents ! LocalServiceMessage(
sendResponse(DelayedPathMountMsg(pguid, sguid, 60, u2=true)) continent.id,
continent.LocalEvents ! LocalServiceMessage( LocalAction.SendResponse(ObjectDetachMessage(sguid, pguid, pos, roll=0, pitch=0, zang))
continent.id, )
LocalAction.SendResponse(ObjectDetachMessage(sguid, pguid, pos, 0, 0, zang))
)
} else {
log.info(s"${player.Name} is prepped for dropping")
//get ready for orbital drop
DismountAction(tplayer, obj, seat_num)
continent.actor ! ZoneActor.RemoveFromBlockMap(player) //character doesn't need it
//DismountAction(...) uses vehicle service, so use that service to coordinate the remainder of the messages
continent.VehicleEvents ! VehicleServiceMessage(
player.Name,
VehicleAction.SendResponse(Service.defaultPlayerGUID, PlayerStasisMessage(pguid)) //the stasis message
)
//when the player dismounts, they will be positioned where the shuttle was when it disappeared in the sky
//the player will fall to the ground and is perfectly vulnerable in this state
//additionally, our player must exist in the current zone
//having no in-game avatar target will throw us out of the map screen when deploying and cause softlock
continent.VehicleEvents ! VehicleServiceMessage(
player.Name,
VehicleAction.SendResponse(
Service.defaultPlayerGUID,
PlayerStateShiftMessage(ShiftState(0, obj.Position, obj.Orientation.z, None)) //cower in the shuttle bay
)
)
continent.VehicleEvents ! VehicleServiceMessage(
continent.id,
VehicleAction.SendResponse(pguid, GenericObjectActionMessage(pguid, 9)) //conceal the player
)
}
sessionData.keepAliveFunc = sessionData.zoning.NormalKeepAlive sessionData.keepAliveFunc = sessionData.zoning.NormalKeepAlive
case Mountable.CanDismount(obj: Vehicle, seat_num, _) if obj.Definition == GlobalDefinitions.droppod => case Mountable.CanDismount(obj: Vehicle, seatNum, _)
if obj.Definition == GlobalDefinitions.orbital_shuttle =>
//get ready for orbital drop
val pguid = player.GUID
val events = continent.VehicleEvents
log.info(s"${player.Name} is prepped for dropping")
DismountAction(tplayer, obj, seatNum)
continent.actor ! ZoneActor.RemoveFromBlockMap(player) //character doesn't need it
//DismountAction(...) uses vehicle service, so use that service to coordinate the remainder of the messages
events ! VehicleServiceMessage(
player.Name,
VehicleAction.SendResponse(Service.defaultPlayerGUID, PlayerStasisMessage(pguid)) //the stasis message
)
//when the player dismounts, they will be positioned where the shuttle was when it disappeared in the sky
//the player will fall to the ground and is perfectly vulnerable in this state
//additionally, our player must exist in the current zone
//having no in-game avatar target will throw us out of the map screen when deploying and cause softlock
events ! VehicleServiceMessage(
player.Name,
VehicleAction.SendResponse(
Service.defaultPlayerGUID,
PlayerStateShiftMessage(ShiftState(unk=0, obj.Position, obj.Orientation.z, vel=None)) //cower in the shuttle bay
)
)
events ! VehicleServiceMessage(
continent.id,
VehicleAction.SendResponse(pguid, GenericObjectActionMessage(pguid, code=9)) //conceal the player
)
sessionData.keepAliveFunc = sessionData.zoning.NormalKeepAlive
case Mountable.CanDismount(obj: Vehicle, seatNum, _)
if obj.Definition == GlobalDefinitions.droppod =>
log.info(s"${tplayer.Name} has landed on ${continent.id}") log.info(s"${tplayer.Name} has landed on ${continent.id}")
sessionData.unaccessContainer(obj) sessionData.unaccessContainer(obj)
DismountAction(tplayer, obj, seat_num) DismountAction(tplayer, obj, seatNum)
obj.Actor ! Vehicle.Deconstruct() obj.Actor ! Vehicle.Deconstruct()
case Mountable.CanDismount(obj: Vehicle, seat_num, _) => case Mountable.CanDismount(obj: Vehicle, seatNum, _)
val player_guid: PlanetSideGUID = tplayer.GUID if tplayer.GUID == player.GUID =>
if (player_guid == player.GUID) { //disembarking self
//disembarking self log.info(s"${player.Name} dismounts the ${obj.Definition.Name}'s ${
log.info(s"${player.Name} dismounts the ${obj.Definition.Name}'s ${ obj.SeatPermissionGroup(seatNum) match {
obj.SeatPermissionGroup(seat_num) match { case Some(AccessPermissionGroup.Driver) => "driver seat"
case Some(AccessPermissionGroup.Driver) => "driver seat" case Some(seatType) => s"$seatType seat (#$seatNum)"
case Some(seatType) => s"$seatType seat (#$seat_num)" case None => "seat"
case None => "seat" }
} }")
}") sessionData.vehicles.ConditionalDriverVehicleControl(obj)
sessionData.vehicles.ConditionalDriverVehicleControl(obj) sessionData.unaccessContainer(obj)
sessionData.unaccessContainer(obj) DismountAction(tplayer, obj, seatNum)
DismountAction(tplayer, obj, seat_num)
} else {
continent.VehicleEvents ! VehicleServiceMessage(
continent.id,
VehicleAction.KickPassenger(player_guid, seat_num, unk2=true, obj.GUID)
)
}
case Mountable.CanDismount(obj: PlanetSideGameObject with WeaponTurret, seat_num, _) => case Mountable.CanDismount(obj: Vehicle, seat_num, _) =>
continent.VehicleEvents ! VehicleServiceMessage(
continent.id,
VehicleAction.KickPassenger(tplayer.GUID, seat_num, unk2=true, obj.GUID)
)
case Mountable.CanDismount(obj: PlanetSideGameObject with WeaponTurret, seatNum, _) =>
log.info(s"${tplayer.Name} dismounts a ${obj.Definition.asInstanceOf[ObjectDefinition].Name}") log.info(s"${tplayer.Name} dismounts a ${obj.Definition.asInstanceOf[ObjectDefinition].Name}")
DismountAction(tplayer, obj, seat_num) DismountAction(tplayer, obj, seatNum)
case Mountable.CanDismount(obj: Mountable, _, _) => case Mountable.CanDismount(obj: Mountable, _, _) =>
log.warn(s"DismountVehicleMsg: $obj is some dismountable object but nothing will happen for ${player.Name}") log.warn(s"DismountVehicleMsg: $obj is some dismountable object but nothing will happen for ${player.Name}")
case Mountable.CanNotMount(obj: Vehicle, mount_point) => case Mountable.CanNotMount(obj: Vehicle, mountPoint) =>
log.warn(s"MountVehicleMsg: ${tplayer.Name} attempted to mount $obj's mount $mount_point, but was not allowed") log.warn(s"MountVehicleMsg: ${tplayer.Name} attempted to mount $obj's mount $mountPoint, but was not allowed")
obj.GetSeatFromMountPoint(mount_point) match { obj.GetSeatFromMountPoint(mountPoint).collect {
case Some(seatNum) if obj.SeatPermissionGroup(seatNum).contains(AccessPermissionGroup.Driver) => case seatNum if obj.SeatPermissionGroup(seatNum).contains(AccessPermissionGroup.Driver) =>
sendResponse( sendResponse(
ChatMsg(ChatMessageType.CMT_OPEN, wideContents=false, "", "You are not the driver of this vehicle.", None) ChatMsg(ChatMessageType.CMT_OPEN, wideContents=false, recipient="", "You are not the driver of this vehicle.", note=None)
) )
case _ =>
} }
case Mountable.CanNotMount(obj: Mountable, mount_point) => case Mountable.CanNotMount(obj: Mountable, mountPoint) =>
log.warn(s"MountVehicleMsg: ${tplayer.Name} attempted to mount $obj's mount $mount_point, but was not allowed") log.warn(s"MountVehicleMsg: ${tplayer.Name} attempted to mount $obj's mount $mountPoint, but was not allowed")
case Mountable.CanNotDismount(obj, seat_num) => case Mountable.CanNotDismount(obj, seatNum) =>
log.warn( log.warn(s"DismountVehicleMsg: ${tplayer.Name} attempted to dismount $obj's mount $seatNum, but was not allowed")
s"DismountVehicleMsg: ${tplayer.Name} attempted to dismount $obj's mount $seat_num, but was not allowed"
)
} }
} }
@ -211,15 +276,15 @@ class SessionMountHandlers(
* @param seatNum the mount into which the player is mounting * @param seatNum the mount into which the player is mounting
*/ */
def MountingAction(tplayer: Player, obj: PlanetSideGameObject with Mountable, seatNum: Int): Unit = { def MountingAction(tplayer: Player, obj: PlanetSideGameObject with Mountable, seatNum: Int): Unit = {
val player_guid: PlanetSideGUID = tplayer.GUID val playerGuid: PlanetSideGUID = tplayer.GUID
val obj_guid: PlanetSideGUID = obj.GUID val objGuid: PlanetSideGUID = obj.GUID
sessionData.playerActionsToCancel() sessionData.playerActionsToCancel()
avatarActor ! AvatarActor.DeactivateActiveImplants() avatarActor ! AvatarActor.DeactivateActiveImplants()
avatarActor ! AvatarActor.SuspendStaminaRegeneration(3 seconds) avatarActor ! AvatarActor.SuspendStaminaRegeneration(3.seconds)
sendResponse(ObjectAttachMessage(obj_guid, player_guid, seatNum)) sendResponse(ObjectAttachMessage(objGuid, playerGuid, seatNum))
continent.VehicleEvents ! VehicleServiceMessage( continent.VehicleEvents ! VehicleServiceMessage(
continent.id, continent.id,
VehicleAction.MountVehicle(player_guid, obj_guid, seatNum) VehicleAction.MountVehicle(playerGuid, objGuid, seatNum)
) )
} }
@ -230,17 +295,17 @@ class SessionMountHandlers(
* @param seatNum the mount out of which which the player is disembarking * @param seatNum the mount out of which which the player is disembarking
*/ */
def DismountAction(tplayer: Player, obj: PlanetSideGameObject with Mountable, seatNum: Int): Unit = { def DismountAction(tplayer: Player, obj: PlanetSideGameObject with Mountable, seatNum: Int): Unit = {
val player_guid: PlanetSideGUID = tplayer.GUID val playerGuid: PlanetSideGUID = tplayer.GUID
sessionData.keepAliveFunc = sessionData.zoning.NormalKeepAlive sessionData.keepAliveFunc = sessionData.zoning.NormalKeepAlive
val bailType = if (tplayer.BailProtection) { val bailType = if (tplayer.BailProtection) {
BailType.Bailed BailType.Bailed
} else { } else {
BailType.Normal BailType.Normal
} }
sendResponse(DismountVehicleMsg(player_guid, bailType, wasKickedByDriver = false)) sendResponse(DismountVehicleMsg(playerGuid, bailType, wasKickedByDriver = false))
continent.VehicleEvents ! VehicleServiceMessage( continent.VehicleEvents ! VehicleServiceMessage(
continent.id, continent.id,
VehicleAction.DismountVehicle(player_guid, bailType, unk2=false) VehicleAction.DismountVehicle(playerGuid, bailType, unk2=false)
) )
} }
} }

View file

@ -28,31 +28,31 @@ class SessionTerminalHandlers(
/* packets */ /* packets */
def handleItemTransaction(pkt: ItemTransactionMessage): Unit = { def handleItemTransaction(pkt: ItemTransactionMessage): Unit = {
val ItemTransactionMessage(terminalGuid, _, _, _, _, _) = pkt val ItemTransactionMessage(terminalGuid, transactionType, _, itemName, _, _) = pkt
continent.GUID(terminalGuid) match { continent.GUID(terminalGuid) match {
case Some(term: Terminal) => case Some(term: Terminal) if lastTerminalOrderFulfillment =>
if (lastTerminalOrderFulfillment) { log.info(s"${player.Name} is submitting an order - $transactionType of $itemName")
log.trace(s"ItemTransactionMessage: ${player.Name} is submitting an order") lastTerminalOrderFulfillment = false
lastTerminalOrderFulfillment = false sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") term.Actor ! Terminal.Request(player, pkt)
term.Actor ! Terminal.Request(player, pkt) case Some(_: Terminal) =>
} log.warn(s"Please Wait until your previous order has been fulfilled, ${player.Name}")
case Some(obj: PlanetSideGameObject) => case Some(obj) =>
log.error(s"ItemTransaction: $obj is not a terminal, ${player.Name}") log.error(s"ItemTransaction: ${obj.Definition.Name} is not a terminal, ${player.Name}")
case _ => case _ =>
log.error(s"ItemTransaction: $terminalGuid does not exist, ${player.Name}") log.error(s"ItemTransaction: entity with guid=${terminalGuid.guid} does not exist, ${player.Name}")
} }
} }
def handleProximityTerminalUse(pkt: ProximityTerminalUseMessage): Unit = { def handleProximityTerminalUse(pkt: ProximityTerminalUseMessage): Unit = {
val ProximityTerminalUseMessage(_, object_guid, _) = pkt val ProximityTerminalUseMessage(_, objectGuid, _) = pkt
continent.GUID(object_guid) match { continent.GUID(objectGuid) match {
case Some(obj: Terminal with ProximityUnit) => case Some(obj: Terminal with ProximityUnit) =>
HandleProximityTerminalUse(obj) HandleProximityTerminalUse(obj)
case Some(obj) => case Some(obj) =>
log.warn(s"ProximityTerminalUse: $obj does not have proximity effects for ${player.Name}") log.warn(s"ProximityTerminalUse: ${obj.Definition.Name} guid=${objectGuid.guid} is not ready to implement proximity effects")
case None => case None =>
log.error(s"ProximityTerminalUse: ${player.Name} can not find an object with guid $object_guid") log.error(s"ProximityTerminalUse: ${player.Name} can not find an object with guid ${objectGuid.guid}")
} }
} }
@ -60,26 +60,27 @@ class SessionTerminalHandlers(
/** /**
* na * na
*
* @param tplayer na * @param tplayer na
* @param msg na * @param msg na
* @param order na * @param order na
*/ */
def handle(tplayer: Player, msg: ItemTransactionMessage, order: Terminal.Exchange): Unit = { def handle(tplayer: Player, msg: ItemTransactionMessage, order: Terminal.Exchange): Unit = {
order match { order match {
case Terminal.BuyEquipment(item)
if tplayer.avatar.purchaseCooldown(item.Definition).nonEmpty =>
lastTerminalOrderFulfillment = true
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false))
case Terminal.BuyEquipment(item) => case Terminal.BuyEquipment(item) =>
tplayer.avatar.purchaseCooldown(item.Definition) match { avatarActor ! AvatarActor.UpdatePurchaseTime(item.Definition)
case Some(_) => TaskWorkflow.execute(BuyNewEquipmentPutInInventory(
lastTerminalOrderFulfillment = true continent.GUID(tplayer.VehicleSeated) match {
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false)) case Some(v: Vehicle) => v
case None => case _ => player
avatarActor ! AvatarActor.UpdatePurchaseTime(item.Definition) },
TaskWorkflow.execute(BuyNewEquipmentPutInInventory( tplayer,
continent.GUID(tplayer.VehicleSeated) match { case Some(v: Vehicle) => v; case _ => player }, msg.terminal_guid
tplayer, )(item))
msg.terminal_guid
)(item))
}
case Terminal.SellEquipment() => case Terminal.SellEquipment() =>
SellEquipmentFromInventory(tplayer, tplayer, msg.terminal_guid)(Player.FreeHandSlot) SellEquipmentFromInventory(tplayer, tplayer, msg.terminal_guid)(Player.FreeHandSlot)
@ -100,77 +101,75 @@ class SessionTerminalHandlers(
avatarActor ! AvatarActor.SellImplant(msg.terminal_guid, implant) avatarActor ! AvatarActor.SellImplant(msg.terminal_guid, implant)
lastTerminalOrderFulfillment = true lastTerminalOrderFulfillment = true
case Terminal.BuyVehicle(vehicle, weapons, trunk) => case Terminal.BuyVehicle(vehicle, _, _)
tplayer.avatar.purchaseCooldown(vehicle.Definition) match { if tplayer.avatar.purchaseCooldown(vehicle.Definition).nonEmpty =>
case Some(_) => sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false))
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false))
case None =>
continent.map.terminalToSpawnPad
.find { case (termid, _) => termid == msg.terminal_guid.guid }
.collect {
case (a: Int, b: Int) => (continent.GUID(a), continent.GUID(b))
case _ => (None, None)
}
.get match {
case (Some(term: Terminal), Some(pad: VehicleSpawnPad)) =>
avatarActor ! AvatarActor.UpdatePurchaseTime(vehicle.Definition)
vehicle.Faction = tplayer.Faction
vehicle.Position = pad.Position
vehicle.Orientation = pad.Orientation + Vector3.z(pad.Definition.VehicleCreationZOrientOffset)
//default loadout, weapons
val vWeapons = vehicle.Weapons
weapons.foreach(entry => {
vWeapons.get(entry.start) match {
case Some(slot) =>
entry.obj.Faction = tplayer.Faction
slot.Equipment = None
slot.Equipment = entry.obj
case None =>
log.warn(
s"BuyVehicle: ${player.Name} tries to apply default loadout to $vehicle on spawn, but can not find a mounted weapon for ${entry.start}"
)
}
})
//default loadout, trunk
val vTrunk = vehicle.Trunk
vTrunk.Clear()
trunk.foreach(entry => {
entry.obj.Faction = tplayer.Faction
vTrunk.InsertQuickly(entry.start, entry.obj)
})
TaskWorkflow.execute(registerVehicleFromSpawnPad(vehicle, pad, term))
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = true))
if (GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition)) {
sendResponse(UnuseItemMessage(player.GUID, msg.terminal_guid))
}
case _ =>
log.error(
s"${tplayer.Name} wanted to spawn a vehicle, but there was no spawn pad associated with terminal ${msg.terminal_guid} to accept it"
)
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false))
}
}
lastTerminalOrderFulfillment = true lastTerminalOrderFulfillment = true
case Terminal.NoDeal() => case Terminal.BuyVehicle(vehicle, weapons, trunk) =>
val order: String = if (msg == null) { continent.map.terminalToSpawnPad
"missing order" .find { case (termid, _) => termid == msg.terminal_guid.guid }
} else { .map { case (a: Int, b: Int) => (continent.GUID(a), continent.GUID(b)) }
s"${msg.transaction_type} order" .collect { case (Some(term: Terminal), Some(pad: VehicleSpawnPad)) =>
avatarActor ! AvatarActor.UpdatePurchaseTime(vehicle.Definition)
vehicle.Faction = tplayer.Faction
vehicle.Position = pad.Position
vehicle.Orientation = pad.Orientation + Vector3.z(pad.Definition.VehicleCreationZOrientOffset)
//default loadout, weapons
val vWeapons = vehicle.Weapons
weapons.foreach { entry =>
vWeapons.get(entry.start) match {
case Some(slot) =>
entry.obj.Faction = tplayer.Faction
slot.Equipment = None
slot.Equipment = entry.obj
case None =>
log.warn(
s"BuyVehicle: ${player.Name} tries to apply default loadout to $vehicle on spawn, but can not find a mounted weapon for ${entry.start}"
)
}
}
//default loadout, trunk
val vTrunk = vehicle.Trunk
vTrunk.Clear()
trunk.foreach { entry =>
entry.obj.Faction = tplayer.Faction
vTrunk.InsertQuickly(entry.start, entry.obj)
}
TaskWorkflow.execute(registerVehicleFromSpawnPad(vehicle, pad, term))
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = true))
if (GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition)) {
sendResponse(UnuseItemMessage(player.GUID, msg.terminal_guid))
}
}.orElse {
log.error(
s"${tplayer.Name} wanted to spawn a vehicle, but there was no spawn pad associated with terminal ${msg.terminal_guid} to accept it"
)
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false))
None
} }
log.warn(s"NoDeal: ${tplayer.Name} made a request but the terminal rejected the $order")
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, msg.transaction_type, success = false)) case Terminal.NoDeal() if msg != null =>
val transaction = msg.transaction_type
log.warn(s"NoDeal: ${tplayer.Name} made a request but the terminal rejected the ${transaction.toString} order")
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, transaction, success = false))
lastTerminalOrderFulfillment = true lastTerminalOrderFulfillment = true
case _ => case _ =>
val transaction = msg.transaction_type val terminal = msg.terminal_guid.guid
log.warn(s"n/a: ${tplayer.Name} made a $transaction request but terminal#${msg.terminal_guid.guid} is missing or wrong") continent.GUID(terminal) match {
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, transaction, success = false)) case Some(term: Terminal) =>
log.warn(s"NoDeal?: ${tplayer.Name} made a request but the ${term.Definition.Name}#$terminal rejected the missing order")
case Some(_) =>
log.warn(s"NoDeal?: ${tplayer.Name} made a request to a non-terminal entity#$terminal")
case None =>
log.warn(s"NoDeal?: ${tplayer.Name} made a request to a missing entity#$terminal")
}
lastTerminalOrderFulfillment = true lastTerminalOrderFulfillment = true
} }
} }
/* */ /* support */
/** /**
* Construct tasking that adds a completed and registered vehicle into the scene. * Construct tasking that adds a completed and registered vehicle into the scene.
@ -209,7 +208,7 @@ class SessionTerminalHandlers(
val term_guid = terminal.GUID val term_guid = terminal.GUID
val targets = FindProximityUnitTargetsInScope(terminal) val targets = FindProximityUnitTargetsInScope(terminal)
val currentTargets = terminal.Targets val currentTargets = terminal.Targets
targets.foreach(target => { targets.foreach { target =>
if (!currentTargets.contains(target)) { if (!currentTargets.contains(target)) {
StartUsingProximityUnit(terminal, target) StartUsingProximityUnit(terminal, target)
} else if (targets.isEmpty) { } else if (targets.isEmpty) {
@ -217,7 +216,7 @@ class SessionTerminalHandlers(
s"HandleProximityTerminalUse: ${player.Name} could not find valid targets to give to proximity unit ${terminal.Definition.Name}@${term_guid.guid}" s"HandleProximityTerminalUse: ${player.Name} could not find valid targets to give to proximity unit ${terminal.Definition.Name}@${term_guid.guid}"
) )
} }
}) }
} }
/** /**
@ -226,7 +225,7 @@ class SessionTerminalHandlers(
* @return na * @return na
*/ */
def FindProximityUnitTargetsInScope(terminal: Terminal with ProximityUnit): Seq[PlanetSideGameObject] = { def FindProximityUnitTargetsInScope(terminal: Terminal with ProximityUnit): Seq[PlanetSideGameObject] = {
terminal.Definition.asInstanceOf[ProximityDefinition].TargetValidation.keySet collect { terminal.Definition.asInstanceOf[ProximityDefinition].TargetValidation.keySet.collect {
case EffectTarget.Category.Player => Some(player) case EffectTarget.Category.Player => Some(player)
case EffectTarget.Category.Vehicle | EffectTarget.Category.Aircraft => continent.GUID(player.VehicleSeated) case EffectTarget.Category.Vehicle | EffectTarget.Category.Aircraft => continent.GUID(player.VehicleSeated)
} collect { } collect {
@ -241,7 +240,6 @@ class SessionTerminalHandlers(
*/ */
def StartUsingProximityUnit(terminal: Terminal with ProximityUnit, target: PlanetSideGameObject): Unit = { def StartUsingProximityUnit(terminal: Terminal with ProximityUnit, target: PlanetSideGameObject): Unit = {
val term_guid = terminal.GUID val term_guid = terminal.GUID
//log.trace(s"StartUsingProximityUnit: ${player.Name} wants to use ${terminal.Definition.Name}@${term_guid.guid} on $target")
if (player.isAlive) { if (player.isAlive) {
target match { target match {
case _: Player => case _: Player =>
@ -256,7 +254,7 @@ class SessionTerminalHandlers(
terminal.Definition match { terminal.Definition match {
case GlobalDefinitions.adv_med_terminal | GlobalDefinitions.medical_terminal => case GlobalDefinitions.adv_med_terminal | GlobalDefinitions.medical_terminal =>
usingMedicalTerminal = Some(term_guid) usingMedicalTerminal = Some(term_guid)
case _ => ; case _ => ()
} }
} }
} }
@ -269,7 +267,7 @@ class SessionTerminalHandlers(
*/ */
def StopUsingProximityUnit(terminal: Terminal with ProximityUnit): Unit = { def StopUsingProximityUnit(terminal: Terminal with ProximityUnit): Unit = {
FindProximityUnitTargetsInScope(terminal).foreach { target => FindProximityUnitTargetsInScope(terminal).foreach { target =>
LocalStopUsingProximityUnit(terminal, target) LocalStopUsingProximityUnit(terminal)
terminal.Actor ! CommonMessages.Unuse(player, Some(target)) terminal.Actor ! CommonMessages.Unuse(player, Some(target))
} }
} }
@ -282,11 +280,8 @@ class SessionTerminalHandlers(
* Other sorts of proximity-based units are put on a timer. * Other sorts of proximity-based units are put on a timer.
* @param terminal the proximity-based unit * @param terminal the proximity-based unit
*/ */
def LocalStopUsingProximityUnit(terminal: Terminal with ProximityUnit, target: PlanetSideGameObject): Unit = { def LocalStopUsingProximityUnit(terminal: Terminal with ProximityUnit): Unit = {
val term_guid = terminal.GUID ForgetAllProximityTerminals(terminal.GUID)
if (usingMedicalTerminal.contains(term_guid)) {
usingMedicalTerminal = None
}
} }
/** /**
@ -296,21 +291,31 @@ class SessionTerminalHandlers(
* @see `postStop` * @see `postStop`
*/ */
def CancelAllProximityUnits(): Unit = { def CancelAllProximityUnits(): Unit = {
continent.GUID(usingMedicalTerminal) match { usingMedicalTerminal.foreach { CancelAllProximityUnits }
case Some(terminal: Terminal with ProximityUnit) => }
/**
* Cease all current interactions with proximity-based units.
* Pair with `PlayerActionsToCancel`, except when logging out (stopping).
* This operations may invoke callback messages.
* @param guid globally unique identifier for a proximity terminal
* @see `postStop`
*/
def CancelAllProximityUnits(guid: PlanetSideGUID): Unit = {
continent.GUID(guid).collect {
case terminal: Terminal with ProximityUnit =>
FindProximityUnitTargetsInScope(terminal).foreach(target => FindProximityUnitTargetsInScope(terminal).foreach(target =>
terminal.Actor ! CommonMessages.Unuse(player, Some(target)) terminal.Actor ! CommonMessages.Unuse(player, Some(target))
) )
ForgetAllProximityTerminals(usingMedicalTerminal.get) ForgetAllProximityTerminals(guid)
case _ => ;
} }
} }
/** /**
* na * na
*/ */
def ForgetAllProximityTerminals(term_guid: PlanetSideGUID): Unit = { def ForgetAllProximityTerminals(termGuid: PlanetSideGUID): Unit = {
if (usingMedicalTerminal.contains(term_guid)) { if (usingMedicalTerminal.contains(termGuid)) {
usingMedicalTerminal = None usingMedicalTerminal = None
} }
} }

View file

@ -3,7 +3,7 @@ package net.psforever.actors.session.support
import akka.actor.{ActorContext, ActorRef, typed} import akka.actor.{ActorContext, ActorRef, typed}
import net.psforever.actors.session.AvatarActor import net.psforever.actors.session.AvatarActor
import net.psforever.objects.equipment.{JammableMountedWeapons, JammableUnit} import net.psforever.objects.equipment.{Equipment, JammableMountedWeapons, JammableUnit}
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow} import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.pad.VehicleSpawnPad import net.psforever.objects.serverobject.pad.VehicleSpawnPad
@ -31,153 +31,34 @@ class SessionVehicleHandlers(
* @param reply na * @param reply na
*/ */
def handle(toChannel: String, guid: PlanetSideGUID, reply: VehicleResponse.Response): Unit = { def handle(toChannel: String, guid: PlanetSideGUID, reply: VehicleResponse.Response): Unit = {
val tplayer_guid = if (player.HasGUID) player.GUID else PlanetSideGUID(0) val resolvedPlayerGuid = if (player.HasGUID) {
player.GUID
} else {
Service.defaultPlayerGUID
}
val isNotSameTarget = resolvedPlayerGuid != guid
reply match { reply match {
case VehicleResponse.AttachToRails(vehicle_guid, pad_guid) => case VehicleResponse.VehicleState(
sendResponse(ObjectAttachMessage(pad_guid, vehicle_guid, 3)) vehicleGuid,
unk1,
case VehicleResponse.ChildObjectState(object_guid, pitch, yaw) => pos,
if (tplayer_guid != guid) { orient,
sendResponse(ChildObjectStateMessage(object_guid, pitch, yaw)) vel,
} unk2,
unk3,
case VehicleResponse.ConcealPlayer(player_guid) => unk4,
sendResponse(GenericObjectActionMessage(player_guid, 9)) wheelDirection,
unk5,
case VehicleResponse.DismountVehicle(bailType, wasKickedByDriver) => unk6
if (tplayer_guid != guid) { ) if isNotSameTarget && player.VehicleSeated.contains(vehicleGuid) =>
sendResponse(DismountVehicleMsg(guid, bailType, wasKickedByDriver)) //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
case VehicleResponse.DeployRequest(object_guid, state, unk1, unk2, pos) => player.Orientation = orient
if (tplayer_guid != guid) { player.Velocity = vel
sendResponse(DeployRequestMessage(guid, object_guid, state, unk1, unk2, pos))
}
case VehicleResponse.DetachFromRails(vehicle_guid, pad_guid, pad_position, pad_orientation_z) =>
val pad = continent.GUID(pad_guid).get.asInstanceOf[VehicleSpawnPad].Definition
sendResponse(
ObjectDetachMessage(
pad_guid,
vehicle_guid,
pad_position + Vector3.z(pad.VehicleCreationZOffset),
pad_orientation_z + pad.VehicleCreationZOrientOffset
)
)
case VehicleResponse.EquipmentInSlot(pkt) =>
if (tplayer_guid != guid) {
sendResponse(pkt)
}
case VehicleResponse.FrameVehicleState(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA) =>
if (tplayer_guid != guid) {
sendResponse(FrameVehicleStateMessage(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA))
}
case VehicleResponse.GenericObjectAction(object_guid, action) =>
if (tplayer_guid != guid) {
sendResponse(GenericObjectActionMessage(object_guid, action))
}
case VehicleResponse.HitHint(source_guid) =>
if (player.isAlive) {
sendResponse(HitHint(source_guid, player.GUID))
}
case VehicleResponse.InventoryState(obj, parent_guid, start, con_data) =>
if (tplayer_guid != guid) {
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
val obj_guid = obj.GUID
sendResponse(ObjectDeleteMessage(obj_guid, 0))
sendResponse(
ObjectCreateDetailedMessage(
obj.Definition.ObjectId,
obj_guid,
ObjectCreateMessageParent(parent_guid, start),
con_data
)
)
}
case VehicleResponse.KickPassenger(_, wasKickedByDriver, vehicle_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))
if (tplayer_guid == guid) {
val typeOfRide = continent.GUID(vehicle_guid) match {
case Some(obj: Vehicle) =>
sessionData.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!")
}
case VehicleResponse.InventoryState2(obj_guid, parent_guid, value) =>
if (tplayer_guid != guid) {
sendResponse(InventoryStateMessage(obj_guid, 0, parent_guid, value))
}
case VehicleResponse.LoadVehicle(vehicle, vtype, vguid, vdata) =>
//this is not be suitable for vehicles with people who are seated in it before it spawns (if that is possible)
if (tplayer_guid != guid) {
sendResponse(ObjectCreateMessage(vtype, vguid, vdata))
Vehicles.ReloadAccessPermissions(vehicle, player.Name)
}
case VehicleResponse.MountVehicle(vehicle_guid, seat) =>
if (tplayer_guid != guid) {
sendResponse(ObjectAttachMessage(vehicle_guid, guid, seat))
}
case VehicleResponse.ObjectDelete(itemGuid) =>
if (tplayer_guid != guid) {
sendResponse(ObjectDeleteMessage(itemGuid, 0))
}
case VehicleResponse.Ownership(vehicleGuid) =>
if (tplayer_guid == guid) { // Only the player that owns this vehicle needs the ownership packet
avatarActor ! AvatarActor.SetVehicle(Some(vehicleGuid))
sendResponse(PlanetsideAttributeMessage(tplayer_guid, 21, vehicleGuid))
}
case VehicleResponse.PlanetsideAttribute(vehicle_guid, attribute_type, attribute_value) =>
if (tplayer_guid != guid) {
sendResponse(PlanetsideAttributeMessage(vehicle_guid, attribute_type, attribute_value))
}
case VehicleResponse.ResetSpawnPad(pad_guid) =>
sendResponse(GenericObjectActionMessage(pad_guid, 23))
case VehicleResponse.RevealPlayer(player_guid) =>
sendResponse(GenericObjectActionMessage(player_guid, 10))
case VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission) =>
if (tplayer_guid != guid) {
sendResponse(PlanetsideAttributeMessage(vehicle_guid, seat_group, permission))
}
case VehicleResponse.StowEquipment(vehicle_guid, slot, item_type, item_guid, item_data) =>
if (tplayer_guid != guid) {
//TODO prefer ObjectAttachMessage, but how to force ammo pools to update properly?
sendResponse(
ObjectCreateDetailedMessage(item_type, item_guid, ObjectCreateMessageParent(vehicle_guid, slot), item_data)
)
}
case VehicleResponse.UnloadVehicle(_, vehicle_guid) =>
sendResponse(ObjectDeleteMessage(vehicle_guid, 0))
case VehicleResponse.UnstowEquipment(item_guid) =>
if (tplayer_guid != guid) {
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
sendResponse(ObjectDeleteMessage(item_guid, 0))
}
case VehicleResponse.VehicleState( case VehicleResponse.VehicleState(
vehicle_guid, vehicleGuid,
unk1, unk1,
pos, pos,
ang, ang,
@ -185,58 +66,190 @@ class SessionVehicleHandlers(
unk2, unk2,
unk3, unk3,
unk4, unk4,
wheel_direction, wheelDirection,
unk5, unk5,
unk6 unk6
) => ) if isNotSameTarget =>
if (tplayer_guid != guid) { //player who is watching the vehicle from the outside
sendResponse( sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, ang, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6))
VehicleStateMessage(vehicle_guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel_direction, unk5, unk6)
) case VehicleResponse.ChildObjectState(objectGuid, pitch, yaw) if isNotSameTarget =>
if (player.VehicleSeated.contains(vehicle_guid)) { sendResponse(ChildObjectStateMessage(objectGuid, pitch, yaw))
player.Position = pos
} case VehicleResponse.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 VehicleResponse.DismountVehicle(bailType, wasKickedByDriver) if isNotSameTarget =>
sendResponse(DismountVehicleMsg(guid, bailType, wasKickedByDriver))
case VehicleResponse.MountVehicle(vehicleGuid, seat) if isNotSameTarget =>
sendResponse(ObjectAttachMessage(vehicleGuid, guid, seat))
case VehicleResponse.DeployRequest(objectGuid, state, unk1, unk2, pos) if isNotSameTarget =>
sendResponse(DeployRequestMessage(guid, objectGuid, state, unk1, unk2, pos))
case VehicleResponse.SendResponse(msg) => case VehicleResponse.SendResponse(msg) =>
sendResponse(msg) sendResponse(msg)
case VehicleResponse.AttachToRails(vehicleGuid, padGuid) =>
sendResponse(ObjectAttachMessage(padGuid, vehicleGuid, slot=3))
case VehicleResponse.ConcealPlayer(playerGuid) =>
sendResponse(GenericObjectActionMessage(playerGuid, code=9))
case VehicleResponse.DetachFromRails(vehicleGuid, padGuid, padPosition, padOrientationZ) =>
val pad = continent.GUID(padGuid).get.asInstanceOf[VehicleSpawnPad].Definition
sendResponse(
ObjectDetachMessage(
padGuid,
vehicleGuid,
padPosition + Vector3.z(pad.VehicleCreationZOffset),
padOrientationZ + pad.VehicleCreationZOrientOffset
)
)
case VehicleResponse.EquipmentInSlot(pkt) if isNotSameTarget =>
sendResponse(pkt)
case VehicleResponse.GenericObjectAction(objectGuid, action) if isNotSameTarget =>
sendResponse(GenericObjectActionMessage(objectGuid, action))
case VehicleResponse.HitHint(sourceGuid) if player.isAlive =>
sendResponse(HitHint(sourceGuid, player.GUID))
case VehicleResponse.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 VehicleResponse.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) =>
sessionData.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!")
case VehicleResponse.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 VehicleResponse.InventoryState2(objGuid, parentGuid, value) if isNotSameTarget =>
sendResponse(InventoryStateMessage(objGuid, unk=0, parentGuid, value))
case VehicleResponse.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 VehicleResponse.ObjectDelete(itemGuid) if isNotSameTarget =>
sendResponse(ObjectDeleteMessage(itemGuid, unk1=0))
case VehicleResponse.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 VehicleResponse.PlanetsideAttribute(vehicleGuid, attributeType, attributeValue) if isNotSameTarget =>
sendResponse(PlanetsideAttributeMessage(vehicleGuid, attributeType, attributeValue))
case VehicleResponse.ResetSpawnPad(padGuid) =>
sendResponse(GenericObjectActionMessage(padGuid, code=23))
case VehicleResponse.RevealPlayer(playerGuid) =>
sendResponse(GenericObjectActionMessage(playerGuid, code=10))
case VehicleResponse.SeatPermissions(vehicleGuid, seatGroup, permission) if isNotSameTarget =>
sendResponse(PlanetsideAttributeMessage(vehicleGuid, seatGroup, permission))
case VehicleResponse.StowEquipment(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 VehicleResponse.UnloadVehicle(_, vehicleGuid) =>
sendResponse(ObjectDeleteMessage(vehicleGuid, unk1=0))
case VehicleResponse.UnstowEquipment(itemGuid) if isNotSameTarget =>
//TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly?
sendResponse(ObjectDeleteMessage(itemGuid, unk1=0))
case VehicleResponse.UpdateAmsSpawnPoint(list) => case VehicleResponse.UpdateAmsSpawnPoint(list) =>
sessionData.zoning.spawn.amsSpawnPoints = list.filter(tube => tube.Faction == player.Faction) sessionData.zoning.spawn.amsSpawnPoints = list.filter(tube => tube.Faction == player.Faction)
sessionData.zoning.spawn.DrawCurrentAmsSpawnPoint() sessionData.zoning.spawn.DrawCurrentAmsSpawnPoint()
case VehicleResponse.TransferPassengerChannel(old_channel, temp_channel, vehicle, vehicle_to_delete) => case VehicleResponse.TransferPassengerChannel(oldChannel, tempChannel, vehicle, vehicleToDelete) if isNotSameTarget =>
if (tplayer_guid != guid) { sessionData.zoning.interstellarFerry = Some(vehicle)
sessionData.zoning.interstellarFerry = Some(vehicle) sessionData.zoning.interstellarFerryTopLevelGUID = Some(vehicleToDelete)
sessionData.zoning.interstellarFerryTopLevelGUID = Some(vehicle_to_delete) continent.VehicleEvents ! Service.Leave(Some(oldChannel)) //old vehicle-specific channel (was s"${vehicle.Actor}")
continent.VehicleEvents ! Service.Leave( galaxyService ! Service.Join(tempChannel) //temporary vehicle-specific channel
Some(old_channel) log.debug(s"TransferPassengerChannel: ${player.Name} now subscribed to $tempChannel for vehicle gating")
) //old vehicle-specific channel (was s"${vehicle.Actor}")
galaxyService ! Service.Join(temp_channel) //temporary vehicle-specific channel
log.debug(s"TransferPassengerChannel: ${player.Name} now subscribed to $temp_channel for vehicle gating")
}
case VehicleResponse.KickCargo(vehicle, speed, delay) => case VehicleResponse.KickCargo(vehicle, speed, delay)
if (player.VehicleSeated.nonEmpty && sessionData.zoning.spawn.deadState == DeadState.Alive) { if player.VehicleSeated.nonEmpty && sessionData.zoning.spawn.deadState == DeadState.Alive && speed > 0 =>
if (speed > 0) { val strafe = 1 + Vehicles.CargoOrientation(vehicle)
val strafe = val reverseSpeed = if (strafe > 1) { 0 } else { speed }
if (Vehicles.CargoOrientation(vehicle) == 1) 2 //strafe or reverse, not both
else 1 sessionData.vehicles.serverVehicleControlVelocity = Some(reverseSpeed)
val reverseSpeed = sendResponse(ServerVehicleOverrideMsg(
if (strafe > 1) 0 lock_accelerator=true,
else speed lock_wheel=true,
//strafe or reverse, not both reverse=true,
sessionData.vehicles.serverVehicleControlVelocity = Some(reverseSpeed) unk4=false,
sendResponse(ServerVehicleOverrideMsg(lock_accelerator=true, lock_wheel=true, reverse=true, unk4=false, 0, strafe, reverseSpeed, Some(0))) lock_vthrust=0,
import scala.concurrent.ExecutionContext.Implicits.global strafe,
context.system.scheduler.scheduleOnce( reverseSpeed,
delay milliseconds, unk8=Some(0)
context.self, ))
VehicleServiceResponse(toChannel, PlanetSideGUID(0), VehicleResponse.KickCargo(vehicle, 0, delay)) import scala.concurrent.ExecutionContext.Implicits.global
) context.system.scheduler.scheduleOnce(
} else { delay milliseconds,
sessionData.vehicles.serverVehicleControlVelocity = None context.self,
sendResponse(ServerVehicleOverrideMsg(lock_accelerator=false,lock_wheel=false, reverse=false, unk4=false, 0, 0, 0, None)) VehicleServiceResponse(toChannel, PlanetSideGUID(0), VehicleResponse.KickCargo(vehicle, speed=0, delay))
} )
case VehicleResponse.KickCargo(_, _, _)
if player.VehicleSeated.nonEmpty && sessionData.zoning.spawn.deadState == DeadState.Alive =>
sessionData.vehicles.serverVehicleControlVelocity = None
sendResponse(ServerVehicleOverrideMsg(
lock_accelerator=false,
lock_wheel=false,
reverse=false,
unk4=false,
lock_vthrust=0,
lock_strafe=0,
movement_speed=0,
unk8=None
))
case VehicleResponse.StartPlayerSeatedInVehicle(vehicle, _)
if player.VisibleSlots.contains(player.DrawnSlot) =>
val vehicle_guid = vehicle.GUID
sessionData.playerActionsToCancel()
sessionData.vehicles.serverVehicleControlVelocity = Some(0)
sessionData.terminals.CancelAllProximityUnits()
player.DrawnSlot = Player.HandsDownSlot
sendResponse(ObjectHeldMessage(player.GUID, Player.HandsDownSlot, unk1=true))
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.SendResponse(player.GUID, ObjectHeldMessage(player.GUID, player.LastDrawnSlot, unk1=false))
)
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 {
case (mountPoint, _) => vehicle.Actor ! Mountable.TryMount(player, mountPoint)
} }
case VehicleResponse.StartPlayerSeatedInVehicle(vehicle, _) => case VehicleResponse.StartPlayerSeatedInVehicle(vehicle, _) =>
@ -244,30 +257,25 @@ class SessionVehicleHandlers(
sessionData.playerActionsToCancel() sessionData.playerActionsToCancel()
sessionData.vehicles.serverVehicleControlVelocity = Some(0) sessionData.vehicles.serverVehicleControlVelocity = Some(0)
sessionData.terminals.CancelAllProximityUnits() sessionData.terminals.CancelAllProximityUnits()
if (player.VisibleSlots.contains(player.DrawnSlot)) { sendResponse(PlanetsideAttributeMessage(vehicle_guid, attribute_type=22, attribute_value=1L)) //mount points off
player.DrawnSlot = Player.HandsDownSlot sendResponse(PlanetsideAttributeMessage(player.GUID, attribute_type=21, vehicle_guid)) //ownership
sendResponse(ObjectHeldMessage(player.GUID, Player.HandsDownSlot, unk1 = true)) vehicle.MountPoints.find { case (_, mp) => mp.seatIndex == 0 }.collect {
continent.AvatarEvents ! AvatarServiceMessage( case (mountPoint, _) => vehicle.Actor ! Mountable.TryMount(player, mountPoint)
continent.id,
AvatarAction.SendResponse(player.GUID, ObjectHeldMessage(player.GUID, player.LastDrawnSlot, unk1 = false))
)
}
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 1L)) //mount points off
sendResponse(PlanetsideAttributeMessage(player.GUID, 21, vehicle_guid)) //ownership
vehicle.MountPoints.find { case (_, mp) => mp.seatIndex == 0 } match {
case Some((mountPoint, _)) => vehicle.Actor ! Mountable.TryMount(player, mountPoint)
case _ => ;
} }
case VehicleResponse.PlayerSeatedInVehicle(vehicle, _) => case VehicleResponse.PlayerSeatedInVehicle(vehicle, _) =>
val vehicle_guid = vehicle.GUID val vehicle_guid = vehicle.GUID
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 0L)) //mount points on sendResponse(PlanetsideAttributeMessage(vehicle_guid, attribute_type=22, attribute_value=0L)) //mount points on
Vehicles.ReloadAccessPermissions(vehicle, player.Name) Vehicles.ReloadAccessPermissions(vehicle, player.Name)
sessionData.vehicles.ServerVehicleLock(vehicle) sessionData.vehicles.ServerVehicleLock(vehicle)
case VehicleResponse.ServerVehicleOverrideStart(vehicle, _) => case VehicleResponse.ServerVehicleOverrideStart(vehicle, _) =>
val vdef = vehicle.Definition val vdef = vehicle.Definition
sessionData.vehicles.ServerVehicleOverride(vehicle, vdef.AutoPilotSpeed1, if (GlobalDefinitions.isFlightVehicle(vdef)) 1 else 0) sessionData.vehicles.ServerVehicleOverride(
vehicle,
vdef.AutoPilotSpeed1,
flight= if (GlobalDefinitions.isFlightVehicle(vdef)) { 1 } else { 0 }
)
case VehicleResponse.ServerVehicleOverrideEnd(vehicle, _) => case VehicleResponse.ServerVehicleOverrideEnd(vehicle, _) =>
session = session.copy(avatar = avatar.copy(vehicle = Some(vehicle.GUID))) session = session.copy(avatar = avatar.copy(vehicle = Some(vehicle.GUID)))
@ -277,63 +285,77 @@ class SessionVehicleHandlers(
sendResponse(ChatMsg( sendResponse(ChatMsg(
ChatMessageType.CMT_OPEN, ChatMessageType.CMT_OPEN,
wideContents=true, wideContents=true,
"", recipient="",
s"The vehicle spawn where you placed your order is blocked. ${data.getOrElse("")}", s"The vehicle spawn where you placed your order is blocked. ${data.getOrElse("")}",
None note=None
)) ))
case VehicleResponse.PeriodicReminder(_, data) => case VehicleResponse.PeriodicReminder(_, data) =>
val (isType, flag, msg): (ChatMessageType, Boolean, String) = data match { val (isType, flag, msg): (ChatMessageType, Boolean, String) = data match {
case Some(msg: String) case Some(msg: String) if msg.startsWith("@") => (ChatMessageType.UNK_227, false, msg)
if msg.startsWith("@") => (ChatMessageType.UNK_227, false, msg)
case Some(msg: String) => (ChatMessageType.CMT_OPEN, true, msg) case Some(msg: String) => (ChatMessageType.CMT_OPEN, true, msg)
case _ => (ChatMessageType.CMT_OPEN, true, "Your vehicle order has been cancelled.") case _ => (ChatMessageType.CMT_OPEN, true, "Your vehicle order has been cancelled.")
} }
sendResponse(ChatMsg(isType, flag, "", msg, None)) sendResponse(ChatMsg(isType, flag, recipient="", msg, None))
case VehicleResponse.ChangeLoadout(target, old_weapons, added_weapons, old_inventory, new_inventory) => case VehicleResponse.ChangeLoadout(target, oldWeapons, addedWeapons, oldInventory, newInventory)
if player.avatar.vehicle.contains(target) =>
//TODO when vehicle weapons can be changed without visual glitches, rewrite this //TODO when vehicle weapons can be changed without visual glitches, rewrite this
continent.GUID(target) match { continent.GUID(target).collect { case vehicle: Vehicle =>
case Some(vehicle: Vehicle) => import net.psforever.login.WorldSession.boolToInt
if (player.avatar.vehicle.contains(target)) { //owner: must unregister old equipment, and register and install new equipment
import net.psforever.login.WorldSession.boolToInt (oldWeapons ++ oldInventory).foreach {
//owner: must unregister old equipment, and register and install new equipment case (obj, eguid) =>
(old_weapons ++ old_inventory).foreach { sendResponse(ObjectDeleteMessage(eguid, unk1=0))
case (obj, eguid) => TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj))
sendResponse(ObjectDeleteMessage(eguid, 0)) }
TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) sessionData.applyPurchaseTimersBeforePackingLoadout(player, vehicle, addedWeapons ++ newInventory)
} //jammer or unjamm new weapons based on vehicle status
sessionData.applyPurchaseTimersBeforePackingLoadout(player, vehicle, added_weapons ++ new_inventory) val vehicleJammered = vehicle.Jammed
//jammer or unjamm new weapons based on vehicle status addedWeapons
val vehicleJammered = vehicle.Jammed .map { _.obj }
added_weapons .collect {
.map { case jamItem: JammableUnit if jamItem.Jammed != vehicleJammered =>
_.obj jamItem.Jammed = vehicleJammered
} JammableMountedWeapons.JammedWeaponStatus(vehicle.Zone, jamItem, vehicleJammered)
.collect {
case jamItem: JammableUnit if jamItem.Jammed != vehicleJammered =>
jamItem.Jammed = vehicleJammered
JammableMountedWeapons.JammedWeaponStatus(vehicle.Zone, jamItem, vehicleJammered)
}
} else if (sessionData.accessedContainer.map { _.GUID }.contains(target)) {
//external participant: observe changes to equipment
(old_weapons ++ old_inventory).foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, 0)) }
} }
vehicle.PassengerInSeat(player) match { changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory)
case Some(seatNum) =>
//participant: observe changes to equipment
(old_weapons ++ old_inventory).foreach {
case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, 0))
}
sessionData.updateWeaponAtSeatPosition(vehicle, seatNum)
case None =>
//observer: observe changes to external equipment
old_weapons.foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, 0)) }
}
case _ => ;
} }
case _ => ; case VehicleResponse.ChangeLoadout(target, oldWeapons, _, oldInventory, _)
if sessionData.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 VehicleResponse.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 _ => ()
}
}
private def changeLoadoutDeleteOldEquipment(
vehicle: Vehicle,
oldWeapons: Iterable[(Equipment, PlanetSideGUID)],
oldInventory: Iterable[(Equipment, PlanetSideGUID)]
): Unit = {
vehicle.PassengerInSeat(player) match {
case Some(seatNum) =>
//participant: observe changes to equipment
(oldWeapons ++ oldInventory).foreach {
case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, unk1=0))
}
sessionData.updateWeaponAtSeatPosition(vehicle, seatNum)
case None =>
//observer: observe changes to external equipment
oldWeapons.foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, unk1=0)) }
} }
} }
} }

View file

@ -1446,14 +1446,11 @@ class ZoningOperations(
Deployables.Disown(continent, avatar, context.self) Deployables.Disown(continent, avatar, context.self)
spawn.drawDeloyableIcon = spawn.RedrawDeployableIcons //important for when SetCurrentAvatar initializes the UI next zone spawn.drawDeloyableIcon = spawn.RedrawDeployableIcons //important for when SetCurrentAvatar initializes the UI next zone
sessionData.squad.squadSetup = sessionData.squad.ZoneChangeSquadSetup sessionData.squad.squadSetup = sessionData.squad.ZoneChangeSquadSetup
val lastSeen = sessionData.avatarResponse.lastSeenStreamMessage sessionData.avatarResponse.lastSeenStreamMessage = SessionAvatarHandlers.blankUpstreamMessages(65535)
lastSeen.indices.foreach { index =>
lastSeen(index) = 0
}
} }
/** /**
* Attempt to tranfer to the player's faction-specific sanctuary continent. * Attempt to transfer to the player's faction-specific sanctuary continent.
* If the server thinks the player is already on his sanctuary continent, and dead, * If the server thinks the player is already on his sanctuary continent, and dead,
* it will disconnect the player under the assumption that an error has occurred. * it will disconnect the player under the assumption that an error has occurred.
* Eventually, this functionality should support better error-handling before it jumps to the conclusion: * Eventually, this functionality should support better error-handling before it jumps to the conclusion:

View file

@ -145,7 +145,7 @@ class LocalService(zone: Zone) extends Actor {
LocalServiceResponse( LocalServiceResponse(
s"/$forChannel/Local", s"/$forChannel/Local",
player_guid, player_guid,
LocalResponse.SendPlanetsideAttributeMessage(target_guid, attribute_number, attribute_value) LocalResponse.PlanetsideAttribute(target_guid, attribute_number, attribute_value)
) )
) )
case LocalAction.SendGenericObjectActionMessage(player_guid, target_guid, action_number) => case LocalAction.SendGenericObjectActionMessage(player_guid, target_guid, action_number) =>
@ -153,7 +153,7 @@ class LocalService(zone: Zone) extends Actor {
LocalServiceResponse( LocalServiceResponse(
s"/$forChannel/Local", s"/$forChannel/Local",
player_guid, player_guid,
LocalResponse.SendGenericObjectActionMessage(target_guid, action_number) LocalResponse.GenericObjectAction(target_guid, action_number)
) )
) )
@ -162,7 +162,7 @@ class LocalService(zone: Zone) extends Actor {
LocalServiceResponse( LocalServiceResponse(
s"/$forChannel/Local", s"/$forChannel/Local",
player_guid, player_guid,
LocalResponse.SendChatMsg(msg) LocalResponse.ChatMessage(msg)
) )
) )
@ -171,7 +171,7 @@ class LocalService(zone: Zone) extends Actor {
LocalServiceResponse( LocalServiceResponse(
s"/$forChannel/Local", s"/$forChannel/Local",
player_guid, player_guid,
LocalResponse.SendGenericActionMessage(action_number) LocalResponse.GenericActionMessage(action_number)
) )
) )
case LocalAction.RouterTelepadMessage(msg) => case LocalAction.RouterTelepadMessage(msg) =>

View file

@ -38,12 +38,12 @@ object LocalResponse {
final case class HackObject(target_guid: PlanetSideGUID, unk1: Long, unk2: Long) extends Response final case class HackObject(target_guid: PlanetSideGUID, unk1: Long, unk2: Long) extends Response
final case class SendPacket(packet: PlanetSideGamePacket) extends Response final case class SendPacket(packet: PlanetSideGamePacket) extends Response
final case class SendPlanetsideAttributeMessage(target_guid: PlanetSideGUID, attribute_number: PlanetsideAttributeEnum, attribute_value: Long) final case class PlanetsideAttribute(target_guid: PlanetSideGUID, attribute_number: PlanetsideAttributeEnum, attribute_value: Long)
extends Response extends Response
final case class SendGenericObjectActionMessage(target_guid: PlanetSideGUID, action_number: GenericObjectActionEnum) final case class GenericObjectAction(target_guid: PlanetSideGUID, action_number: GenericObjectActionEnum)
extends Response extends Response
final case class SendChatMsg(msg: ChatMsg) extends Response final case class ChatMessage(msg: ChatMsg) extends Response
final case class SendGenericActionMessage(action_num: GenericAction) extends Response final case class GenericActionMessage(action_num: GenericAction) extends Response
final case class LluSpawned(llu: CaptureFlag) extends Response final case class LluSpawned(llu: CaptureFlag) extends Response
final case class LluDespawned(guid: PlanetSideGUID, position: Vector3) extends Response final case class LluDespawned(guid: PlanetSideGUID, position: Vector3) extends Response