further changes to session actor handler classes

This commit is contained in:
Fate-JH 2023-03-13 17:17:44 -04:00
parent f012126fae
commit 39ad0fa997
10 changed files with 467 additions and 330 deletions

View file

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

View file

@ -32,7 +32,10 @@ class SessionAvatarHandlers(
implicit val context: ActorContext
) extends CommonSessionInterfacingFunctionality {
//TODO player characters only exist within a certain range of GUIDs for a given zone; this is overkill
private[support] var lastSeenStreamMessage: Array[Long] = Array.fill[Long](65535)(elem = 0L)
private[support] var lastSeenStreamMessage: Array[SessionAvatarHandlers.LastUpstream] = {
SessionAvatarHandlers.blankUpstreamMessages(65535)
}
private[this] val hidingPlayerRandomizer = new scala.util.Random
/**
* na
@ -47,12 +50,15 @@ class SessionAvatarHandlers(
Service.defaultPlayerGUID
}
val isNotSameTarget = resolvedPlayerGuid != guid
val isSameTarget = !isNotSameTarget
reply match {
/* special messages */
case AvatarResponse.TeardownConnection() =>
log.trace(s"ending ${player.Name}'s old session by event system request (relog)")
context.stop(context.self)
case AvatarResponse.PlayerState(
/* really common messages (very frequently, every life) */
case pstate @ AvatarResponse.PlayerState(
pos,
vel,
yaw,
@ -68,12 +74,10 @@ class SessionAvatarHandlers(
) if isNotSameTarget =>
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)
val r2 = 2 + hidingPlayerRandomizer.nextInt(4000).toFloat
(Vector3(r2, r2, 1f), 0L, 0f)
} else {
val before = lastSeenStreamMessage(guid.guid)
val before = lastSeenStreamMessage(guid.guid).time
val dist = Vector3.DistanceSquared(player.Position, pos)
(pos, now - before, dist)
}
@ -86,157 +90,45 @@ class SessionAvatarHandlers(
yaw,
pitch,
yawUpper,
timestamp=0,
timestamp = 0,
isCrouching,
isJumping,
jumpThrust,
isCloaking
)
)
lastSeenStreamMessage(guid.guid) = now
lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstate), now)
}
case AvatarResponse.SendResponse(msg) =>
sendResponse(msg)
case AvatarResponse.SendResponseTargeted(targetGuid, msg) if resolvedPlayerGuid == targetGuid =>
sendResponse(msg)
case AvatarResponse.Revive(revivalTargetGuid) if resolvedPlayerGuid == revivalTargetGuid =>
log.info(s"No time for rest, ${player.Name}. Back on your feet!")
sessionData.zoning.spawn.reviveTimer.cancel()
sessionData.zoning.spawn.deadState = DeadState.Alive
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.ConcealPlayer() =>
sendResponse(GenericObjectActionMessage(guid, code=9))
case AvatarResponse.EnvironmentalDamage(_, _, _) =>
//TODO damage marker?
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg")
case AvatarResponse.Destroy(victim, killer, weapon, pos) =>
// guid = victim // killer = killer
sendResponse(DestroyMessage(victim, killer, weapon, pos))
case AvatarResponse.DestroyDisplay(killer, victim, method, unk) =>
sendResponse(sessionData.destroyDisplayMessage(killer, victim, method, unk))
// TODO Temporary thing that should go somewhere else and use proper xp values
if (killer.CharId == avatar.id && killer.Faction != victim.Faction) {
avatarActor ! AvatarActor.AwardBep((1000 * Config.app.game.bepRate).toLong, ExperienceType.Normal)
avatarActor ! AvatarActor.AwardCep((100 * Config.app.game.cepRate).toLong)
}
case AvatarResponse.DropItem(pkt) if isNotSameTarget =>
sendResponse(pkt)
case AvatarResponse.EquipmentInHand(pkt) if isNotSameTarget =>
sendResponse(pkt)
case AvatarResponse.GenericObjectAction(objectGuid, actionCode) if isNotSameTarget =>
sendResponse(GenericObjectActionMessage(objectGuid, actionCode))
case AvatarResponse.HitHint(source_guid) if player.isAlive =>
sendResponse(HitHint(source_guid, guid))
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg")
case AvatarResponse.DropSpecialItem() =>
sessionData.dropSpecialSlotItem()
case AvatarResponse.Killed(mount) =>
val cause = (player.LastDamage match {
case Some(reason) => (Some(reason), reason.adversarial)
case None => (None, None)
}) match {
case (_, Some(adversarial)) => adversarial.attacker.Name
case (Some(reason), None) => s"a ${reason.interaction.cause.getClass.getSimpleName}"
case _ => s"an unfortunate circumstance (probably ${player.Sex.pronounObject} own fault)"
}
log.info(s"${player.Name} has died, killed by $cause")
val respawnTimer = 300.seconds
//drop free hand item
player.FreeHand.Equipment.foreach { item =>
DropEquipmentFromInventory(player)(item)
}
sessionData.dropSpecialSlotItem()
sessionData.toggleMaxSpecialState(enable = false)
if (player.LastDamage.flatMap { damage =>
Some(damage.interaction.cause match {
case cause: ExplodingEntityReason => cause.entity.isInstanceOf[VehicleSpawnPad]
case _ => false
}) }.contains(true)
) {
//also, @SVCP_Killed_TooCloseToPadOnCreate^n~ or "... within n meters of pad ..."
sendResponse(ChatMsg(ChatMessageType.UNK_227, wideContents = false, "", "@SVCP_Killed_OnPadOnCreate", None))
}
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()
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel")
if (sessionData.shooting.shotsWhileDead > 0) {
log.warn(
s"KillPlayer/SHOTS_WHILE_DEAD: client of ${avatar.name} fired ${sessionData.shooting.shotsWhileDead} rounds while character was dead on server"
)
sessionData.shooting.shotsWhileDead = 0
}
sessionData.zoning.spawn.reviveTimer.cancel()
if (player.death_by == 0) {
sessionData.zoning.spawn.reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer) {
sessionData.cluster ! ICS.GetRandomSpawnPoint(
Zones.sanctuaryZoneNumber(player.Faction),
player.Faction,
Seq(SpawnGroup.Sanctuary),
context.self
)
}
} else {
sessionData.zoning.spawn.HandleReleaseAvatar(player, continent)
}
AvatarActor.savePlayerLocation(player)
sessionData.renewCharSavedTimer(fixedLen = 1800L, varLen = 0L)
case AvatarResponse.LoadPlayer(pkt) if isNotSameTarget =>
sendResponse(pkt)
case AvatarResponse.LoadProjectile(pkt) if isNotSameTarget =>
sendResponse(pkt)
case AvatarResponse.ObjectDelete(itemGuid, unk) if isNotSameTarget =>
sendResponse(ObjectDeleteMessage(itemGuid, unk))
case AvatarResponse.ObjectHeld(slot, _) if resolvedPlayerGuid == guid && slot > -1 =>
case AvatarResponse.ObjectHeld(slot, _)
if isSameTarget && player.VisibleSlots.contains(slot) =>
sendResponse(ObjectHeldMessage(guid, slot, unk1=true))
//Stop using proximity terminals if player unholsters a weapon
if (player.VisibleSlots.contains(slot)) {
continent.GUID(sessionData.terminals.usingMedicalTerminal).collect {
case term: Terminal with ProximityUnit =>
sessionData.terminals.StopUsingProximityUnit(term)
}
continent.GUID(sessionData.terminals.usingMedicalTerminal).collect {
case term: Terminal with ProximityUnit => sessionData.terminals.StopUsingProximityUnit(term)
}
case AvatarResponse.ObjectHeld(slot, _)
if isSameTarget && slot > -1 =>
sendResponse(ObjectHeldMessage(guid, slot, unk1=true))
case AvatarResponse.ObjectHeld(_, _)
if isSameTarget => ()
case AvatarResponse.ObjectHeld(_, previousSlot) =>
sendResponse(ObjectHeldMessage(guid, previousSlot, unk1=false))
case AvatarResponse.OxygenState(player, vehicle) =>
sendResponse(OxygenStateMessage(
DrowningTarget(player.guid, player.progress, player.state),
vehicle.flatMap { vinfo => Some(DrowningTarget(vinfo.guid, vinfo.progress, vinfo.state)) }
))
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))
@ -244,73 +136,43 @@ class SessionAvatarHandlers(
case AvatarResponse.PlanetsideAttributeToAll(attributeType, attributeValue) =>
sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue))
case AvatarResponse.PlanetsideAttributeSelf(attributeType, attributeValue) if resolvedPlayerGuid == guid =>
case AvatarResponse.PlanetsideAttributeSelf(attributeType, attributeValue) if isSameTarget =>
sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue))
case AvatarResponse.ProjectileExplodes(projectileGuid, projectile) =>
sendResponse(
ProjectileStateMessage(
projectileGuid,
projectile.Position,
shot_vel = Vector3.Zero,
projectile.Orientation,
sequence_num=0,
end=true,
hit_target_guid=PlanetSideGUID(0)
)
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")
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) =>
// guid = victim // killer = killer
sendResponse(DestroyMessage(victim, killer, weapon, pos))
case AvatarResponse.DestroyDisplay(killer, victim, method, unk) =>
sendResponse(sessionData.destroyDisplayMessage(killer, victim, method, unk))
case AvatarResponse.TerminalOrderResult(terminalGuid, action, result)
if result && (action == TransactionType.Buy || action == TransactionType.Loadout) =>
sendResponse(ItemTransactionResultMessage(terminalGuid, action, result))
sessionData.terminals.lastTerminalOrderFulfillment = true
AvatarActor.savePlayerData(player)
sessionData.renewCharSavedTimer(
Config.app.game.savedMsg.interruptedByAction.fixed,
Config.app.game.savedMsg.interruptedByAction.variable
)
sendResponse(ObjectDeleteMessage(projectileGuid, unk1=2))
case AvatarResponse.ProjectileAutoLockAwareness(mode) =>
sendResponse(GenericActionMessage(mode))
case AvatarResponse.ProjectileState(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid) if isNotSameTarget =>
sendResponse(ProjectileStateMessage(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid))
case AvatarResponse.PutDownFDU(target) if isNotSameTarget =>
sendResponse(GenericObjectActionMessage(target, code=53))
case AvatarResponse.Release(tplayer) if isNotSameTarget =>
sessionData.zoning.spawn.DepictPlayerAsCorpse(tplayer)
case AvatarResponse.Reload(itemGuid) if isNotSameTarget =>
sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0))
case AvatarResponse.SetEmpire(objectGuid, faction) if isNotSameTarget =>
sendResponse(SetEmpireMessage(objectGuid, faction))
case AvatarResponse.StowEquipment(target, slot, item) if isNotSameTarget =>
val definition = item.Definition
sendResponse(
ObjectCreateDetailedMessage(
definition.ObjectId,
item.GUID,
ObjectCreateMessageParent(target, slot),
definition.Packet.DetailedConstructorData(item).get
)
)
case AvatarResponse.WeaponDryFire(weaponGuid) if isNotSameTarget =>
continent.GUID(weaponGuid).collect {
case tool: Tool if tool.Magazine == 0 =>
// check that the magazine is still empty before sending WeaponDryFireMessage
// if it has been reloaded since then, other clients not see it firing
sendResponse(WeaponDryFireMessage(weaponGuid))
case _ =>
sendResponse(WeaponDryFireMessage(weaponGuid))
}
case AvatarResponse.TerminalOrderResult(terminalGuid, action, result) =>
sendResponse(ItemTransactionResultMessage(terminalGuid, 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,
@ -425,13 +287,13 @@ class SessionAvatarHandlers(
DropLeftovers(player)(drops)
case AvatarResponse.ChangeLoadout(target, armor, exosuit, subtype, slot, _, oldHolsters, _, _, _, _) =>
//redraw handled by callbacks
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)) }
//redraw handled by callback
case AvatarResponse.UseKit(kguid, kObjId) =>
sendResponse(
@ -456,9 +318,191 @@ class SessionAvatarHandlers(
case AvatarResponse.KitNotUsed(_, msg) =>
sessionData.kitToBeUsed = None
sendResponse(ChatMsg(ChatMessageType.UNK_225, wideContents=false, recipient="", msg, note=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) =>
//log and chat messages
val cause = player.LastDamage.flatMap { damage =>
damage.interaction.cause match {
case cause: ExplodingEntityReason if cause.entity.isInstanceOf[VehicleSpawnPad] =>
//also, @SVCP_Killed_TooCloseToPadOnCreate^n~ or "... within n meters of pad ..."
sendResponse(ChatMsg(ChatMessageType.UNK_227, "@SVCP_Killed_OnPadOnCreate"))
case _ => ()
}
damage match {
case damage if damage.adversarial.nonEmpty => Some(damage.adversarial.get.attacker.Name)
case damage => Some(s"a ${damage.interaction.cause.getClass.getSimpleName}")
}
}.getOrElse { s"an unfortunate circumstance (probably ${player.Sex.pronounObject} own fault)" }
log.info(s"${player.Name} has died, killed by $cause")
if (sessionData.shooting.shotsWhileDead > 0) {
log.warn(
s"SHOTS_WHILE_DEAD: client of ${avatar.name} fired ${sessionData.shooting.shotsWhileDead} rounds while character was dead on server"
)
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()
if (player.death_by == 0) {
sessionData.zoning.spawn.reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer) {
sessionData.cluster ! ICS.GetRandomSpawnPoint(
Zones.sanctuaryZoneNumber(player.Faction),
player.Faction,
Seq(SpawnGroup.Sanctuary),
context.self
)
}
} else {
sessionData.zoning.spawn.HandleReleaseAvatar(player, continent)
}
case AvatarResponse.Release(tplayer) if isNotSameTarget =>
sessionData.zoning.spawn.DepictPlayerAsCorpse(tplayer)
case AvatarResponse.Revive(revivalTargetGuid) if resolvedPlayerGuid == revivalTargetGuid =>
log.info(s"No time for rest, ${player.Name}. Back on your feet!")
sessionData.zoning.spawn.reviveTimer.cancel()
sessionData.zoning.spawn.deadState = DeadState.Alive
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)
)
/* uncommon messages (utility, or once in a while) */
case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data)
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.ChangeFireMode(itemGuid, mode) if isNotSameTarget =>
sendResponse(ChangeFireModeMessage(itemGuid, mode))
case AvatarResponse.ConcealPlayer() =>
sendResponse(GenericObjectActionMessage(guid, code=9))
case AvatarResponse.EnvironmentalDamage(_, _, _) =>
//TODO damage marker?
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg")
case AvatarResponse.DropItem(pkt) if isNotSameTarget =>
sendResponse(pkt)
case AvatarResponse.ObjectDelete(itemGuid, unk) if isNotSameTarget =>
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) =>
sendResponse(OxygenStateMessage(
DrowningTarget(player.guid, player.progress, player.state),
vehicle.flatMap { vinfo => Some(DrowningTarget(vinfo.guid, vinfo.progress, vinfo.state)) }
))
case AvatarResponse.LoadProjectile(pkt) if isNotSameTarget =>
sendResponse(pkt)
case AvatarResponse.ProjectileState(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid) if isNotSameTarget =>
sendResponse(ProjectileStateMessage(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid))
case AvatarResponse.ProjectileExplodes(projectileGuid, projectile) =>
sendResponse(
ProjectileStateMessage(
projectileGuid,
projectile.Position,
shot_vel = Vector3.Zero,
projectile.Orientation,
sequence_num=0,
end=true,
hit_target_guid=PlanetSideGUID(0)
)
)
sendResponse(ObjectDeleteMessage(projectileGuid, unk1=2))
case AvatarResponse.ProjectileAutoLockAwareness(mode) =>
sendResponse(GenericActionMessage(mode))
case AvatarResponse.PutDownFDU(target) if isNotSameTarget =>
sendResponse(GenericObjectActionMessage(target, code=53))
case AvatarResponse.StowEquipment(target, slot, item) if isNotSameTarget =>
val definition = item.Definition
sendResponse(
ObjectCreateDetailedMessage(
definition.ObjectId,
item.GUID,
ObjectCreateMessageParent(target, slot),
definition.Packet.DetailedConstructorData(item).get
)
)
case AvatarResponse.WeaponDryFire(weaponGuid) if isNotSameTarget =>
continent.GUID(weaponGuid).collect {
case tool: Tool if tool.Magazine == 0 =>
// check that the magazine is still empty before sending WeaponDryFireMessage
// if it has been reloaded since then, other clients not see it firing
sendResponse(WeaponDryFireMessage(weaponGuid))
case _ =>
sendResponse(WeaponDryFireMessage(weaponGuid))
}
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 => ()
}
val wepInHand: Boolean = player.Slot(player.DrawnSlot).Equipment match {
case Some(item) => item.Definition == GlobalDefinitions.bolt_driver
case _ => false
}
val eagleEye: Boolean = canSeeReallyFar
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.PlayerState(
@ -276,7 +273,7 @@ class SessionData(
jumpThrust,
isCloaking,
player.spectator,
wepInHand
eagleEye
)
)
squad.updateSquad()
@ -2910,6 +2907,23 @@ class SessionData(
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 = {
charSaved()
renewCharSavedTimer(fixedLen, varLen)

View file

@ -114,16 +114,16 @@ class SessionLocalHandlers(
case LocalResponse.HackObject(targetGuid, unk1, unk2) =>
HackObject(targetGuid, unk1, unk2)
case LocalResponse.SendPlanetsideAttributeMessage(targetGuid, attributeType, attributeValue) =>
case LocalResponse.PlanetsideAttribute(targetGuid, attributeType, attributeValue) =>
SendPlanetsideAttributeMessage(targetGuid, attributeType, attributeValue)
case LocalResponse.SendGenericObjectActionMessage(targetGuid, actionNumber) =>
case LocalResponse.GenericObjectAction(targetGuid, actionNumber) =>
sendResponse(GenericObjectActionMessage(targetGuid, actionNumber))
case LocalResponse.SendGenericActionMessage(actionNumber) =>
case LocalResponse.GenericActionMessage(actionNumber) =>
sendResponse(GenericActionMessage(actionNumber))
case LocalResponse.SendChatMsg(msg) =>
case LocalResponse.ChatMessage(msg) =>
sendResponse(msg)
case LocalResponse.SendPacket(packet) =>
@ -142,7 +142,7 @@ class SessionLocalHandlers(
sendResponse(TriggerSoundMessage(TriggeredSound.LLUDeconstruct, position, unk=20, volume=0.8000001f))
sendResponse(ObjectDeleteMessage(lluGuid, unk1=0))
// If the player was holding the LLU, remove it from their tracked special item slot
sessionData.specialItemSlotGuid.foreach { case guid if guid == lluGuid =>
sessionData.specialItemSlotGuid.collect { case guid if guid == lluGuid =>
sessionData.specialItemSlotGuid = None
player.Carrying = None
}
@ -198,20 +198,19 @@ class SessionLocalHandlers(
case LocalResponse.TriggerSound(sound, pos, unk, volume) =>
sendResponse(TriggerSoundMessage(sound, pos, unk, volume))
case LocalResponse.UpdateForceDomeStatus(buildingGuid, activated) if activated =>
case LocalResponse.UpdateForceDomeStatus(buildingGuid, true) =>
sendResponse(GenericObjectActionMessage(buildingGuid, 11))
case LocalResponse.UpdateForceDomeStatus(buildingGuid, _) =>
case LocalResponse.UpdateForceDomeStatus(buildingGuid, false) =>
sendResponse(GenericObjectActionMessage(buildingGuid, 12))
case LocalResponse.RechargeVehicleWeapon(vehicleGuid, weaponGuid) if resolvedPlayerGuid == guid =>
continent.GUID(vehicleGuid).collect { case vehicle: MountableWeapons =>
vehicle.PassengerInSeat(player).collect { seat_num =>
vehicle.WeaponControlledFromSeat(seat_num).collect { case weapon: Tool if weapon.GUID == weaponGuid =>
sendResponse(InventoryStateMessage(weapon.AmmoSlot.Box.GUID, weapon.GUID, weapon.Magazine))
}
continent.GUID(vehicleGuid)
.collect { case vehicle: MountableWeapons => (vehicle, vehicle.PassengerInSeat(player)) }
.collect { case (vehicle, Some(seat_num)) => vehicle.WeaponControlledFromSeat(seat_num) }
.collect { case weapon: Tool if weapon.GUID == weaponGuid =>
sendResponse(InventoryStateMessage(weapon.AmmoSlot.Box.GUID, weapon.GUID, weapon.Magazine))
}
}
case _ => ()
}

View file

@ -46,54 +46,118 @@ class SessionMountHandlers(
MountingAction(tplayer, obj, seatNumber)
sessionData.keepAliveFunc = sessionData.keepAlivePersistence
case Mountable.CanMount(obj: Vehicle, seatNumber, _) =>
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
if obj.Definition == GlobalDefinitions.ant =>
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
log.info(s"${player.Name} mounts the ${obj.Definition.Name} in ${
obj.SeatPermissionGroup(seatNumber) match {
case Some(AccessPermissionGroup.Driver) => "the driver seat"
case Some(seatType) => s"a $seatType seat (#$seatNumber)"
case None => "a seat"
}
}")
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))
if (obj.Definition == GlobalDefinitions.ant) {
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=45, obj.NtuCapacitorScaled))
}
if (obj.Definition.MaxCapacitor > 0) {
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=113, obj.Capacitor))
}
if (seatNumber == 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, code=11))
} else if (obj.WeaponControlledFromSeat(seatNumber).isEmpty) {
sessionData.keepAliveFunc = sessionData.keepAlivePersistence
}
sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=45, obj.NtuCapacitorScaled))
sendResponse(GenericObjectActionMessage(obj_guid, code=11))
MountingAction(tplayer, obj, seatNumber)
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
if obj.Definition == GlobalDefinitions.quadstealth =>
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))
//exclusive to the wraith, cloak state matches the cloak state of the driver
//phantasm doesn't uncloak if the driver is uncloaked and no other vehicle cloaks
obj.Cloaked = tplayer.Cloaked
sendResponse(GenericObjectActionMessage(obj_guid, code=11))
sessionData.accessContainer(obj)
MountingAction(tplayer, obj, seatNumber)
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: FacilityTurret, seatNumber, _) =>
case Mountable.CanMount(obj: Vehicle, seatNumber, _)
if seatNumber == 0 =>
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount")
if (!obj.isUpgrading) {
log.info(s"${player.Name} mounts the ${obj.Definition.Name}")
if (obj.Definition == GlobalDefinitions.vanu_sentry_turret) {
obj.Zone.LocalEvents ! LocalServiceMessage(obj.Zone.id, LocalAction.SetEmpire(obj.GUID, player.Faction))
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"
}
sendResponse(PlanetsideAttributeMessage(obj.GUID, attribute_type=0, obj.Health))
sessionData.updateWeaponAtSeatPosition(obj, seatNumber)
MountingAction(tplayer, obj, seatNumber)
} else {
log.warn(
s"MountVehicleMsg: ${tplayer.Name} wants to mount turret ${obj.GUID.guid}, but needs to wait until it finishes updating"
)
}
} 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")

View file

@ -28,19 +28,19 @@ class SessionTerminalHandlers(
/* packets */
def handleItemTransaction(pkt: ItemTransactionMessage): Unit = {
val ItemTransactionMessage(terminalGuid, _, _, _, _, _) = pkt
val ItemTransactionMessage(terminalGuid, transactionType, _, itemName, _, _) = pkt
continent.GUID(terminalGuid) match {
case Some(term: Terminal) =>
if (lastTerminalOrderFulfillment) {
log.trace(s"ItemTransactionMessage: ${player.Name} is submitting an order")
lastTerminalOrderFulfillment = false
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
term.Actor ! Terminal.Request(player, pkt)
}
case Some(obj: PlanetSideGameObject) =>
log.error(s"ItemTransaction: $obj is not a terminal, ${player.Name}")
case Some(term: Terminal) if lastTerminalOrderFulfillment =>
log.info(s"${player.Name} is submitting an order - $transactionType of $itemName")
lastTerminalOrderFulfillment = false
sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
term.Actor ! Terminal.Request(player, pkt)
case Some(_: Terminal) =>
log.warn(s"Please Wait until your previous order has been fulfilled, ${player.Name}")
case Some(obj) =>
log.error(s"ItemTransaction: ${obj.Definition.Name} is not a terminal, ${player.Name}")
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}")
}
}
@ -50,9 +50,9 @@ class SessionTerminalHandlers(
case Some(obj: Terminal with ProximityUnit) =>
HandleProximityTerminalUse(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 =>
log.error(s"ProximityTerminalUse: ${player.Name} can not find an object with guid $objectGuid")
log.error(s"ProximityTerminalUse: ${player.Name} can not find an object with guid ${objectGuid.guid}")
}
}
@ -66,7 +66,8 @@ class SessionTerminalHandlers(
*/
def handle(tplayer: Player, msg: ItemTransactionMessage, order: Terminal.Exchange): Unit = {
order match {
case Terminal.BuyEquipment(item) if tplayer.avatar.purchaseCooldown(item.Definition).nonEmpty =>
case Terminal.BuyEquipment(item)
if tplayer.avatar.purchaseCooldown(item.Definition).nonEmpty =>
lastTerminalOrderFulfillment = true
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false))
@ -100,19 +101,16 @@ class SessionTerminalHandlers(
avatarActor ! AvatarActor.SellImplant(msg.terminal_guid, implant)
lastTerminalOrderFulfillment = true
case Terminal.BuyVehicle(vehicle, _, _) if tplayer.avatar.purchaseCooldown(vehicle.Definition).nonEmpty =>
case Terminal.BuyVehicle(vehicle, _, _)
if tplayer.avatar.purchaseCooldown(vehicle.Definition).nonEmpty =>
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false))
lastTerminalOrderFulfillment = true
case Terminal.BuyVehicle(vehicle, weapons, trunk) =>
continent.map.terminalToSpawnPad
.find { case (termid, _) => termid == msg.terminal_guid.guid }
.map {
case (a: Int, b: Int) => (continent.GUID(a), continent.GUID(b))
case _ => (None, None)
}
.get match {
case (Some(term: Terminal), Some(pad: VehicleSpawnPad)) =>
.map { case (a: Int, b: Int) => (continent.GUID(a), continent.GUID(b)) }
.collect { case (Some(term: Terminal), Some(pad: VehicleSpawnPad)) =>
avatarActor ! AvatarActor.UpdatePurchaseTime(vehicle.Definition)
vehicle.Faction = tplayer.Faction
vehicle.Position = pad.Position
@ -143,27 +141,30 @@ class SessionTerminalHandlers(
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))
}.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
}
case Terminal.NoDeal() =>
val order: String = if (msg == null) {
"missing order"
} else {
s"${msg.transaction_type} order"
}
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
case _ =>
val transaction = msg.transaction_type
log.warn(s"n/a: ${tplayer.Name} made a $transaction request but terminal#${msg.terminal_guid.guid} is missing or wrong")
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, transaction, success = false))
val terminal = msg.terminal_guid.guid
continent.GUID(terminal) match {
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
}
}
@ -239,7 +240,6 @@ class SessionTerminalHandlers(
*/
def StartUsingProximityUnit(terminal: Terminal with ProximityUnit, target: PlanetSideGameObject): Unit = {
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) {
target match {
case _: Player =>
@ -267,7 +267,7 @@ class SessionTerminalHandlers(
*/
def StopUsingProximityUnit(terminal: Terminal with ProximityUnit): Unit = {
FindProximityUnitTargetsInScope(terminal).foreach { target =>
LocalStopUsingProximityUnit(terminal, target)
LocalStopUsingProximityUnit(terminal)
terminal.Actor ! CommonMessages.Unuse(player, Some(target))
}
}
@ -280,7 +280,7 @@ class SessionTerminalHandlers(
* Other sorts of proximity-based units are put on a timer.
* @param terminal the proximity-based unit
*/
def LocalStopUsingProximityUnit(terminal: Terminal with ProximityUnit, target: PlanetSideGameObject): Unit = {
def LocalStopUsingProximityUnit(terminal: Terminal with ProximityUnit): Unit = {
ForgetAllProximityTerminals(terminal.GUID)
}
@ -291,12 +291,23 @@ class SessionTerminalHandlers(
* @see `postStop`
*/
def CancelAllProximityUnits(): Unit = {
continent.GUID(usingMedicalTerminal).collect {
usingMedicalTerminal.foreach { CancelAllProximityUnits }
}
/**
* 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 =>
terminal.Actor ! CommonMessages.Unuse(player, Some(target))
)
ForgetAllProximityTerminals(usingMedicalTerminal.get)
ForgetAllProximityTerminals(guid)
}
}

View file

@ -163,10 +163,8 @@ class SessionVehicleHandlers(
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.PlanetsideAttribute(vehicleGuid, attributeType, attributeValue) if isNotSameTarget =>
sendResponse(PlanetsideAttributeMessage(vehicleGuid, attributeType, attributeValue))
case VehicleResponse.ResetSpawnPad(padGuid) =>
sendResponse(GenericObjectActionMessage(padGuid, code=23))
@ -201,7 +199,7 @@ class SessionVehicleHandlers(
case VehicleResponse.KickCargo(vehicle, speed, delay)
if player.VehicleSeated.nonEmpty && sessionData.zoning.spawn.deadState == DeadState.Alive && speed > 0 =>
val strafe = if (Vehicles.CargoOrientation(vehicle) == 1) { 2 } else { 1 }
val strafe = 1 + Vehicles.CargoOrientation(vehicle)
val reverseSpeed = if (strafe > 1) { 0 } else { speed }
//strafe or reverse, not both
sessionData.vehicles.serverVehicleControlVelocity = Some(reverseSpeed)
@ -236,19 +234,29 @@ class SessionVehicleHandlers(
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, _) =>
val vehicle_guid = vehicle.GUID
sessionData.playerActionsToCancel()
sessionData.vehicles.serverVehicleControlVelocity = Some(0)
sessionData.terminals.CancelAllProximityUnits()
if (player.VisibleSlots.contains(player.DrawnSlot)) {
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 {

View file

@ -1446,14 +1446,11 @@ class ZoningOperations(
Deployables.Disown(continent, avatar, context.self)
spawn.drawDeloyableIcon = spawn.RedrawDeployableIcons //important for when SetCurrentAvatar initializes the UI next zone
sessionData.squad.squadSetup = sessionData.squad.ZoneChangeSquadSetup
val lastSeen = sessionData.avatarResponse.lastSeenStreamMessage
lastSeen.indices.foreach { index =>
lastSeen(index) = 0
}
sessionData.avatarResponse.lastSeenStreamMessage = SessionAvatarHandlers.blankUpstreamMessages(65535)
}
/**
* 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,
* 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:

View file

@ -145,7 +145,7 @@ class LocalService(zone: Zone) extends Actor {
LocalServiceResponse(
s"/$forChannel/Local",
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) =>
@ -153,7 +153,7 @@ class LocalService(zone: Zone) extends Actor {
LocalServiceResponse(
s"/$forChannel/Local",
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(
s"/$forChannel/Local",
player_guid,
LocalResponse.SendChatMsg(msg)
LocalResponse.ChatMessage(msg)
)
)
@ -171,7 +171,7 @@ class LocalService(zone: Zone) extends Actor {
LocalServiceResponse(
s"/$forChannel/Local",
player_guid,
LocalResponse.SendGenericActionMessage(action_number)
LocalResponse.GenericActionMessage(action_number)
)
)
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 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
final case class SendGenericObjectActionMessage(target_guid: PlanetSideGUID, action_number: GenericObjectActionEnum)
final case class GenericObjectAction(target_guid: PlanetSideGUID, action_number: GenericObjectActionEnum)
extends Response
final case class SendChatMsg(msg: ChatMsg) extends Response
final case class SendGenericActionMessage(action_num: GenericAction) extends Response
final case class ChatMessage(msg: ChatMsg) extends Response
final case class GenericActionMessage(action_num: GenericAction) extends Response
final case class LluSpawned(llu: CaptureFlag) extends Response
final case class LluDespawned(guid: PlanetSideGUID, position: Vector3) extends Response