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) 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

@ -32,7 +32,10 @@ class SessionAvatarHandlers(
implicit val context: ActorContext implicit val context: ActorContext
) extends CommonSessionInterfacingFunctionality { ) extends CommonSessionInterfacingFunctionality {
//TODO player characters only exist within a certain range of GUIDs for a given zone; this is overkill //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 * na
@ -47,12 +50,15 @@ class SessionAvatarHandlers(
Service.defaultPlayerGUID Service.defaultPlayerGUID
} }
val isNotSameTarget = resolvedPlayerGuid != guid 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.PlayerState( /* really common messages (very frequently, every life) */
case pstate @ AvatarResponse.PlayerState(
pos, pos,
vel, vel,
yaw, yaw,
@ -68,12 +74,10 @@ class SessionAvatarHandlers(
) if isNotSameTarget => ) if isNotSameTarget =>
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
val (location, time, distanceSq): (Vector3, Long, Float) = if (spectating) { val (location, time, distanceSq): (Vector3, Long, Float) = if (spectating) {
val r = new scala.util.Random val r2 = 2 + hidingPlayerRandomizer.nextInt(4000).toFloat
val r1 = 2 + r.nextInt(30).toFloat (Vector3(r2, r2, 1f), 0L, 0f)
val r2 = 2 + r.nextInt(4000).toFloat
(Vector3(r2, r2, r1), 0L, 0f)
} else { } else {
val before = lastSeenStreamMessage(guid.guid) val before = lastSeenStreamMessage(guid.guid).time
val dist = Vector3.DistanceSquared(player.Position, pos) val dist = Vector3.DistanceSquared(player.Position, pos)
(pos, now - before, dist) (pos, now - before, dist)
} }
@ -86,157 +90,45 @@ class SessionAvatarHandlers(
yaw, yaw,
pitch, pitch,
yawUpper, yawUpper,
timestamp=0, timestamp = 0,
isCrouching, isCrouching,
isJumping, isJumping,
jumpThrust, jumpThrust,
isCloaking isCloaking
) )
) )
lastSeenStreamMessage(guid.guid) = now lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstate), now)
} }
case AvatarResponse.SendResponse(msg) => case AvatarResponse.ObjectHeld(slot, _)
sendResponse(msg) if isSameTarget && player.VisibleSlots.contains(slot) =>
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 =>
sendResponse(ObjectHeldMessage(guid, slot, unk1=true)) sendResponse(ObjectHeldMessage(guid, slot, unk1=true))
//Stop using proximity terminals if player unholsters a weapon //Stop using proximity terminals if player unholsters a weapon
if (player.VisibleSlots.contains(slot)) { continent.GUID(sessionData.terminals.usingMedicalTerminal).collect {
continent.GUID(sessionData.terminals.usingMedicalTerminal).collect { case term: Terminal with ProximityUnit => sessionData.terminals.StopUsingProximityUnit(term)
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) => case AvatarResponse.ObjectHeld(_, previousSlot) =>
sendResponse(ObjectHeldMessage(guid, previousSlot, unk1=false)) sendResponse(ObjectHeldMessage(guid, previousSlot, unk1=false))
case AvatarResponse.OxygenState(player, vehicle) => case AvatarResponse.ChangeFireState_Start(weaponGuid) if isNotSameTarget =>
sendResponse(OxygenStateMessage( sendResponse(ChangeFireStateMessage_Start(weaponGuid))
DrowningTarget(player.guid, player.progress, player.state),
vehicle.flatMap { vinfo => Some(DrowningTarget(vinfo.guid, vinfo.progress, vinfo.state)) } 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 => case AvatarResponse.PlanetsideAttribute(attributeType, attributeValue) if isNotSameTarget =>
sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue)) sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue))
@ -244,73 +136,43 @@ class SessionAvatarHandlers(
case AvatarResponse.PlanetsideAttributeToAll(attributeType, attributeValue) => case AvatarResponse.PlanetsideAttributeToAll(attributeType, attributeValue) =>
sendResponse(PlanetsideAttributeMessage(guid, 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)) sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue))
case AvatarResponse.ProjectileExplodes(projectileGuid, projectile) => case AvatarResponse.GenericObjectAction(objectGuid, actionCode) if isNotSameTarget =>
sendResponse( sendResponse(GenericObjectActionMessage(objectGuid, actionCode))
ProjectileStateMessage(
projectileGuid, case AvatarResponse.HitHint(sourceGuid) if player.isAlive =>
projectile.Position, sendResponse(HitHint(sourceGuid, guid))
shot_vel = Vector3.Zero, sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg")
projectile.Orientation,
sequence_num=0, case AvatarResponse.DestroyDisplay(killer, victim, method, unk)
end=true, if killer.CharId == avatar.id && killer.Faction != victim.Faction =>
hit_target_guid=PlanetSideGUID(0) // 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) => case AvatarResponse.TerminalOrderResult(terminalGuid, action, result) =>
sendResponse(ItemTransactionResultMessage(terminalGuid, action, result)) sendResponse(ItemTransactionResultMessage(terminalGuid, action, result))
sessionData.terminals.lastTerminalOrderFulfillment = true 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( case AvatarResponse.ChangeExosuit(
target, target,
@ -425,13 +287,13 @@ class SessionAvatarHandlers(
DropLeftovers(player)(drops) DropLeftovers(player)(drops)
case AvatarResponse.ChangeLoadout(target, armor, exosuit, subtype, slot, _, oldHolsters, _, _, _, _) => case AvatarResponse.ChangeLoadout(target, armor, exosuit, subtype, slot, _, oldHolsters, _, _, _, _) =>
//redraw handled by callbacks
sendResponse(ArmorChangedMessage(target, exosuit, subtype)) sendResponse(ArmorChangedMessage(target, exosuit, subtype))
sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor))
//happening to some other player //happening to some other player
sendResponse(ObjectHeldMessage(target, slot, unk1=false)) sendResponse(ObjectHeldMessage(target, slot, unk1=false))
//cleanup //cleanup
oldHolsters.foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) } oldHolsters.foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) }
//redraw handled by callback
case AvatarResponse.UseKit(kguid, kObjId) => case AvatarResponse.UseKit(kguid, kObjId) =>
sendResponse( sendResponse(
@ -456,9 +318,191 @@ class SessionAvatarHandlers(
case AvatarResponse.KitNotUsed(_, msg) => case AvatarResponse.KitNotUsed(_, msg) =>
sessionData.kitToBeUsed = None 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 _ => () 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

@ -114,16 +114,16 @@ class SessionLocalHandlers(
case LocalResponse.HackObject(targetGuid, unk1, unk2) => case LocalResponse.HackObject(targetGuid, unk1, unk2) =>
HackObject(targetGuid, unk1, unk2) HackObject(targetGuid, unk1, unk2)
case LocalResponse.SendPlanetsideAttributeMessage(targetGuid, attributeType, attributeValue) => case LocalResponse.PlanetsideAttribute(targetGuid, attributeType, attributeValue) =>
SendPlanetsideAttributeMessage(targetGuid, attributeType, attributeValue) SendPlanetsideAttributeMessage(targetGuid, attributeType, attributeValue)
case LocalResponse.SendGenericObjectActionMessage(targetGuid, actionNumber) => case LocalResponse.GenericObjectAction(targetGuid, actionNumber) =>
sendResponse(GenericObjectActionMessage(targetGuid, actionNumber)) sendResponse(GenericObjectActionMessage(targetGuid, actionNumber))
case LocalResponse.SendGenericActionMessage(actionNumber) => case LocalResponse.GenericActionMessage(actionNumber) =>
sendResponse(GenericActionMessage(actionNumber)) sendResponse(GenericActionMessage(actionNumber))
case LocalResponse.SendChatMsg(msg) => case LocalResponse.ChatMessage(msg) =>
sendResponse(msg) sendResponse(msg)
case LocalResponse.SendPacket(packet) => case LocalResponse.SendPacket(packet) =>
@ -142,7 +142,7 @@ class SessionLocalHandlers(
sendResponse(TriggerSoundMessage(TriggeredSound.LLUDeconstruct, position, unk=20, volume=0.8000001f)) sendResponse(TriggerSoundMessage(TriggeredSound.LLUDeconstruct, position, unk=20, volume=0.8000001f))
sendResponse(ObjectDeleteMessage(lluGuid, unk1=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.foreach { case guid if guid == lluGuid => sessionData.specialItemSlotGuid.collect { case guid if guid == lluGuid =>
sessionData.specialItemSlotGuid = None sessionData.specialItemSlotGuid = None
player.Carrying = None player.Carrying = None
} }
@ -198,20 +198,19 @@ class SessionLocalHandlers(
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(buildingGuid, activated) if activated => case LocalResponse.UpdateForceDomeStatus(buildingGuid, true) =>
sendResponse(GenericObjectActionMessage(buildingGuid, 11)) sendResponse(GenericObjectActionMessage(buildingGuid, 11))
case LocalResponse.UpdateForceDomeStatus(buildingGuid, _) => case LocalResponse.UpdateForceDomeStatus(buildingGuid, false) =>
sendResponse(GenericObjectActionMessage(buildingGuid, 12)) sendResponse(GenericObjectActionMessage(buildingGuid, 12))
case LocalResponse.RechargeVehicleWeapon(vehicleGuid, weaponGuid) if resolvedPlayerGuid == guid => case LocalResponse.RechargeVehicleWeapon(vehicleGuid, weaponGuid) if resolvedPlayerGuid == guid =>
continent.GUID(vehicleGuid).collect { case vehicle: MountableWeapons => continent.GUID(vehicleGuid)
vehicle.PassengerInSeat(player).collect { seat_num => .collect { case vehicle: MountableWeapons => (vehicle, vehicle.PassengerInSeat(player)) }
vehicle.WeaponControlledFromSeat(seat_num).collect { case weapon: Tool if weapon.GUID == weaponGuid => .collect { case (vehicle, Some(seat_num)) => vehicle.WeaponControlledFromSeat(seat_num) }
sendResponse(InventoryStateMessage(weapon.AmmoSlot.Box.GUID, weapon.GUID, weapon.Magazine)) .collect { case weapon: Tool if weapon.GUID == weaponGuid =>
} sendResponse(InventoryStateMessage(weapon.AmmoSlot.Box.GUID, weapon.GUID, weapon.Magazine))
} }
}
case _ => () case _ => ()
} }

View file

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

View file

@ -28,19 +28,19 @@ 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}")
} }
} }
@ -50,9 +50,9 @@ class SessionTerminalHandlers(
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 $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 = { 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 => case Terminal.BuyEquipment(item)
if tplayer.avatar.purchaseCooldown(item.Definition).nonEmpty =>
lastTerminalOrderFulfillment = true lastTerminalOrderFulfillment = true
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false)) sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false))
@ -100,19 +101,16 @@ class SessionTerminalHandlers(
avatarActor ! AvatarActor.SellImplant(msg.terminal_guid, implant) avatarActor ! AvatarActor.SellImplant(msg.terminal_guid, implant)
lastTerminalOrderFulfillment = true 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)) sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false))
lastTerminalOrderFulfillment = true lastTerminalOrderFulfillment = true
case Terminal.BuyVehicle(vehicle, weapons, trunk) => case Terminal.BuyVehicle(vehicle, weapons, trunk) =>
continent.map.terminalToSpawnPad continent.map.terminalToSpawnPad
.find { case (termid, _) => termid == msg.terminal_guid.guid } .find { case (termid, _) => termid == msg.terminal_guid.guid }
.map { .map { case (a: Int, b: Int) => (continent.GUID(a), continent.GUID(b)) }
case (a: Int, b: Int) => (continent.GUID(a), continent.GUID(b)) .collect { case (Some(term: Terminal), Some(pad: VehicleSpawnPad)) =>
case _ => (None, None)
}
.get match {
case (Some(term: Terminal), Some(pad: VehicleSpawnPad)) =>
avatarActor ! AvatarActor.UpdatePurchaseTime(vehicle.Definition) avatarActor ! AvatarActor.UpdatePurchaseTime(vehicle.Definition)
vehicle.Faction = tplayer.Faction vehicle.Faction = tplayer.Faction
vehicle.Position = pad.Position vehicle.Position = pad.Position
@ -143,27 +141,30 @@ class SessionTerminalHandlers(
if (GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition)) { if (GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition)) {
sendResponse(UnuseItemMessage(player.GUID, msg.terminal_guid)) sendResponse(UnuseItemMessage(player.GUID, msg.terminal_guid))
} }
case _ => }.orElse {
log.error( 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" 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)) sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false))
None
} }
case Terminal.NoDeal() => case Terminal.NoDeal() if msg != null =>
val order: String = if (msg == null) { val transaction = msg.transaction_type
"missing order" log.warn(s"NoDeal: ${tplayer.Name} made a request but the terminal rejected the ${transaction.toString} order")
} else { sendResponse(ItemTransactionResultMessage(msg.terminal_guid, transaction, success = false))
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))
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
} }
} }
@ -239,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 =>
@ -267,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))
} }
} }
@ -280,7 +280,7 @@ 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 = {
ForgetAllProximityTerminals(terminal.GUID) ForgetAllProximityTerminals(terminal.GUID)
} }
@ -291,12 +291,23 @@ class SessionTerminalHandlers(
* @see `postStop` * @see `postStop`
*/ */
def CancelAllProximityUnits(): Unit = { 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 => 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)
} }
} }

View file

@ -163,10 +163,8 @@ class SessionVehicleHandlers(
avatarActor ! AvatarActor.SetVehicle(Some(vehicleGuid)) avatarActor ! AvatarActor.SetVehicle(Some(vehicleGuid))
sendResponse(PlanetsideAttributeMessage(resolvedPlayerGuid, attribute_type=21, vehicleGuid)) sendResponse(PlanetsideAttributeMessage(resolvedPlayerGuid, attribute_type=21, vehicleGuid))
case VehicleResponse.PlanetsideAttribute(vehicleGuid, attributeType, attributeValue) => case VehicleResponse.PlanetsideAttribute(vehicleGuid, attributeType, attributeValue) if isNotSameTarget =>
if (isNotSameTarget) { sendResponse(PlanetsideAttributeMessage(vehicleGuid, attributeType, attributeValue))
sendResponse(PlanetsideAttributeMessage(vehicleGuid, attributeType, attributeValue))
}
case VehicleResponse.ResetSpawnPad(padGuid) => case VehicleResponse.ResetSpawnPad(padGuid) =>
sendResponse(GenericObjectActionMessage(padGuid, code=23)) sendResponse(GenericObjectActionMessage(padGuid, code=23))
@ -201,7 +199,7 @@ class SessionVehicleHandlers(
case VehicleResponse.KickCargo(vehicle, speed, delay) case VehicleResponse.KickCargo(vehicle, speed, delay)
if player.VehicleSeated.nonEmpty && sessionData.zoning.spawn.deadState == DeadState.Alive && speed > 0 => 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 } val reverseSpeed = if (strafe > 1) { 0 } else { speed }
//strafe or reverse, not both //strafe or reverse, not both
sessionData.vehicles.serverVehicleControlVelocity = Some(reverseSpeed) sessionData.vehicles.serverVehicleControlVelocity = Some(reverseSpeed)
@ -236,19 +234,29 @@ class SessionVehicleHandlers(
unk8=None 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, _) =>
val vehicle_guid = vehicle.GUID val vehicle_guid = vehicle.GUID
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)) {
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(vehicle_guid, attribute_type=22, attribute_value=1L)) //mount points off
sendResponse(PlanetsideAttributeMessage(player.GUID, attribute_type=21, vehicle_guid)) //ownership sendResponse(PlanetsideAttributeMessage(player.GUID, attribute_type=21, vehicle_guid)) //ownership
vehicle.MountPoints.find { case (_, mp) => mp.seatIndex == 0 }.collect { vehicle.MountPoints.find { case (_, mp) => mp.seatIndex == 0 }.collect {

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