From f012126fae93c84286315adbe9069b4ff8b9a78a Mon Sep 17 00:00:00 2001 From: Fate-JH Date: Wed, 15 Feb 2023 22:21:51 -0500 Subject: [PATCH] changes to the session actor handler classes --- .../support/SessionAvatarHandlers.scala | 603 ++++++++---------- .../support/SessionGalaxyHandlers.scala | 69 +- .../support/SessionLocalHandlers.scala | 251 ++++---- .../support/SessionMountHandlers.scala | 217 +++---- .../support/SessionTerminalHandlers.scala | 154 +++-- .../support/SessionVehicleHandlers.scala | 496 +++++++------- 6 files changed, 852 insertions(+), 938 deletions(-) diff --git a/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala index 2c6d534c6..2b43bf630 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala @@ -3,6 +3,8 @@ package net.psforever.actors.session.support import akka.actor.typed.scaladsl.adapter._ import akka.actor.{ActorContext, typed} +import net.psforever.services.Service + import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ // @@ -29,90 +31,99 @@ class SessionAvatarHandlers( chatActor: typed.ActorRef[ChatActor.Command], implicit val context: ActorContext ) extends CommonSessionInterfacingFunctionality { - private[support] var lastSeenStreamMessage: Array[Long] = Array.fill[Long](65535)(elem=0L) + //TODO player characters only exist within a certain range of GUIDs for a given zone; this is overkill + private[support] var lastSeenStreamMessage: Array[Long] = Array.fill[Long](65535)(elem = 0L) /** * na - * * @param toChannel na * @param guid na * @param reply na */ def handle(toChannel: String, guid: PlanetSideGUID, reply: AvatarResponse.Response): Unit = { - val tplayer_guid = - if (player != null && player.HasGUID) player.GUID - else PlanetSideGUID(0) + val resolvedPlayerGuid = if (player != null && player.HasGUID) { + player.GUID + } else { + Service.defaultPlayerGUID + } + val isNotSameTarget = resolvedPlayerGuid != guid reply match { case AvatarResponse.TeardownConnection() => log.trace(s"ending ${player.Name}'s old session by event system request (relog)") context.stop(context.self) + case AvatarResponse.PlayerState( + pos, + vel, + yaw, + pitch, + yawUpper, + _, + isCrouching, + isJumping, + jumpThrust, + isCloaking, + spectating, + _ + ) 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) + } else { + val before = lastSeenStreamMessage(guid.guid) + val dist = Vector3.DistanceSquared(player.Position, pos) + (pos, now - before, dist) + } + if (distanceSq < 302500 || time > 5000) { // Render distance seems to be approx 525m. Reduce update rate at ~550m to be safe + sendResponse( + PlayerStateMessage( + guid, + location, + vel, + yaw, + pitch, + yawUpper, + timestamp=0, + isCrouching, + isJumping, + jumpThrust, + isCloaking + ) + ) + lastSeenStreamMessage(guid.guid) = now + } + case AvatarResponse.SendResponse(msg) => sendResponse(msg) - case AvatarResponse.SendResponseTargeted(target_guid, msg) => - if (tplayer_guid == target_guid) { - sendResponse(msg) - } + case AvatarResponse.SendResponseTargeted(targetGuid, msg) if resolvedPlayerGuid == targetGuid => + sendResponse(msg) - case AvatarResponse.Revive(target_guid) => - if (tplayer_guid == target_guid) { - log.info(s"No time for rest, ${player.Name}. Back on your feet!") - sessionData.zoning.spawn.reviveTimer.cancel() - sessionData.zoning.spawn.deadState = DeadState.Alive - player.Revive - val health = player.Health - sendResponse(PlanetsideAttributeMessage(target_guid, 0, health)) - sendResponse(AvatarDeadStateMessage(DeadState.Alive, 0, 0, player.Position, player.Faction, unk5=true)) - continent.AvatarEvents ! AvatarServiceMessage( - continent.id, - AvatarAction.PlanetsideAttributeToAll(target_guid, 0, health) - ) - } - - case AvatarResponse.ArmorChanged(suit, subtype) => - if (tplayer_guid != guid) { - sendResponse(ArmorChangedMessage(guid, suit, subtype)) - } - - case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) => - if (tplayer_guid != guid) { - sendResponse(ObjectDetachMessage(weapon_guid, previous_guid, Vector3.Zero, 0)) - sendResponse( - ObjectCreateMessage( - ammo_id, - ammo_guid, - ObjectCreateMessageParent(weapon_guid, weapon_slot), - ammo_data - ) - ) - sendResponse(ChangeAmmoMessage(weapon_guid, 1)) - } - - case AvatarResponse.ChangeFireMode(item_guid, mode) => - if (tplayer_guid != guid) { - sendResponse(ChangeFireModeMessage(item_guid, mode)) - } - - case AvatarResponse.ChangeFireState_Start(weapon_guid) => - if (tplayer_guid != guid) { - sendResponse(ChangeFireStateMessage_Start(weapon_guid)) - } - - case AvatarResponse.ChangeFireState_Stop(weapon_guid) => - if (tplayer_guid != guid) { - sendResponse(ChangeFireStateMessage_Stop(weapon_guid)) - } + 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, 9)) + sendResponse(GenericObjectActionMessage(guid, code=9)) case AvatarResponse.EnvironmentalDamage(_, _, _) => + //TODO damage marker? sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg") - //TODO damage marker? case AvatarResponse.Destroy(victim, killer, weapon, pos) => - // guid = victim // killer = killer ;) + // guid = victim // killer = killer sendResponse(DestroyMessage(victim, killer, weapon, pos)) case AvatarResponse.DestroyDisplay(killer, victim, method, unk) => @@ -123,26 +134,18 @@ class SessionAvatarHandlers( avatarActor ! AvatarActor.AwardCep((100 * Config.app.game.cepRate).toLong) } - case AvatarResponse.DropItem(pkt) => - if (tplayer_guid != guid) { - sendResponse(pkt) - } + case AvatarResponse.DropItem(pkt) if isNotSameTarget => + sendResponse(pkt) - case AvatarResponse.EquipmentInHand(pkt) => - if (tplayer_guid != guid) { - sendResponse(pkt) - } + case AvatarResponse.EquipmentInHand(pkt) if isNotSameTarget => + sendResponse(pkt) - case AvatarResponse.GenericObjectAction(object_guid, action_code) => - if (tplayer_guid != guid) { - sendResponse(GenericObjectActionMessage(object_guid, action_code)) - } + 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.HitHint(source_guid) if player.isAlive => + sendResponse(HitHint(source_guid, guid)) + sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg") case AvatarResponse.DropSpecialItem() => sessionData.dropSpecialSlotItem() @@ -159,32 +162,28 @@ class SessionAvatarHandlers( log.info(s"${player.Name} has died, killed by $cause") val respawnTimer = 300.seconds //drop free hand item - player.FreeHand.Equipment match { - case Some(item) => - DropEquipmentFromInventory(player)(item) - case None => ; + player.FreeHand.Equipment.foreach { item => + DropEquipmentFromInventory(player)(item) } sessionData.dropSpecialSlotItem() sessionData.toggleMaxSpecialState(enable = false) - if (player.LastDamage match { - case Some(damage) => damage.interaction.cause match { + + if (player.LastDamage.flatMap { damage => + Some(damage.interaction.cause match { case cause: ExplodingEntityReason => cause.entity.isInstanceOf[VehicleSpawnPad] case _ => false - } - case None => 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)) + 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) match { - case Some(obj: Vehicle) => - sessionData.vehicles.ConditionalDriverVehicleControl(obj) - sessionData.vehicles.serverVehicleControlVelocity = None - sessionData.unaccessContainer(obj) - case _ => ; + continent.GUID(mount).collect { case obj: Vehicle => + sessionData.vehicles.ConditionalDriverVehicleControl(obj) + sessionData.vehicles.serverVehicleControlVelocity = None + sessionData.unaccessContainer(obj) } sessionData.playerActionsToCancel() sessionData.terminals.CancelAllProximityUnits() @@ -211,180 +210,98 @@ class SessionAvatarHandlers( AvatarActor.savePlayerLocation(player) sessionData.renewCharSavedTimer(fixedLen = 1800L, varLen = 0L) - case AvatarResponse.LoadPlayer(pkt) => - if (tplayer_guid != guid) { - sendResponse(pkt) - } + case AvatarResponse.LoadPlayer(pkt) if isNotSameTarget => + sendResponse(pkt) - case AvatarResponse.LoadProjectile(pkt) => - if (tplayer_guid != guid) { - sendResponse(pkt) - } + case AvatarResponse.LoadProjectile(pkt) if isNotSameTarget => + sendResponse(pkt) - case AvatarResponse.ObjectDelete(item_guid, unk) => - if (tplayer_guid != guid) { - sendResponse(ObjectDeleteMessage(item_guid, unk)) - } + case AvatarResponse.ObjectDelete(itemGuid, unk) if isNotSameTarget => + sendResponse(ObjectDeleteMessage(itemGuid, unk)) - case AvatarResponse.ObjectHeld(slot, previousSlot) => - if (tplayer_guid == guid) { - if (slot > -1) { - 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) match { - case Some(term: Terminal with ProximityUnit) => - sessionData.terminals.StopUsingProximityUnit(term) - case _ => ; - } - } + case AvatarResponse.ObjectHeld(slot, _) if resolvedPlayerGuid == guid && slot > -1 => + 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) } - } else { - sendResponse(ObjectHeldMessage(guid, previousSlot, unk1 = false)) } + 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 match { - case Some(vinfo) => Some(DrowningTarget(vinfo.guid, vinfo.progress, vinfo.state)) - case None => None - } - ) - ) + sendResponse(OxygenStateMessage( + DrowningTarget(player.guid, player.progress, player.state), + vehicle.flatMap { vinfo => Some(DrowningTarget(vinfo.guid, vinfo.progress, vinfo.state)) } + )) - case AvatarResponse.PlanetsideAttribute(attribute_type, attribute_value) => - if (tplayer_guid != guid) { - sendResponse(PlanetsideAttributeMessage(guid, attribute_type, attribute_value)) - } + case AvatarResponse.PlanetsideAttribute(attributeType, attributeValue) if isNotSameTarget => + sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue)) - case AvatarResponse.PlanetsideAttributeToAll(attribute_type, attribute_value) => - sendResponse(PlanetsideAttributeMessage(guid, attribute_type, attribute_value)) + case AvatarResponse.PlanetsideAttributeToAll(attributeType, attributeValue) => + sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue)) - case AvatarResponse.PlanetsideAttributeSelf(attribute_type, attribute_value) => - if (tplayer_guid == guid) { - sendResponse(PlanetsideAttributeMessage(guid, attribute_type, attribute_value)) - } + case AvatarResponse.PlanetsideAttributeSelf(attributeType, attributeValue) if resolvedPlayerGuid == guid => + sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue)) - case AvatarResponse.PlayerState( - pos, - vel, - yaw, - pitch, - yaw_upper, - _, - is_crouching, - is_jumping, - jump_thrust, - is_cloaking, - spectating, - _ - ) => - if (tplayer_guid != guid) { - val now = System.currentTimeMillis() - val (location, time, distanceSq): (Vector3, Long, Float) = if (spectating) { - val r = new scala.util.Random - val r1 = 2 + r.nextInt(30).toFloat - val r2 = 2 + r.nextInt(4000).toFloat - (Vector3(r2, r2, r1), 0L, 0f) - } else { - val before = lastSeenStreamMessage(guid.guid) - val dist = Vector3.DistanceSquared(player.Position, pos) - (pos, now - before, dist) - } - if (distanceSq < 302500 || time > 5000) { // Render distance seems to be approx 525m. Reduce update rate at ~550m to be safe - sendResponse( - PlayerStateMessage( - guid, - location, - vel, - yaw, - pitch, - yaw_upper, - timestamp = 0, - is_crouching, - is_jumping, - jump_thrust, - is_cloaking - ) - ) - lastSeenStreamMessage(guid.guid) = now - } - } - - case AvatarResponse.ProjectileExplodes(projectile_guid, projectile) => + case AvatarResponse.ProjectileExplodes(projectileGuid, projectile) => sendResponse( ProjectileStateMessage( - projectile_guid, + projectileGuid, projectile.Position, - Vector3.Zero, + shot_vel = Vector3.Zero, projectile.Orientation, - 0, + sequence_num=0, end=true, - PlanetSideGUID(0) + hit_target_guid=PlanetSideGUID(0) ) ) - sendResponse(ObjectDeleteMessage(projectile_guid, 2)) + sendResponse(ObjectDeleteMessage(projectileGuid, unk1=2)) case AvatarResponse.ProjectileAutoLockAwareness(mode) => sendResponse(GenericActionMessage(mode)) - case AvatarResponse.ProjectileState(projectile_guid, shot_pos, shot_vel, shot_orient, seq, end, target_guid) => - if (tplayer_guid != guid) { - sendResponse(ProjectileStateMessage(projectile_guid, shot_pos, shot_vel, shot_orient, seq, end, target_guid)) - } + 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 (tplayer_guid != guid) { - sendResponse(GenericObjectActionMessage(target, 53)) - } + case AvatarResponse.PutDownFDU(target) if isNotSameTarget => + sendResponse(GenericObjectActionMessage(target, code=53)) - case AvatarResponse.Release(tplayer) => - if (tplayer_guid != guid) { - sessionData.zoning.spawn.DepictPlayerAsCorpse(tplayer) - } + case AvatarResponse.Release(tplayer) if isNotSameTarget => + sessionData.zoning.spawn.DepictPlayerAsCorpse(tplayer) - case AvatarResponse.Reload(item_guid) => - if (tplayer_guid != guid) { - sendResponse(ReloadMessage(item_guid, 1, 0)) - } + case AvatarResponse.Reload(itemGuid) if isNotSameTarget => + sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0)) - case AvatarResponse.SetEmpire(object_guid, faction) => - if (tplayer_guid != guid) { - sendResponse(SetEmpireMessage(object_guid, faction)) - } + case AvatarResponse.SetEmpire(objectGuid, faction) if isNotSameTarget => + sendResponse(SetEmpireMessage(objectGuid, faction)) - case AvatarResponse.StowEquipment(target, slot, item) => - if (tplayer_guid != guid) { - val definition = item.Definition - sendResponse( - ObjectCreateDetailedMessage( - definition.ObjectId, - item.GUID, - ObjectCreateMessageParent(target, slot), - definition.Packet.DetailedConstructorData(item).get - ) + case AvatarResponse.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.WeaponDryFire(weapon_guid) => - if (tplayer_guid != guid) { - continent.GUID(weapon_guid) match { - case Some(tool: Tool) => - // check that the magazine is still empty before sending WeaponDryFireMessage - // if it has been reloaded since then, other clients not see it firing - if (tool.Magazine == 0) { - sendResponse(WeaponDryFireMessage(weapon_guid)) - } - case Some(_) => - sendResponse(WeaponDryFireMessage(weapon_guid)) - case None => ; - } - } - - case AvatarResponse.TerminalOrderResult(terminal_guid, action, result) => - sendResponse(ItemTransactionResultMessage(terminal_guid, action, result)) + case AvatarResponse.TerminalOrderResult(terminalGuid, action, result) => + sendResponse(ItemTransactionResultMessage(terminalGuid, action, result)) sessionData.terminals.lastTerminalOrderFulfillment = true if (result && (action == TransactionType.Buy || action == TransactionType.Loadout)) { @@ -400,79 +317,78 @@ class SessionAvatarHandlers( armor, exosuit, subtype, - slot, + _, maxhand, - old_holsters, + oldHolsters, holsters, - old_inventory, + oldInventory, inventory, drop, delete - ) => + ) if resolvedPlayerGuid == target => sendResponse(ArmorChangedMessage(target, exosuit, subtype)) - sendResponse(PlanetsideAttributeMessage(target, 4, armor)) - if (tplayer_guid == target) { - //happening to this player - //cleanup - sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=false)) - (old_holsters ++ old_inventory ++ delete).foreach { - case (_, dguid) => sendResponse(ObjectDeleteMessage(dguid, 0)) - } - //functionally delete - delete.foreach { case (obj, _) => TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) } - //redraw - if (maxhand) { - TaskWorkflow.execute(HoldNewEquipmentUp(player)( - Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)), - 0 - )) - } - //draw free hand - player.FreeHand.Equipment match { - case Some(obj) => - val definition = obj.Definition - sendResponse( - ObjectCreateDetailedMessage( - definition.ObjectId, - obj.GUID, - ObjectCreateMessageParent(target, Player.FreeHandSlot), - definition.Packet.DetailedConstructorData(obj).get - ) + sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) + //happening to this player + //cleanup + sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=false)) + (oldHolsters ++ oldInventory ++ delete).foreach { + case (_, dguid) => sendResponse(ObjectDeleteMessage(dguid, unk1=0)) + } + //functionally delete + delete.foreach { case (obj, _) => TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) } + //redraw + if (maxhand) { + TaskWorkflow.execute(HoldNewEquipmentUp(player)( + Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)), + 0 + )) + } + //draw free hand + player.FreeHand.Equipment.foreach { obj => + val definition = obj.Definition + sendResponse( + ObjectCreateDetailedMessage( + definition.ObjectId, + obj.GUID, + ObjectCreateMessageParent(target, Player.FreeHandSlot), + definition.Packet.DetailedConstructorData(obj).get + ) + ) + } + //draw holsters and inventory + (holsters ++ inventory).foreach { + case InventoryItem(obj, index) => + val definition = obj.Definition + sendResponse( + ObjectCreateDetailedMessage( + definition.ObjectId, + obj.GUID, + ObjectCreateMessageParent(target, index), + definition.Packet.DetailedConstructorData(obj).get ) - case None => ; - } - //draw holsters and inventory - (holsters ++ inventory).foreach { - case InventoryItem(obj, index) => - val definition = obj.Definition - sendResponse( - ObjectCreateDetailedMessage( - definition.ObjectId, - obj.GUID, - ObjectCreateMessageParent(target, index), - definition.Packet.DetailedConstructorData(obj).get - ) + ) + } + DropLeftovers(player)(drop) + + case AvatarResponse.ChangeExosuit(target, armor, exosuit, subtype, slot, _, oldHolsters, holsters, _, _, _, delete) => + sendResponse(ArmorChangedMessage(target, exosuit, subtype)) + sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) + //happening to some other player + sendResponse(ObjectHeldMessage(target, slot, unk1 = false)) + //cleanup + (oldHolsters ++ delete).foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) } + //draw holsters + holsters.foreach { + case InventoryItem(obj, index) => + val definition = obj.Definition + sendResponse( + ObjectCreateMessage( + definition.ObjectId, + obj.GUID, + ObjectCreateMessageParent(target, index), + definition.Packet.ConstructorData(obj).get ) - } - DropLeftovers(player)(drop) - } else { - //happening to some other player - sendResponse(ObjectHeldMessage(target, slot, unk1=false)) - //cleanup - (old_holsters ++ delete).foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, 0)) } - //draw holsters - holsters.foreach { - case InventoryItem(obj, index) => - val definition = obj.Definition - sendResponse( - ObjectCreateMessage( - definition.ObjectId, - obj.GUID, - ObjectCreateMessageParent(target, index), - definition.Packet.ConstructorData(obj).get - ) - ) - } + ) } case AvatarResponse.ChangeLoadout( @@ -480,68 +396,69 @@ class SessionAvatarHandlers( armor, exosuit, subtype, - slot, + _, maxhand, - old_holsters, + oldHolsters, holsters, - old_inventory, + oldInventory, inventory, drops - ) => + ) if resolvedPlayerGuid == target => sendResponse(ArmorChangedMessage(target, exosuit, subtype)) - sendResponse(PlanetsideAttributeMessage(target, 4, armor)) - if (tplayer_guid == target) { - //happening to this player - sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=true)) - //cleanup - (old_holsters ++ old_inventory).foreach { - case (obj, objGuid) => - sendResponse(ObjectDeleteMessage(objGuid, 0)) - TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) - } - //redraw - if (maxhand) { - TaskWorkflow.execute(HoldNewEquipmentUp(player)( - Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)), - slot = 0 - )) - } - sessionData.applyPurchaseTimersBeforePackingLoadout(player, player, holsters ++ inventory) - DropLeftovers(player)(drops) - } else { - //happening to some other player - sendResponse(ObjectHeldMessage(target, slot, unk1=false)) - //cleanup - old_holsters.foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, 0)) } - //redraw handled by callback + sendResponse(PlanetsideAttributeMessage(target, attribute_type = 4, armor)) + //happening to this player + sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=true)) + //cleanup + (oldHolsters ++ oldInventory).foreach { + case (obj, objGuid) => + sendResponse(ObjectDeleteMessage(objGuid, unk1=0)) + TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) } + //redraw + if (maxhand) { + TaskWorkflow.execute(HoldNewEquipmentUp(player)( + Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)), + slot = 0 + )) + } + sessionData.applyPurchaseTimersBeforePackingLoadout(player, player, holsters ++ inventory) + DropLeftovers(player)(drops) + + case AvatarResponse.ChangeLoadout(target, armor, exosuit, subtype, slot, _, oldHolsters, _, _, _, _) => + 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( UseItemMessage( - tplayer_guid, + resolvedPlayerGuid, kguid, - tplayer_guid, - 4294967295L, - unk3=false, - Vector3.Zero, - Vector3.Zero, - 126, - 0, //sequence time? - 137, + resolvedPlayerGuid, + unk2 = 4294967295L, + unk3 = false, + unk4 = Vector3.Zero, + unk5 = Vector3.Zero, + unk6 = 126, + unk7 = 0, //sequence time? + unk8 = 137, kObjId ) ) - sendResponse(ObjectDeleteMessage(kguid, 0)) + sendResponse(ObjectDeleteMessage(kguid, unk1=0)) case AvatarResponse.KitNotUsed(_, "") => sessionData.kitToBeUsed = None case AvatarResponse.KitNotUsed(_, msg) => sessionData.kitToBeUsed = None - sendResponse(ChatMsg(ChatMessageType.UNK_225, wideContents=false, "", msg, None)) + sendResponse(ChatMsg(ChatMessageType.UNK_225, wideContents=false, recipient="", msg, note=None)) - case _ => ; + case _ => () } } } diff --git a/src/main/scala/net/psforever/actors/session/support/SessionGalaxyHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionGalaxyHandlers.scala index f9fb7ee72..0f0ccdf3f 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionGalaxyHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionGalaxyHandlers.scala @@ -19,12 +19,12 @@ class SessionGalaxyHandlers( ) extends CommonSessionInterfacingFunctionality { def handle(reply: GalaxyResponse.Response): Unit = { reply match { - case GalaxyResponse.HotSpotUpdate(zone_index, priority, hot_spot_info) => + case GalaxyResponse.HotSpotUpdate(zoneIndex, priority, hotSpotInfo) => sendResponse( HotSpotUpdateMessage( - zone_index, + zoneIndex, priority, - hot_spot_info.map { spot => PacketHotSpotInfo(spot.DisplayLocation.x, spot.DisplayLocation.y, 40) } + hotSpotInfo.map { spot => PacketHotSpotInfo(spot.DisplayLocation.x, spot.DisplayLocation.y, 40) } ) ) @@ -44,40 +44,33 @@ class SessionGalaxyHandlers( case GalaxyResponse.FlagMapUpdate(msg) => sendResponse(msg) - case GalaxyResponse.TransferPassenger(temp_channel, vehicle, _, manifest) => + case GalaxyResponse.TransferPassenger(tempChannel, vehicle, _, manifest) => val playerName = player.Name log.debug(s"TransferPassenger: $playerName received the summons to transfer to ${vehicle.Zone.id} ...") - (manifest.passengers.find { _.name.equals(playerName) } match { - case Some(entry) if vehicle.Seats(entry.mount).occupant.isEmpty => - player.VehicleSeated = None - vehicle.Seats(entry.mount).mount(player) - player.VehicleSeated = vehicle.GUID - Some(vehicle) - case Some(entry) if vehicle.Seats(entry.mount).occupant.contains(player) => - Some(vehicle) - case Some(entry) => - log.warn( - s"TransferPassenger: $playerName tried to mount seat ${entry.mount} during summoning, but it was already occupied, and ${player.Sex.pronounSubject} was rebuked" - ) - None - case None => - //log.warn(s"TransferPassenger: $playerName is missing from the manifest of a summoning ${vehicle.Definition.Name} from ${vehicle.Zone.id}") - None - }).orElse { - manifest.cargo.find { _.name.equals(playerName) } match { - case Some(entry) => - vehicle.CargoHolds(entry.mount).occupant match { - case out @ Some(cargo) if cargo.Seats(0).occupants.exists(_.Name.equals(playerName)) => - out - case _ => - None - } - case None => + manifest.passengers + .find { _.name.equals(playerName) } + .collect { + case entry if vehicle.Seats(entry.mount).occupant.isEmpty => + player.VehicleSeated = None + vehicle.Seats(entry.mount).mount(player) + player.VehicleSeated = vehicle.GUID + Some(vehicle) + case entry if vehicle.Seats(entry.mount).occupant.contains(player) => + Some(vehicle) + case entry => + log.warn( + s"TransferPassenger: $playerName tried to mount seat ${entry.mount} during summoning, but it was already occupied, and ${player.Sex.pronounSubject} was rebuked" + ) None - } + }.orElse { + manifest.cargo.find { _.name.equals(playerName) }.flatMap { entry => + vehicle.CargoHolds(entry.mount).occupant.collect { + case cargo if cargo.Seats(0).occupants.exists(_.Name.equals(playerName)) => cargo + } + } } match { case Some(v: Vehicle) => - galaxyService ! Service.Leave(Some(temp_channel)) //temporary vehicle-specific channel (see above) + galaxyService ! Service.Leave(Some(tempChannel)) //temporary vehicle-specific channel (see above) sessionData.zoning.spawn.deadState = DeadState.Release sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, player.Faction, unk5=true)) sessionData.zoning.interstellarFerry = Some(v) //on the other continent and registered to that continent's GUID system @@ -85,7 +78,7 @@ class SessionGalaxyHandlers( case _ => sessionData.zoning.interstellarFerry match { case None => - galaxyService ! Service.Leave(Some(temp_channel)) //no longer being transferred between zones + galaxyService ! Service.Leave(Some(tempChannel)) //no longer being transferred between zones sessionData.zoning.interstellarFerryTopLevelGUID = None case Some(_) => ; //wait patiently @@ -95,7 +88,7 @@ class SessionGalaxyHandlers( case GalaxyResponse.LockedZoneUpdate(zone, time) => sendResponse(ZoneInfoMessage(zone.Number, empire_status=false, lock_time=time)) - case GalaxyResponse.UnlockedZoneUpdate(zone) => ; + case GalaxyResponse.UnlockedZoneUpdate(zone) => sendResponse(ZoneInfoMessage(zone.Number, empire_status=true, lock_time=0L)) val popBO = 0 val popTR = zone.Players.count(_.faction == PlanetSideEmpire.TR) @@ -103,13 +96,13 @@ class SessionGalaxyHandlers( val popVS = zone.Players.count(_.faction == PlanetSideEmpire.VS) sendResponse(ZonePopulationUpdateMessage(zone.Number, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO)) - case GalaxyResponse.LogStatusChange(name) => - if (avatar.people.friend.exists { _.name.equals(name) }) { - avatarActor ! AvatarActor.MemberListRequest(MemberAction.UpdateFriend, name) - } + case GalaxyResponse.LogStatusChange(name) if avatar.people.friend.exists { _.name.equals(name) } => + avatarActor ! AvatarActor.MemberListRequest(MemberAction.UpdateFriend, name) case GalaxyResponse.SendResponse(msg) => sendResponse(msg) + + case _ => () } } } diff --git a/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala index f42254d06..5cf54218b 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala @@ -7,6 +7,7 @@ import net.psforever.objects.vehicles.MountableWeapons import net.psforever.objects._ import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum import net.psforever.packet.game._ +import net.psforever.services.Service import net.psforever.services.local.LocalResponse import net.psforever.types.{ChatMessageType, PlanetSideGUID, Vector3} @@ -21,97 +22,106 @@ class SessionLocalHandlers( * @param reply na */ def handle(toChannel: String, guid: PlanetSideGUID, reply: LocalResponse.Response): Unit = { - val tplayer_guid = if (player.HasGUID) { player.GUID } - else { PlanetSideGUID(0) } + val resolvedPlayerGuid = if (player.HasGUID) { + player.GUID + } else { + Service.defaultPlayerGUID + } + val isNotSameTarget = resolvedPlayerGuid != guid reply match { - case LocalResponse.DeployableMapIcon(behavior, deployInfo) => - if (tplayer_guid != guid) { - sendResponse(DeployableObjectsInfoMessage(behavior, deployInfo)) - } + case LocalResponse.DeployableMapIcon(behavior, deployInfo) if isNotSameTarget => + sendResponse(DeployableObjectsInfoMessage(behavior, deployInfo)) case LocalResponse.DeployableUIFor(item) => sessionData.updateDeployableUIElements(avatar.deployables.UpdateUIElement(item)) case LocalResponse.Detonate(dguid, _: BoomerDeployable) => sendResponse(TriggerEffectMessage(dguid, "detonate_boomer")) - sendResponse(PlanetsideAttributeMessage(dguid, 29, 1)) - sendResponse(ObjectDeleteMessage(dguid, 0)) + sendResponse(PlanetsideAttributeMessage(dguid, attribute_type=29, attribute_value=1)) + sendResponse(ObjectDeleteMessage(dguid, unk1=0)) case LocalResponse.Detonate(dguid, _: ExplosiveDeployable) => - sendResponse(GenericObjectActionMessage(dguid, 19)) - sendResponse(PlanetsideAttributeMessage(dguid, 29, 1)) - sendResponse(ObjectDeleteMessage(dguid, 0)) + sendResponse(GenericObjectActionMessage(dguid, code=19)) + sendResponse(PlanetsideAttributeMessage(dguid, attribute_type=29, attribute_value=1)) + sendResponse(ObjectDeleteMessage(dguid, unk1=0)) case LocalResponse.Detonate(_, obj) => log.warn(s"LocalResponse.Detonate: ${obj.Definition.Name} not configured to explode correctly") - case LocalResponse.DoorOpens(door_guid) => - if (tplayer_guid != guid) { - sendResponse(GenericObjectStateMsg(door_guid, 16)) - } + case LocalResponse.DoorOpens(doorGuid) if isNotSameTarget => + sendResponse(GenericObjectStateMsg(doorGuid, state=16)) - case LocalResponse.DoorCloses(door_guid) => //door closes for everyone - sendResponse(GenericObjectStateMsg(door_guid, 17)) + case LocalResponse.DoorCloses(doorGuid) => //door closes for everyone + sendResponse(GenericObjectStateMsg(doorGuid, state=17)) + + case LocalResponse.EliminateDeployable(obj: TurretDeployable, dguid, _, _) if obj.Destroyed => + sendResponse(ObjectDeleteMessage(dguid, unk1=0)) case LocalResponse.EliminateDeployable(obj: TurretDeployable, dguid, pos, _) => - if (obj.Destroyed) { - sendResponse(ObjectDeleteMessage(dguid, 0)) - } else { - obj.Destroyed = true - DeconstructDeployable( - obj, - dguid, - pos, - obj.Orientation, - if (obj.MountPoints.isEmpty) 2 else 1 - ) - } + obj.Destroyed = true + DeconstructDeployable( + obj, + dguid, + pos, + obj.Orientation, + deletionType= if (obj.MountPoints.isEmpty) { 2 } else { 1 } + ) + + case LocalResponse.EliminateDeployable(obj: ExplosiveDeployable, dguid, _, _) + if obj.Destroyed || obj.Jammed || obj.Health == 0 => + sendResponse(ObjectDeleteMessage(dguid, unk1=0)) case LocalResponse.EliminateDeployable(obj: ExplosiveDeployable, dguid, pos, effect) => - if (obj.Destroyed || obj.Jammed || obj.Health == 0) { - sendResponse(ObjectDeleteMessage(dguid, 0)) - } else { - obj.Destroyed = true - DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect) - } + obj.Destroyed = true + DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect) + + case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Active && obj.Destroyed => + //if active, deactivate + obj.Active = false + sendResponse(GenericObjectActionMessage(dguid, code=29)) + sendResponse(GenericObjectActionMessage(dguid, code=30)) + //standard deployable elimination behavior + sendResponse(ObjectDeleteMessage(dguid, unk1=0)) + + case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) if obj.Active => + //if active, deactivate + obj.Active = false + sendResponse(GenericObjectActionMessage(dguid, code=29)) + sendResponse(GenericObjectActionMessage(dguid, code=30)) + //standard deployable elimination behavior + obj.Destroyed = true + DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2) + + case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Destroyed => + //standard deployable elimination behavior + sendResponse(ObjectDeleteMessage(dguid, unk1=0)) case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) => - //if active, deactivate - if (obj.Active) { - obj.Active = false - sendResponse(GenericObjectActionMessage(dguid, 29)) - sendResponse(GenericObjectActionMessage(dguid, 30)) - } //standard deployable elimination behavior - if (obj.Destroyed) { - sendResponse(ObjectDeleteMessage(dguid, 0)) - } else { - obj.Destroyed = true - DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType = 2) - } + obj.Destroyed = true + DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2) + + case LocalResponse.EliminateDeployable(obj, dguid, _, _) if obj.Destroyed => + sendResponse(ObjectDeleteMessage(dguid, unk1=0)) case LocalResponse.EliminateDeployable(obj, dguid, pos, effect) => - if (obj.Destroyed) { - sendResponse(ObjectDeleteMessage(dguid, 0)) - } else { - obj.Destroyed = true - DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect) - } + obj.Destroyed = true + DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect) - case LocalResponse.SendHackMessageHackCleared(target_guid, unk1, unk2) => - sendResponse(HackMessage(0, target_guid, guid, 0, unk1, HackState.HackCleared, unk2)) + case LocalResponse.SendHackMessageHackCleared(targetGuid, unk1, unk2) => + sendResponse(HackMessage(unk1=0, targetGuid, guid, progress=0, unk1, HackState.HackCleared, unk2)) - case LocalResponse.HackObject(target_guid, unk1, unk2) => - HackObject(target_guid, unk1, unk2) + case LocalResponse.HackObject(targetGuid, unk1, unk2) => + HackObject(targetGuid, unk1, unk2) - case LocalResponse.SendPlanetsideAttributeMessage(target_guid, attribute_number, attribute_value) => - SendPlanetsideAttributeMessage(target_guid, attribute_number, attribute_value) + case LocalResponse.SendPlanetsideAttributeMessage(targetGuid, attributeType, attributeValue) => + SendPlanetsideAttributeMessage(targetGuid, attributeType, attributeValue) - case LocalResponse.SendGenericObjectActionMessage(target_guid, action_number) => - sendResponse(GenericObjectActionMessage(target_guid, action_number)) + case LocalResponse.SendGenericObjectActionMessage(targetGuid, actionNumber) => + sendResponse(GenericObjectActionMessage(targetGuid, actionNumber)) - case LocalResponse.SendGenericActionMessage(action_number) => - sendResponse(GenericActionMessage(action_number)) + case LocalResponse.SendGenericActionMessage(actionNumber) => + sendResponse(GenericActionMessage(actionNumber)) case LocalResponse.SendChatMsg(msg) => sendResponse(msg) @@ -121,49 +131,43 @@ class SessionLocalHandlers( case LocalResponse.LluSpawned(llu) => // Create LLU on client - sendResponse( - ObjectCreateMessage( - llu.Definition.ObjectId, - llu.GUID, - llu.Definition.Packet.ConstructorData(llu).get - ) - ) - sendResponse(TriggerSoundMessage(TriggeredSound.LLUMaterialize, llu.Position, unk = 20, 0.8000001f)) + sendResponse(ObjectCreateMessage( + llu.Definition.ObjectId, + llu.GUID, + llu.Definition.Packet.ConstructorData(llu).get + )) + sendResponse(TriggerSoundMessage(TriggeredSound.LLUMaterialize, llu.Position, unk=20, volume=0.8000001f)) case LocalResponse.LluDespawned(lluGuid, position) => - sendResponse(TriggerSoundMessage(TriggeredSound.LLUDeconstruct, position, unk = 20, 0.8000001f)) - sendResponse(ObjectDeleteMessage(lluGuid, 0)) + 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 match { - case Some(guid) if guid == lluGuid => - sessionData.specialItemSlotGuid = None - player.Carrying = None - case _ => () + sessionData.specialItemSlotGuid.foreach { case guid if guid == lluGuid => + sessionData.specialItemSlotGuid = None + player.Carrying = None } - case LocalResponse.ObjectDelete(object_guid, unk) => - if (tplayer_guid != guid) { - sendResponse(ObjectDeleteMessage(object_guid, unk)) - } + case LocalResponse.ObjectDelete(objectGuid, unk) if isNotSameTarget => + sendResponse(ObjectDeleteMessage(objectGuid, unk)) case LocalResponse.ProximityTerminalEffect(object_guid, true) => - sendResponse(ProximityTerminalUseMessage(PlanetSideGUID(0), object_guid, unk=true)) + sendResponse(ProximityTerminalUseMessage(Service.defaultPlayerGUID, object_guid, unk=true)) - case LocalResponse.ProximityTerminalEffect(object_guid, false) => - sendResponse(ProximityTerminalUseMessage(PlanetSideGUID(0), object_guid, unk=false)) - sessionData.terminals.ForgetAllProximityTerminals(object_guid) + case LocalResponse.ProximityTerminalEffect(objectGuid, false) => + sendResponse(ProximityTerminalUseMessage(Service.defaultPlayerGUID, objectGuid, unk=false)) + sessionData.terminals.ForgetAllProximityTerminals(objectGuid) case LocalResponse.RouterTelepadMessage(msg) => - sendResponse(ChatMsg(ChatMessageType.UNK_229, wideContents=false, "", msg, None)) + sendResponse(ChatMsg(ChatMessageType.UNK_229, wideContents=false, recipient="", msg, note=None)) - case LocalResponse.RouterTelepadTransport(passenger_guid, src_guid, dest_guid) => - sessionData.useRouterTelepadEffect(passenger_guid, src_guid, dest_guid) + case LocalResponse.RouterTelepadTransport(passengerGuid, srcGuid, destGuid) => + sessionData.useRouterTelepadEffect(passengerGuid, srcGuid, destGuid) case LocalResponse.SendResponse(msg) => sendResponse(msg) - case LocalResponse.SetEmpire(object_guid, empire) => - sendResponse(SetEmpireMessage(object_guid, empire)) + case LocalResponse.SetEmpire(objectGuid, empire) => + sendResponse(SetEmpireMessage(objectGuid, empire)) case LocalResponse.ShuttleEvent(ev) => val msg = OrbitalShuttleTimeMsg( @@ -172,7 +176,7 @@ class SessionLocalHandlers( ev.t1, ev.t2, ev.t3, - ev.pairs.map { case ((a, b), c) => PadAndShuttlePair(a, b, c) } + pairs=ev.pairs.map { case ((a, b), c) => PadAndShuttlePair(a, b, c) } ) sendResponse(msg) @@ -183,42 +187,33 @@ class SessionLocalHandlers( sendResponse(ObjectDetachMessage(pguid, sguid, pos, orient)) case LocalResponse.ShuttleState(sguid, pos, orient, state) => - sendResponse(VehicleStateMessage(sguid, 0, pos, orient, None, Some(state), 0, 0, 15, is_decelerating=false, is_cloaked=false)) + sendResponse(VehicleStateMessage(sguid, unk1=0, pos, orient, vel=None, Some(state), unk3=0, unk4=0, wheel_direction=15, is_decelerating=false, is_cloaked=false)) - case LocalResponse.ToggleTeleportSystem(router, system_plan) => - sessionData.toggleTeleportSystem(router, system_plan) + case LocalResponse.ToggleTeleportSystem(router, systemPlan) => + sessionData.toggleTeleportSystem(router, systemPlan) - case LocalResponse.TriggerEffect(target_guid, effect, effectInfo, triggerLocation) => - sendResponse(TriggerEffectMessage(target_guid, effect, effectInfo, triggerLocation)) + case LocalResponse.TriggerEffect(targetGuid, effect, effectInfo, triggerLocation) => + sendResponse(TriggerEffectMessage(targetGuid, effect, effectInfo, triggerLocation)) case LocalResponse.TriggerSound(sound, pos, unk, volume) => sendResponse(TriggerSoundMessage(sound, pos, unk, volume)) - case LocalResponse.UpdateForceDomeStatus(building_guid, activated) => - if (activated) { - sendResponse(GenericObjectActionMessage(building_guid, 11)) - } else { - sendResponse(GenericObjectActionMessage(building_guid, 12)) - } + case LocalResponse.UpdateForceDomeStatus(buildingGuid, activated) if activated => + sendResponse(GenericObjectActionMessage(buildingGuid, 11)) - case LocalResponse.RechargeVehicleWeapon(vehicle_guid, weapon_guid) => - if (tplayer_guid == guid) { - continent.GUID(vehicle_guid) match { - case Some(vehicle: MountableWeapons) => - vehicle.PassengerInSeat(player) match { - case Some(seat_num: Int) => - vehicle.WeaponControlledFromSeat(seat_num) foreach { - case weapon: Tool if weapon.GUID == weapon_guid => - sendResponse(InventoryStateMessage(weapon.AmmoSlot.Box.GUID, weapon.GUID, weapon.Magazine)) - case _ => ; - } - case _ => ; - } - case _ => ; + case LocalResponse.UpdateForceDomeStatus(buildingGuid, _) => + 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)) + } } } - case _ => ; + case _ => () } } @@ -244,25 +239,25 @@ class SessionLocalHandlers( /** * na - * @param target_guid na + * @param targetGuid na * @param unk1 na * @param unk2 na */ - def HackObject(target_guid: PlanetSideGUID, unk1: Long, unk2: Long): Unit = { - sendResponse(HackMessage(0, target_guid, PlanetSideGUID(0), 100, unk1, HackState.Hacked, unk2)) + def HackObject(targetGuid: PlanetSideGUID, unk1: Long, unk2: Long): Unit = { + sendResponse(HackMessage(unk1=0, targetGuid, player_guid=Service.defaultPlayerGUID, progress=100, unk1, HackState.Hacked, unk2)) } /** * Send a PlanetsideAttributeMessage packet to the client - * @param target_guid The target of the attribute - * @param attribute_number The attribute number - * @param attribute_value The attribute value + * @param targetGuid The target of the attribute + * @param attributeType The attribute number + * @param attributeValue The attribute value */ def SendPlanetsideAttributeMessage( - target_guid: PlanetSideGUID, - attribute_number: PlanetsideAttributeEnum, - attribute_value: Long + targetGuid: PlanetSideGUID, + attributeType: PlanetsideAttributeEnum, + attributeValue: Long ): Unit = { - sendResponse(PlanetsideAttributeMessage(target_guid, attribute_number, attribute_value)) + sendResponse(PlanetsideAttributeMessage(targetGuid, attributeType, attributeValue)) } } diff --git a/src/main/scala/net/psforever/actors/session/support/SessionMountHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionMountHandlers.scala index b34a4210e..e33fb97bb 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionMountHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionMountHandlers.scala @@ -31,176 +31,177 @@ class SessionMountHandlers( */ def handle(tplayer: Player, reply: Mountable.Exchange): Unit = { reply match { - case Mountable.CanMount(obj: ImplantTerminalMech, seat_number, _) => + case Mountable.CanMount(obj: ImplantTerminalMech, seatNumber, _) => sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") log.info(s"${player.Name} mounts an implant terminal") sessionData.terminals.CancelAllProximityUnits() - MountingAction(tplayer, obj, seat_number) + MountingAction(tplayer, obj, seatNumber) sessionData.keepAliveFunc = sessionData.keepAlivePersistence - case Mountable.CanMount(obj: Vehicle, seat_number, _) if obj.Definition == GlobalDefinitions.orbital_shuttle => + case Mountable.CanMount(obj: Vehicle, seatNumber, _) + if obj.Definition == GlobalDefinitions.orbital_shuttle => sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount") log.info(s"${player.Name} mounts the orbital shuttle") sessionData.terminals.CancelAllProximityUnits() - MountingAction(tplayer, obj, seat_number) + MountingAction(tplayer, obj, seatNumber) sessionData.keepAliveFunc = sessionData.keepAlivePersistence - case Mountable.CanMount(obj: Vehicle, seat_number, _) => + case Mountable.CanMount(obj: Vehicle, seatNumber, _) => sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount") log.info(s"${player.Name} mounts the ${obj.Definition.Name} in ${ - obj.SeatPermissionGroup(seat_number) match { + obj.SeatPermissionGroup(seatNumber) match { case Some(AccessPermissionGroup.Driver) => "the driver seat" - case Some(seatType) => s"a $seatType seat (#$seat_number)" + case Some(seatType) => s"a $seatType seat (#$seatNumber)" case None => "a seat" } }") val obj_guid: PlanetSideGUID = obj.GUID sessionData.terminals.CancelAllProximityUnits() - sendResponse(PlanetsideAttributeMessage(obj_guid, 0, obj.Health)) + sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=0, obj.Health)) sendResponse(PlanetsideAttributeMessage(obj_guid, obj.Definition.shieldUiAttribute, obj.Shields)) if (obj.Definition == GlobalDefinitions.ant) { - sendResponse(PlanetsideAttributeMessage(obj_guid, 45, obj.NtuCapacitorScaled)) + sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=45, obj.NtuCapacitorScaled)) } if (obj.Definition.MaxCapacitor > 0) { - sendResponse(PlanetsideAttributeMessage(obj_guid, 113, obj.Capacitor)) + sendResponse(PlanetsideAttributeMessage(obj_guid, attribute_type=113, obj.Capacitor)) } - if (seat_number == 0) { + 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, 11)) - } else if (obj.WeaponControlledFromSeat(seat_number).isEmpty) { + sendResponse(GenericObjectActionMessage(obj_guid, code=11)) + } else if (obj.WeaponControlledFromSeat(seatNumber).isEmpty) { sessionData.keepAliveFunc = sessionData.keepAlivePersistence } sessionData.accessContainer(obj) - sessionData.updateWeaponAtSeatPosition(obj, seat_number) - MountingAction(tplayer, obj, seat_number) + sessionData.updateWeaponAtSeatPosition(obj, seatNumber) + MountingAction(tplayer, obj, seatNumber) - case Mountable.CanMount(obj: FacilityTurret, seat_number, _) => + case Mountable.CanMount(obj: FacilityTurret, seatNumber, _) => 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)) } - sendResponse(PlanetsideAttributeMessage(obj.GUID, 0, obj.Health)) - sessionData.updateWeaponAtSeatPosition(obj, seat_number) - MountingAction(tplayer, obj, seat_number) + 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" ) } - case Mountable.CanMount(obj: PlanetSideGameObject with WeaponTurret, seat_number, _) => + case Mountable.CanMount(obj: PlanetSideGameObject with WeaponTurret, seatNumber, _) => sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount") log.info(s"${player.Name} mounts the ${obj.Definition.asInstanceOf[BasicDefinition].Name}") - sendResponse(PlanetsideAttributeMessage(obj.GUID, 0, obj.Health)) - sessionData.updateWeaponAtSeatPosition(obj, seat_number) - MountingAction(tplayer, obj, seat_number) + sendResponse(PlanetsideAttributeMessage(obj.GUID, attribute_type=0, obj.Health)) + sessionData.updateWeaponAtSeatPosition(obj, seatNumber) + MountingAction(tplayer, obj, seatNumber) case Mountable.CanMount(obj: Mountable, _, _) => log.warn(s"MountVehicleMsg: $obj is some mountable object and nothing will happen for ${player.Name}") - case Mountable.CanDismount(obj: ImplantTerminalMech, seat_num, _) => + case Mountable.CanDismount(obj: ImplantTerminalMech, seatNum, _) => log.info(s"${tplayer.Name} dismounts the implant terminal") - DismountAction(tplayer, obj, seat_num) + DismountAction(tplayer, obj, seatNum) - case Mountable.CanDismount(obj: Vehicle, seat_num, mount_point) - if obj.Definition == GlobalDefinitions.orbital_shuttle => + case Mountable.CanDismount(obj: Vehicle, _, mountPoint) + if obj.Definition == GlobalDefinitions.orbital_shuttle && obj.MountedIn.nonEmpty => + //dismount to hart lobby val pguid = player.GUID - if (obj.MountedIn.nonEmpty) { - //dismount to hart lobby - log.info(s"${tplayer.Name} dismounts the orbital shuttle into the lobby") - val sguid = obj.GUID - val (pos, zang) = Vehicles.dismountShuttle(obj, mount_point) - tplayer.Position = pos - sendResponse(DelayedPathMountMsg(pguid, sguid, 60, u2=true)) - continent.LocalEvents ! LocalServiceMessage( - continent.id, - LocalAction.SendResponse(ObjectDetachMessage(sguid, pguid, pos, 0, 0, zang)) - ) - } else { - log.info(s"${player.Name} is prepped for dropping") - //get ready for orbital drop - DismountAction(tplayer, obj, seat_num) - continent.actor ! ZoneActor.RemoveFromBlockMap(player) //character doesn't need it - //DismountAction(...) uses vehicle service, so use that service to coordinate the remainder of the messages - continent.VehicleEvents ! VehicleServiceMessage( - player.Name, - VehicleAction.SendResponse(Service.defaultPlayerGUID, PlayerStasisMessage(pguid)) //the stasis message - ) - //when the player dismounts, they will be positioned where the shuttle was when it disappeared in the sky - //the player will fall to the ground and is perfectly vulnerable in this state - //additionally, our player must exist in the current zone - //having no in-game avatar target will throw us out of the map screen when deploying and cause softlock - continent.VehicleEvents ! VehicleServiceMessage( - player.Name, - VehicleAction.SendResponse( - Service.defaultPlayerGUID, - PlayerStateShiftMessage(ShiftState(0, obj.Position, obj.Orientation.z, None)) //cower in the shuttle bay - ) - ) - continent.VehicleEvents ! VehicleServiceMessage( - continent.id, - VehicleAction.SendResponse(pguid, GenericObjectActionMessage(pguid, 9)) //conceal the player - ) - } + log.info(s"${tplayer.Name} dismounts the orbital shuttle into the lobby") + val sguid = obj.GUID + val (pos, zang) = Vehicles.dismountShuttle(obj, mountPoint) + tplayer.Position = pos + sendResponse(DelayedPathMountMsg(pguid, sguid, u1=60, u2=true)) + continent.LocalEvents ! LocalServiceMessage( + continent.id, + LocalAction.SendResponse(ObjectDetachMessage(sguid, pguid, pos, roll=0, pitch=0, zang)) + ) sessionData.keepAliveFunc = sessionData.zoning.NormalKeepAlive - case Mountable.CanDismount(obj: Vehicle, seat_num, _) if obj.Definition == GlobalDefinitions.droppod => + case Mountable.CanDismount(obj: Vehicle, seatNum, _) + if obj.Definition == GlobalDefinitions.orbital_shuttle => + //get ready for orbital drop + val pguid = player.GUID + val events = continent.VehicleEvents + log.info(s"${player.Name} is prepped for dropping") + DismountAction(tplayer, obj, seatNum) + continent.actor ! ZoneActor.RemoveFromBlockMap(player) //character doesn't need it + //DismountAction(...) uses vehicle service, so use that service to coordinate the remainder of the messages + events ! VehicleServiceMessage( + player.Name, + VehicleAction.SendResponse(Service.defaultPlayerGUID, PlayerStasisMessage(pguid)) //the stasis message + ) + //when the player dismounts, they will be positioned where the shuttle was when it disappeared in the sky + //the player will fall to the ground and is perfectly vulnerable in this state + //additionally, our player must exist in the current zone + //having no in-game avatar target will throw us out of the map screen when deploying and cause softlock + events ! VehicleServiceMessage( + player.Name, + VehicleAction.SendResponse( + Service.defaultPlayerGUID, + PlayerStateShiftMessage(ShiftState(unk=0, obj.Position, obj.Orientation.z, vel=None)) //cower in the shuttle bay + ) + ) + events ! VehicleServiceMessage( + continent.id, + VehicleAction.SendResponse(pguid, GenericObjectActionMessage(pguid, code=9)) //conceal the player + ) + sessionData.keepAliveFunc = sessionData.zoning.NormalKeepAlive + + case Mountable.CanDismount(obj: Vehicle, seatNum, _) + if obj.Definition == GlobalDefinitions.droppod => log.info(s"${tplayer.Name} has landed on ${continent.id}") sessionData.unaccessContainer(obj) - DismountAction(tplayer, obj, seat_num) + DismountAction(tplayer, obj, seatNum) obj.Actor ! Vehicle.Deconstruct() - case Mountable.CanDismount(obj: Vehicle, seat_num, _) => - val player_guid: PlanetSideGUID = tplayer.GUID - if (player_guid == player.GUID) { - //disembarking self - log.info(s"${player.Name} dismounts the ${obj.Definition.Name}'s ${ - obj.SeatPermissionGroup(seat_num) match { - case Some(AccessPermissionGroup.Driver) => "driver seat" - case Some(seatType) => s"$seatType seat (#$seat_num)" - case None => "seat" - } - }") - sessionData.vehicles.ConditionalDriverVehicleControl(obj) - sessionData.unaccessContainer(obj) - DismountAction(tplayer, obj, seat_num) - } else { - continent.VehicleEvents ! VehicleServiceMessage( - continent.id, - VehicleAction.KickPassenger(player_guid, seat_num, unk2=true, obj.GUID) - ) - } + case Mountable.CanDismount(obj: Vehicle, seatNum, _) + if tplayer.GUID == player.GUID => + //disembarking self + log.info(s"${player.Name} dismounts the ${obj.Definition.Name}'s ${ + obj.SeatPermissionGroup(seatNum) match { + case Some(AccessPermissionGroup.Driver) => "driver seat" + case Some(seatType) => s"$seatType seat (#$seatNum)" + case None => "seat" + } + }") + sessionData.vehicles.ConditionalDriverVehicleControl(obj) + sessionData.unaccessContainer(obj) + DismountAction(tplayer, obj, seatNum) - case Mountable.CanDismount(obj: PlanetSideGameObject with WeaponTurret, seat_num, _) => + case Mountable.CanDismount(obj: Vehicle, seat_num, _) => + continent.VehicleEvents ! VehicleServiceMessage( + continent.id, + VehicleAction.KickPassenger(tplayer.GUID, seat_num, unk2=true, obj.GUID) + ) + + case Mountable.CanDismount(obj: PlanetSideGameObject with WeaponTurret, seatNum, _) => log.info(s"${tplayer.Name} dismounts a ${obj.Definition.asInstanceOf[ObjectDefinition].Name}") - DismountAction(tplayer, obj, seat_num) + DismountAction(tplayer, obj, seatNum) case Mountable.CanDismount(obj: Mountable, _, _) => log.warn(s"DismountVehicleMsg: $obj is some dismountable object but nothing will happen for ${player.Name}") - case Mountable.CanNotMount(obj: Vehicle, mount_point) => - log.warn(s"MountVehicleMsg: ${tplayer.Name} attempted to mount $obj's mount $mount_point, but was not allowed") - obj.GetSeatFromMountPoint(mount_point) match { - case Some(seatNum) if obj.SeatPermissionGroup(seatNum).contains(AccessPermissionGroup.Driver) => + case Mountable.CanNotMount(obj: Vehicle, mountPoint) => + log.warn(s"MountVehicleMsg: ${tplayer.Name} attempted to mount $obj's mount $mountPoint, but was not allowed") + obj.GetSeatFromMountPoint(mountPoint).collect { + case seatNum if obj.SeatPermissionGroup(seatNum).contains(AccessPermissionGroup.Driver) => sendResponse( - ChatMsg(ChatMessageType.CMT_OPEN, wideContents=false, "", "You are not the driver of this vehicle.", None) + ChatMsg(ChatMessageType.CMT_OPEN, wideContents=false, recipient="", "You are not the driver of this vehicle.", note=None) ) - case _ => } - case Mountable.CanNotMount(obj: Mountable, mount_point) => - log.warn(s"MountVehicleMsg: ${tplayer.Name} attempted to mount $obj's mount $mount_point, but was not allowed") + case Mountable.CanNotMount(obj: Mountable, mountPoint) => + log.warn(s"MountVehicleMsg: ${tplayer.Name} attempted to mount $obj's mount $mountPoint, but was not allowed") - case Mountable.CanNotDismount(obj, seat_num) => - log.warn( - s"DismountVehicleMsg: ${tplayer.Name} attempted to dismount $obj's mount $seat_num, but was not allowed" - ) + case Mountable.CanNotDismount(obj, seatNum) => + log.warn(s"DismountVehicleMsg: ${tplayer.Name} attempted to dismount $obj's mount $seatNum, but was not allowed") } } @@ -211,15 +212,15 @@ class SessionMountHandlers( * @param seatNum the mount into which the player is mounting */ def MountingAction(tplayer: Player, obj: PlanetSideGameObject with Mountable, seatNum: Int): Unit = { - val player_guid: PlanetSideGUID = tplayer.GUID - val obj_guid: PlanetSideGUID = obj.GUID + val playerGuid: PlanetSideGUID = tplayer.GUID + val objGuid: PlanetSideGUID = obj.GUID sessionData.playerActionsToCancel() avatarActor ! AvatarActor.DeactivateActiveImplants() - avatarActor ! AvatarActor.SuspendStaminaRegeneration(3 seconds) - sendResponse(ObjectAttachMessage(obj_guid, player_guid, seatNum)) + avatarActor ! AvatarActor.SuspendStaminaRegeneration(3.seconds) + sendResponse(ObjectAttachMessage(objGuid, playerGuid, seatNum)) continent.VehicleEvents ! VehicleServiceMessage( continent.id, - VehicleAction.MountVehicle(player_guid, obj_guid, seatNum) + VehicleAction.MountVehicle(playerGuid, objGuid, seatNum) ) } @@ -230,17 +231,17 @@ class SessionMountHandlers( * @param seatNum the mount out of which which the player is disembarking */ def DismountAction(tplayer: Player, obj: PlanetSideGameObject with Mountable, seatNum: Int): Unit = { - val player_guid: PlanetSideGUID = tplayer.GUID + val playerGuid: PlanetSideGUID = tplayer.GUID sessionData.keepAliveFunc = sessionData.zoning.NormalKeepAlive val bailType = if (tplayer.BailProtection) { BailType.Bailed } else { BailType.Normal } - sendResponse(DismountVehicleMsg(player_guid, bailType, wasKickedByDriver = false)) + sendResponse(DismountVehicleMsg(playerGuid, bailType, wasKickedByDriver = false)) continent.VehicleEvents ! VehicleServiceMessage( continent.id, - VehicleAction.DismountVehicle(player_guid, bailType, unk2=false) + VehicleAction.DismountVehicle(playerGuid, bailType, unk2=false) ) } } diff --git a/src/main/scala/net/psforever/actors/session/support/SessionTerminalHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionTerminalHandlers.scala index 434af2ca7..1bac4329a 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionTerminalHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionTerminalHandlers.scala @@ -45,14 +45,14 @@ class SessionTerminalHandlers( } def handleProximityTerminalUse(pkt: ProximityTerminalUseMessage): Unit = { - val ProximityTerminalUseMessage(_, object_guid, _) = pkt - continent.GUID(object_guid) match { + val ProximityTerminalUseMessage(_, objectGuid, _) = pkt + continent.GUID(objectGuid) match { case Some(obj: Terminal with ProximityUnit) => HandleProximityTerminalUse(obj) case Some(obj) => log.warn(s"ProximityTerminalUse: $obj does not have proximity effects for ${player.Name}") case None => - log.error(s"ProximityTerminalUse: ${player.Name} can not find an object with guid $object_guid") + log.error(s"ProximityTerminalUse: ${player.Name} can not find an object with guid $objectGuid") } } @@ -60,26 +60,26 @@ class SessionTerminalHandlers( /** * na - * * @param tplayer na * @param msg na * @param order na */ def handle(tplayer: Player, msg: ItemTransactionMessage, order: Terminal.Exchange): Unit = { order match { + case Terminal.BuyEquipment(item) if tplayer.avatar.purchaseCooldown(item.Definition).nonEmpty => + lastTerminalOrderFulfillment = true + sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false)) + case Terminal.BuyEquipment(item) => - tplayer.avatar.purchaseCooldown(item.Definition) match { - case Some(_) => - lastTerminalOrderFulfillment = true - sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false)) - case None => - avatarActor ! AvatarActor.UpdatePurchaseTime(item.Definition) - TaskWorkflow.execute(BuyNewEquipmentPutInInventory( - continent.GUID(tplayer.VehicleSeated) match { case Some(v: Vehicle) => v; case _ => player }, - tplayer, - msg.terminal_guid - )(item)) - } + avatarActor ! AvatarActor.UpdatePurchaseTime(item.Definition) + TaskWorkflow.execute(BuyNewEquipmentPutInInventory( + continent.GUID(tplayer.VehicleSeated) match { + case Some(v: Vehicle) => v + case _ => player + }, + tplayer, + msg.terminal_guid + )(item)) case Terminal.SellEquipment() => SellEquipmentFromInventory(tplayer, tplayer, msg.terminal_guid)(Player.FreeHandSlot) @@ -100,58 +100,56 @@ class SessionTerminalHandlers( avatarActor ! AvatarActor.SellImplant(msg.terminal_guid, implant) lastTerminalOrderFulfillment = true - case Terminal.BuyVehicle(vehicle, weapons, trunk) => - tplayer.avatar.purchaseCooldown(vehicle.Definition) match { - case Some(_) => - sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false)) - case None => - continent.map.terminalToSpawnPad - .find { case (termid, _) => termid == msg.terminal_guid.guid } - .collect { - case (a: Int, b: Int) => (continent.GUID(a), continent.GUID(b)) - case _ => (None, None) - } - .get match { - case (Some(term: Terminal), Some(pad: VehicleSpawnPad)) => - avatarActor ! AvatarActor.UpdatePurchaseTime(vehicle.Definition) - vehicle.Faction = tplayer.Faction - vehicle.Position = pad.Position - vehicle.Orientation = pad.Orientation + Vector3.z(pad.Definition.VehicleCreationZOrientOffset) - //default loadout, weapons - val vWeapons = vehicle.Weapons - weapons.foreach(entry => { - vWeapons.get(entry.start) match { - case Some(slot) => - entry.obj.Faction = tplayer.Faction - slot.Equipment = None - slot.Equipment = entry.obj - case None => - log.warn( - s"BuyVehicle: ${player.Name} tries to apply default loadout to $vehicle on spawn, but can not find a mounted weapon for ${entry.start}" - ) - } - }) - //default loadout, trunk - val vTrunk = vehicle.Trunk - vTrunk.Clear() - trunk.foreach(entry => { - entry.obj.Faction = tplayer.Faction - vTrunk.InsertQuickly(entry.start, entry.obj) - }) - TaskWorkflow.execute(registerVehicleFromSpawnPad(vehicle, pad, term)) - sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = true)) - if (GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition)) { - sendResponse(UnuseItemMessage(player.GUID, msg.terminal_guid)) - } - case _ => - log.error( - s"${tplayer.Name} wanted to spawn a vehicle, but there was no spawn pad associated with terminal ${msg.terminal_guid} to accept it" - ) - sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false)) - } - } + 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)) => + avatarActor ! AvatarActor.UpdatePurchaseTime(vehicle.Definition) + vehicle.Faction = tplayer.Faction + vehicle.Position = pad.Position + vehicle.Orientation = pad.Orientation + Vector3.z(pad.Definition.VehicleCreationZOrientOffset) + //default loadout, weapons + val vWeapons = vehicle.Weapons + weapons.foreach { entry => + vWeapons.get(entry.start) match { + case Some(slot) => + entry.obj.Faction = tplayer.Faction + slot.Equipment = None + slot.Equipment = entry.obj + case None => + log.warn( + s"BuyVehicle: ${player.Name} tries to apply default loadout to $vehicle on spawn, but can not find a mounted weapon for ${entry.start}" + ) + } + } + //default loadout, trunk + val vTrunk = vehicle.Trunk + vTrunk.Clear() + trunk.foreach { entry => + entry.obj.Faction = tplayer.Faction + vTrunk.InsertQuickly(entry.start, entry.obj) + } + TaskWorkflow.execute(registerVehicleFromSpawnPad(vehicle, pad, term)) + sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = true)) + if (GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition)) { + sendResponse(UnuseItemMessage(player.GUID, msg.terminal_guid)) + } + case _ => + log.error( + s"${tplayer.Name} wanted to spawn a vehicle, but there was no spawn pad associated with terminal ${msg.terminal_guid} to accept it" + ) + sendResponse(ItemTransactionResultMessage(msg.terminal_guid, TransactionType.Buy, success = false)) + } + case Terminal.NoDeal() => val order: String = if (msg == null) { "missing order" @@ -170,7 +168,7 @@ class SessionTerminalHandlers( } } - /* */ + /* support */ /** * Construct tasking that adds a completed and registered vehicle into the scene. @@ -209,7 +207,7 @@ class SessionTerminalHandlers( val term_guid = terminal.GUID val targets = FindProximityUnitTargetsInScope(terminal) val currentTargets = terminal.Targets - targets.foreach(target => { + targets.foreach { target => if (!currentTargets.contains(target)) { StartUsingProximityUnit(terminal, target) } else if (targets.isEmpty) { @@ -217,7 +215,7 @@ class SessionTerminalHandlers( s"HandleProximityTerminalUse: ${player.Name} could not find valid targets to give to proximity unit ${terminal.Definition.Name}@${term_guid.guid}" ) } - }) + } } /** @@ -226,7 +224,7 @@ class SessionTerminalHandlers( * @return na */ def FindProximityUnitTargetsInScope(terminal: Terminal with ProximityUnit): Seq[PlanetSideGameObject] = { - terminal.Definition.asInstanceOf[ProximityDefinition].TargetValidation.keySet collect { + terminal.Definition.asInstanceOf[ProximityDefinition].TargetValidation.keySet.collect { case EffectTarget.Category.Player => Some(player) case EffectTarget.Category.Vehicle | EffectTarget.Category.Aircraft => continent.GUID(player.VehicleSeated) } collect { @@ -256,7 +254,7 @@ class SessionTerminalHandlers( terminal.Definition match { case GlobalDefinitions.adv_med_terminal | GlobalDefinitions.medical_terminal => usingMedicalTerminal = Some(term_guid) - case _ => ; + case _ => () } } } @@ -283,10 +281,7 @@ class SessionTerminalHandlers( * @param terminal the proximity-based unit */ def LocalStopUsingProximityUnit(terminal: Terminal with ProximityUnit, target: PlanetSideGameObject): Unit = { - val term_guid = terminal.GUID - if (usingMedicalTerminal.contains(term_guid)) { - usingMedicalTerminal = None - } + ForgetAllProximityTerminals(terminal.GUID) } /** @@ -296,21 +291,20 @@ class SessionTerminalHandlers( * @see `postStop` */ def CancelAllProximityUnits(): Unit = { - continent.GUID(usingMedicalTerminal) match { - case Some(terminal: Terminal with ProximityUnit) => + continent.GUID(usingMedicalTerminal).collect { + case terminal: Terminal with ProximityUnit => FindProximityUnitTargetsInScope(terminal).foreach(target => terminal.Actor ! CommonMessages.Unuse(player, Some(target)) ) ForgetAllProximityTerminals(usingMedicalTerminal.get) - case _ => ; } } /** * na */ - def ForgetAllProximityTerminals(term_guid: PlanetSideGUID): Unit = { - if (usingMedicalTerminal.contains(term_guid)) { + def ForgetAllProximityTerminals(termGuid: PlanetSideGUID): Unit = { + if (usingMedicalTerminal.contains(termGuid)) { usingMedicalTerminal = None } } diff --git a/src/main/scala/net/psforever/actors/session/support/SessionVehicleHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionVehicleHandlers.scala index f60a156eb..5f411b5ea 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionVehicleHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionVehicleHandlers.scala @@ -3,7 +3,7 @@ package net.psforever.actors.session.support import akka.actor.{ActorContext, ActorRef, typed} import net.psforever.actors.session.AvatarActor -import net.psforever.objects.equipment.{JammableMountedWeapons, JammableUnit} +import net.psforever.objects.equipment.{Equipment, JammableMountedWeapons, JammableUnit} import net.psforever.objects.guid.{GUIDTask, TaskWorkflow} import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.pad.VehicleSpawnPad @@ -31,153 +31,34 @@ class SessionVehicleHandlers( * @param reply na */ def handle(toChannel: String, guid: PlanetSideGUID, reply: VehicleResponse.Response): Unit = { - val tplayer_guid = if (player.HasGUID) player.GUID else PlanetSideGUID(0) + val resolvedPlayerGuid = if (player.HasGUID) { + player.GUID + } else { + Service.defaultPlayerGUID + } + val isNotSameTarget = resolvedPlayerGuid != guid reply match { - case VehicleResponse.AttachToRails(vehicle_guid, pad_guid) => - sendResponse(ObjectAttachMessage(pad_guid, vehicle_guid, 3)) - - case VehicleResponse.ChildObjectState(object_guid, pitch, yaw) => - if (tplayer_guid != guid) { - sendResponse(ChildObjectStateMessage(object_guid, pitch, yaw)) - } - - case VehicleResponse.ConcealPlayer(player_guid) => - sendResponse(GenericObjectActionMessage(player_guid, 9)) - - case VehicleResponse.DismountVehicle(bailType, wasKickedByDriver) => - if (tplayer_guid != guid) { - sendResponse(DismountVehicleMsg(guid, bailType, wasKickedByDriver)) - } - - case VehicleResponse.DeployRequest(object_guid, state, unk1, unk2, pos) => - if (tplayer_guid != guid) { - sendResponse(DeployRequestMessage(guid, object_guid, state, unk1, unk2, pos)) - } - - case VehicleResponse.DetachFromRails(vehicle_guid, pad_guid, pad_position, pad_orientation_z) => - val pad = continent.GUID(pad_guid).get.asInstanceOf[VehicleSpawnPad].Definition - sendResponse( - ObjectDetachMessage( - pad_guid, - vehicle_guid, - pad_position + Vector3.z(pad.VehicleCreationZOffset), - pad_orientation_z + pad.VehicleCreationZOrientOffset - ) - ) - - case VehicleResponse.EquipmentInSlot(pkt) => - if (tplayer_guid != guid) { - sendResponse(pkt) - } - - case VehicleResponse.FrameVehicleState(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA) => - if (tplayer_guid != guid) { - sendResponse(FrameVehicleStateMessage(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA)) - } - - case VehicleResponse.GenericObjectAction(object_guid, action) => - if (tplayer_guid != guid) { - sendResponse(GenericObjectActionMessage(object_guid, action)) - } - - case VehicleResponse.HitHint(source_guid) => - if (player.isAlive) { - sendResponse(HitHint(source_guid, player.GUID)) - } - - case VehicleResponse.InventoryState(obj, parent_guid, start, con_data) => - if (tplayer_guid != guid) { - //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly? - val obj_guid = obj.GUID - sendResponse(ObjectDeleteMessage(obj_guid, 0)) - sendResponse( - ObjectCreateDetailedMessage( - obj.Definition.ObjectId, - obj_guid, - ObjectCreateMessageParent(parent_guid, start), - con_data - ) - ) - } - - case VehicleResponse.KickPassenger(_, wasKickedByDriver, vehicle_guid) => - //seat number (first field) seems to be correct if passenger is kicked manually by driver - //but always seems to return 4 if user is kicked by mount permissions changing - sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver)) - if (tplayer_guid == guid) { - val typeOfRide = continent.GUID(vehicle_guid) match { - case Some(obj: Vehicle) => - sessionData.unaccessContainer(obj) - s"the ${obj.Definition.Name}'s seat by ${obj.OwnerName.getOrElse("the pilot")}" - case _ => - s"${player.Sex.possessive} ride" - } - log.info(s"${player.Name} has been kicked from $typeOfRide!") - } - - case VehicleResponse.InventoryState2(obj_guid, parent_guid, value) => - if (tplayer_guid != guid) { - sendResponse(InventoryStateMessage(obj_guid, 0, parent_guid, value)) - } - - case VehicleResponse.LoadVehicle(vehicle, vtype, vguid, vdata) => - //this is not be suitable for vehicles with people who are seated in it before it spawns (if that is possible) - if (tplayer_guid != guid) { - sendResponse(ObjectCreateMessage(vtype, vguid, vdata)) - Vehicles.ReloadAccessPermissions(vehicle, player.Name) - } - - case VehicleResponse.MountVehicle(vehicle_guid, seat) => - if (tplayer_guid != guid) { - sendResponse(ObjectAttachMessage(vehicle_guid, guid, seat)) - } - - case VehicleResponse.ObjectDelete(itemGuid) => - if (tplayer_guid != guid) { - sendResponse(ObjectDeleteMessage(itemGuid, 0)) - } - - case VehicleResponse.Ownership(vehicleGuid) => - if (tplayer_guid == guid) { // Only the player that owns this vehicle needs the ownership packet - avatarActor ! AvatarActor.SetVehicle(Some(vehicleGuid)) - sendResponse(PlanetsideAttributeMessage(tplayer_guid, 21, vehicleGuid)) - } - - case VehicleResponse.PlanetsideAttribute(vehicle_guid, attribute_type, attribute_value) => - if (tplayer_guid != guid) { - sendResponse(PlanetsideAttributeMessage(vehicle_guid, attribute_type, attribute_value)) - } - - case VehicleResponse.ResetSpawnPad(pad_guid) => - sendResponse(GenericObjectActionMessage(pad_guid, 23)) - - case VehicleResponse.RevealPlayer(player_guid) => - sendResponse(GenericObjectActionMessage(player_guid, 10)) - - case VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission) => - if (tplayer_guid != guid) { - sendResponse(PlanetsideAttributeMessage(vehicle_guid, seat_group, permission)) - } - - case VehicleResponse.StowEquipment(vehicle_guid, slot, item_type, item_guid, item_data) => - if (tplayer_guid != guid) { - //TODO prefer ObjectAttachMessage, but how to force ammo pools to update properly? - sendResponse( - ObjectCreateDetailedMessage(item_type, item_guid, ObjectCreateMessageParent(vehicle_guid, slot), item_data) - ) - } - - case VehicleResponse.UnloadVehicle(_, vehicle_guid) => - sendResponse(ObjectDeleteMessage(vehicle_guid, 0)) - - case VehicleResponse.UnstowEquipment(item_guid) => - if (tplayer_guid != guid) { - //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly? - sendResponse(ObjectDeleteMessage(item_guid, 0)) - } + case VehicleResponse.VehicleState( + vehicleGuid, + unk1, + pos, + orient, + vel, + unk2, + unk3, + unk4, + wheelDirection, + unk5, + unk6 + ) if isNotSameTarget && player.VehicleSeated.contains(vehicleGuid) => + //player who is also in the vehicle (not driver) + sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, orient, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6)) + player.Position = pos + player.Orientation = orient + player.Velocity = vel case VehicleResponse.VehicleState( - vehicle_guid, + vehicleGuid, unk1, pos, ang, @@ -185,59 +66,175 @@ class SessionVehicleHandlers( unk2, unk3, unk4, - wheel_direction, + wheelDirection, unk5, unk6 - ) => - if (tplayer_guid != guid) { - sendResponse( - VehicleStateMessage(vehicle_guid, unk1, pos, ang, vel, unk2, unk3, unk4, wheel_direction, unk5, unk6) - ) - if (player.VehicleSeated.contains(vehicle_guid)) { - player.Position = pos - } - } + ) if isNotSameTarget => + //player who is watching the vehicle from the outside + sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, ang, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6)) + + case VehicleResponse.ChildObjectState(objectGuid, pitch, yaw) if isNotSameTarget => + sendResponse(ChildObjectStateMessage(objectGuid, pitch, yaw)) + + case VehicleResponse.FrameVehicleState(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA) + if isNotSameTarget => + sendResponse(FrameVehicleStateMessage(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA)) + + case VehicleResponse.DismountVehicle(bailType, wasKickedByDriver) if isNotSameTarget => + sendResponse(DismountVehicleMsg(guid, bailType, wasKickedByDriver)) + + case VehicleResponse.MountVehicle(vehicleGuid, seat) if isNotSameTarget => + sendResponse(ObjectAttachMessage(vehicleGuid, guid, seat)) + + case VehicleResponse.DeployRequest(objectGuid, state, unk1, unk2, pos) if isNotSameTarget => + sendResponse(DeployRequestMessage(guid, objectGuid, state, unk1, unk2, pos)) + case VehicleResponse.SendResponse(msg) => sendResponse(msg) + case VehicleResponse.AttachToRails(vehicleGuid, padGuid) => + sendResponse(ObjectAttachMessage(padGuid, vehicleGuid, slot=3)) + + case VehicleResponse.ConcealPlayer(playerGuid) => + sendResponse(GenericObjectActionMessage(playerGuid, code=9)) + + case VehicleResponse.DetachFromRails(vehicleGuid, padGuid, padPosition, padOrientationZ) => + val pad = continent.GUID(padGuid).get.asInstanceOf[VehicleSpawnPad].Definition + sendResponse( + ObjectDetachMessage( + padGuid, + vehicleGuid, + padPosition + Vector3.z(pad.VehicleCreationZOffset), + padOrientationZ + pad.VehicleCreationZOrientOffset + ) + ) + + case VehicleResponse.EquipmentInSlot(pkt) if isNotSameTarget => + sendResponse(pkt) + + case VehicleResponse.GenericObjectAction(objectGuid, action) if isNotSameTarget => + sendResponse(GenericObjectActionMessage(objectGuid, action)) + + case VehicleResponse.HitHint(sourceGuid) if player.isAlive => + sendResponse(HitHint(sourceGuid, player.GUID)) + + case VehicleResponse.InventoryState(obj, parentGuid, start, conData) if isNotSameTarget => + //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly? + val objGuid = obj.GUID + sendResponse(ObjectDeleteMessage(objGuid, unk1=0)) + sendResponse(ObjectCreateDetailedMessage( + obj.Definition.ObjectId, + objGuid, + ObjectCreateMessageParent(parentGuid, start), + conData + )) + + case VehicleResponse.KickPassenger(_, wasKickedByDriver, vehicleGuid) if resolvedPlayerGuid == guid => + //seat number (first field) seems to be correct if passenger is kicked manually by driver + //but always seems to return 4 if user is kicked by mount permissions changing + sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver)) + val typeOfRide = continent.GUID(vehicleGuid) match { + case Some(obj: Vehicle) => + sessionData.unaccessContainer(obj) + s"the ${obj.Definition.Name}'s seat by ${obj.OwnerName.getOrElse("the pilot")}" + case _ => + s"${player.Sex.possessive} ride" + } + log.info(s"${player.Name} has been kicked from $typeOfRide!") + + case VehicleResponse.KickPassenger(_, wasKickedByDriver, _) => + //seat number (first field) seems to be correct if passenger is kicked manually by driver + //but always seems to return 4 if user is kicked by mount permissions changing + sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver)) + + case VehicleResponse.InventoryState2(objGuid, parentGuid, value) if isNotSameTarget => + sendResponse(InventoryStateMessage(objGuid, unk=0, parentGuid, value)) + + case VehicleResponse.LoadVehicle(vehicle, vtype, vguid, vdata) if isNotSameTarget => + //this is not be suitable for vehicles with people who are seated in it before it spawns (if that is possible) + sendResponse(ObjectCreateMessage(vtype, vguid, vdata)) + Vehicles.ReloadAccessPermissions(vehicle, player.Name) + + case VehicleResponse.ObjectDelete(itemGuid) if isNotSameTarget => + sendResponse(ObjectDeleteMessage(itemGuid, unk1=0)) + + case VehicleResponse.Ownership(vehicleGuid) if resolvedPlayerGuid == guid => + //Only the player that owns this vehicle needs the ownership packet + avatarActor ! AvatarActor.SetVehicle(Some(vehicleGuid)) + sendResponse(PlanetsideAttributeMessage(resolvedPlayerGuid, attribute_type=21, vehicleGuid)) + + case VehicleResponse.PlanetsideAttribute(vehicleGuid, attributeType, attributeValue) => + if (isNotSameTarget) { + sendResponse(PlanetsideAttributeMessage(vehicleGuid, attributeType, attributeValue)) + } + + case VehicleResponse.ResetSpawnPad(padGuid) => + sendResponse(GenericObjectActionMessage(padGuid, code=23)) + + case VehicleResponse.RevealPlayer(playerGuid) => + sendResponse(GenericObjectActionMessage(playerGuid, code=10)) + + case VehicleResponse.SeatPermissions(vehicleGuid, seatGroup, permission) if isNotSameTarget => + sendResponse(PlanetsideAttributeMessage(vehicleGuid, seatGroup, permission)) + + case VehicleResponse.StowEquipment(vehicleGuid, slot, itemType, itemGuid, itemData) if isNotSameTarget => + //TODO prefer ObjectAttachMessage, but how to force ammo pools to update properly? + sendResponse(ObjectCreateDetailedMessage(itemType, itemGuid, ObjectCreateMessageParent(vehicleGuid, slot), itemData)) + + case VehicleResponse.UnloadVehicle(_, vehicleGuid) => + sendResponse(ObjectDeleteMessage(vehicleGuid, unk1=0)) + + case VehicleResponse.UnstowEquipment(itemGuid) if isNotSameTarget => + //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly? + sendResponse(ObjectDeleteMessage(itemGuid, unk1=0)) + case VehicleResponse.UpdateAmsSpawnPoint(list) => sessionData.zoning.spawn.amsSpawnPoints = list.filter(tube => tube.Faction == player.Faction) sessionData.zoning.spawn.DrawCurrentAmsSpawnPoint() - case VehicleResponse.TransferPassengerChannel(old_channel, temp_channel, vehicle, vehicle_to_delete) => - if (tplayer_guid != guid) { - sessionData.zoning.interstellarFerry = Some(vehicle) - sessionData.zoning.interstellarFerryTopLevelGUID = Some(vehicle_to_delete) - continent.VehicleEvents ! Service.Leave( - Some(old_channel) - ) //old vehicle-specific channel (was s"${vehicle.Actor}") - galaxyService ! Service.Join(temp_channel) //temporary vehicle-specific channel - log.debug(s"TransferPassengerChannel: ${player.Name} now subscribed to $temp_channel for vehicle gating") - } + case VehicleResponse.TransferPassengerChannel(oldChannel, tempChannel, vehicle, vehicleToDelete) if isNotSameTarget => + sessionData.zoning.interstellarFerry = Some(vehicle) + sessionData.zoning.interstellarFerryTopLevelGUID = Some(vehicleToDelete) + continent.VehicleEvents ! Service.Leave(Some(oldChannel)) //old vehicle-specific channel (was s"${vehicle.Actor}") + galaxyService ! Service.Join(tempChannel) //temporary vehicle-specific channel + log.debug(s"TransferPassengerChannel: ${player.Name} now subscribed to $tempChannel for vehicle gating") - case VehicleResponse.KickCargo(vehicle, speed, delay) => - if (player.VehicleSeated.nonEmpty && sessionData.zoning.spawn.deadState == DeadState.Alive) { - if (speed > 0) { - val strafe = - if (Vehicles.CargoOrientation(vehicle) == 1) 2 - else 1 - val reverseSpeed = - if (strafe > 1) 0 - else speed - //strafe or reverse, not both - sessionData.vehicles.serverVehicleControlVelocity = Some(reverseSpeed) - sendResponse(ServerVehicleOverrideMsg(lock_accelerator=true, lock_wheel=true, reverse=true, unk4=false, 0, strafe, reverseSpeed, Some(0))) - import scala.concurrent.ExecutionContext.Implicits.global - context.system.scheduler.scheduleOnce( - delay milliseconds, - context.self, - VehicleServiceResponse(toChannel, PlanetSideGUID(0), VehicleResponse.KickCargo(vehicle, 0, delay)) - ) - } else { - sessionData.vehicles.serverVehicleControlVelocity = None - sendResponse(ServerVehicleOverrideMsg(lock_accelerator=false,lock_wheel=false, reverse=false, unk4=false, 0, 0, 0, None)) - } - } + 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 reverseSpeed = if (strafe > 1) { 0 } else { speed } + //strafe or reverse, not both + sessionData.vehicles.serverVehicleControlVelocity = Some(reverseSpeed) + sendResponse(ServerVehicleOverrideMsg( + lock_accelerator=true, + lock_wheel=true, + reverse=true, + unk4=false, + lock_vthrust=0, + strafe, + reverseSpeed, + unk8=Some(0) + )) + import scala.concurrent.ExecutionContext.Implicits.global + context.system.scheduler.scheduleOnce( + delay milliseconds, + context.self, + VehicleServiceResponse(toChannel, PlanetSideGUID(0), VehicleResponse.KickCargo(vehicle, speed=0, delay)) + ) + + case VehicleResponse.KickCargo(_, _, _) + if player.VehicleSeated.nonEmpty && sessionData.zoning.spawn.deadState == DeadState.Alive => + sessionData.vehicles.serverVehicleControlVelocity = None + sendResponse(ServerVehicleOverrideMsg( + lock_accelerator=false, + lock_wheel=false, + reverse=false, + unk4=false, + lock_vthrust=0, + lock_strafe=0, + movement_speed=0, + unk8=None + )) case VehicleResponse.StartPlayerSeatedInVehicle(vehicle, _) => val vehicle_guid = vehicle.GUID @@ -246,28 +243,31 @@ class SessionVehicleHandlers( sessionData.terminals.CancelAllProximityUnits() if (player.VisibleSlots.contains(player.DrawnSlot)) { player.DrawnSlot = Player.HandsDownSlot - sendResponse(ObjectHeldMessage(player.GUID, Player.HandsDownSlot, unk1 = true)) + sendResponse(ObjectHeldMessage(player.GUID, Player.HandsDownSlot, unk1=true)) continent.AvatarEvents ! AvatarServiceMessage( continent.id, - AvatarAction.SendResponse(player.GUID, ObjectHeldMessage(player.GUID, player.LastDrawnSlot, unk1 = false)) + AvatarAction.SendResponse(player.GUID, ObjectHeldMessage(player.GUID, player.LastDrawnSlot, unk1=false)) ) } - sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 1L)) //mount points off - sendResponse(PlanetsideAttributeMessage(player.GUID, 21, vehicle_guid)) //ownership - vehicle.MountPoints.find { case (_, mp) => mp.seatIndex == 0 } match { - case Some((mountPoint, _)) => vehicle.Actor ! Mountable.TryMount(player, mountPoint) - case _ => ; + 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.PlayerSeatedInVehicle(vehicle, _) => val vehicle_guid = vehicle.GUID - sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 0L)) //mount points on + sendResponse(PlanetsideAttributeMessage(vehicle_guid, attribute_type=22, attribute_value=0L)) //mount points on Vehicles.ReloadAccessPermissions(vehicle, player.Name) sessionData.vehicles.ServerVehicleLock(vehicle) case VehicleResponse.ServerVehicleOverrideStart(vehicle, _) => val vdef = vehicle.Definition - sessionData.vehicles.ServerVehicleOverride(vehicle, vdef.AutoPilotSpeed1, if (GlobalDefinitions.isFlightVehicle(vdef)) 1 else 0) + sessionData.vehicles.ServerVehicleOverride( + vehicle, + vdef.AutoPilotSpeed1, + flight= if (GlobalDefinitions.isFlightVehicle(vdef)) { 1 } else { 0 } + ) case VehicleResponse.ServerVehicleOverrideEnd(vehicle, _) => session = session.copy(avatar = avatar.copy(vehicle = Some(vehicle.GUID))) @@ -277,63 +277,77 @@ class SessionVehicleHandlers( sendResponse(ChatMsg( ChatMessageType.CMT_OPEN, wideContents=true, - "", + recipient="", s"The vehicle spawn where you placed your order is blocked. ${data.getOrElse("")}", - None + note=None )) case VehicleResponse.PeriodicReminder(_, data) => val (isType, flag, msg): (ChatMessageType, Boolean, String) = data match { - case Some(msg: String) - if msg.startsWith("@") => (ChatMessageType.UNK_227, false, msg) + case Some(msg: String) if msg.startsWith("@") => (ChatMessageType.UNK_227, false, msg) case Some(msg: String) => (ChatMessageType.CMT_OPEN, true, msg) case _ => (ChatMessageType.CMT_OPEN, true, "Your vehicle order has been cancelled.") } - sendResponse(ChatMsg(isType, flag, "", msg, None)) + sendResponse(ChatMsg(isType, flag, recipient="", msg, None)) - case VehicleResponse.ChangeLoadout(target, old_weapons, added_weapons, old_inventory, new_inventory) => + case VehicleResponse.ChangeLoadout(target, oldWeapons, addedWeapons, oldInventory, newInventory) + if player.avatar.vehicle.contains(target) => //TODO when vehicle weapons can be changed without visual glitches, rewrite this - continent.GUID(target) match { - case Some(vehicle: Vehicle) => - if (player.avatar.vehicle.contains(target)) { - import net.psforever.login.WorldSession.boolToInt - //owner: must unregister old equipment, and register and install new equipment - (old_weapons ++ old_inventory).foreach { - case (obj, eguid) => - sendResponse(ObjectDeleteMessage(eguid, 0)) - TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) - } - sessionData.applyPurchaseTimersBeforePackingLoadout(player, vehicle, added_weapons ++ new_inventory) - //jammer or unjamm new weapons based on vehicle status - val vehicleJammered = vehicle.Jammed - added_weapons - .map { - _.obj - } - .collect { - case jamItem: JammableUnit if jamItem.Jammed != vehicleJammered => - jamItem.Jammed = vehicleJammered - JammableMountedWeapons.JammedWeaponStatus(vehicle.Zone, jamItem, vehicleJammered) - } - } else if (sessionData.accessedContainer.map { _.GUID }.contains(target)) { - //external participant: observe changes to equipment - (old_weapons ++ old_inventory).foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, 0)) } + continent.GUID(target).collect { case vehicle: Vehicle => + import net.psforever.login.WorldSession.boolToInt + //owner: must unregister old equipment, and register and install new equipment + (oldWeapons ++ oldInventory).foreach { + case (obj, eguid) => + sendResponse(ObjectDeleteMessage(eguid, unk1=0)) + TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) + } + sessionData.applyPurchaseTimersBeforePackingLoadout(player, vehicle, addedWeapons ++ newInventory) + //jammer or unjamm new weapons based on vehicle status + val vehicleJammered = vehicle.Jammed + addedWeapons + .map { _.obj } + .collect { + case jamItem: JammableUnit if jamItem.Jammed != vehicleJammered => + jamItem.Jammed = vehicleJammered + JammableMountedWeapons.JammedWeaponStatus(vehicle.Zone, jamItem, vehicleJammered) } - vehicle.PassengerInSeat(player) match { - case Some(seatNum) => - //participant: observe changes to equipment - (old_weapons ++ old_inventory).foreach { - case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, 0)) - } - sessionData.updateWeaponAtSeatPosition(vehicle, seatNum) - case None => - //observer: observe changes to external equipment - old_weapons.foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, 0)) } - } - case _ => ; + changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory) } - case _ => ; + case VehicleResponse.ChangeLoadout(target, oldWeapons, _, oldInventory, _) + if sessionData.accessedContainer.map { _.GUID }.contains(target) => + //TODO when vehicle weapons can be changed without visual glitches, rewrite this + continent.GUID(target).collect { case vehicle: Vehicle => + //external participant: observe changes to equipment + (oldWeapons ++ oldInventory).foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, unk1=0)) } + changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory) + } + + case VehicleResponse.ChangeLoadout(target, oldWeapons, _, oldInventory, _) => + //TODO when vehicle weapons can be changed without visual glitches, rewrite this + continent.GUID(target).collect { case vehicle: Vehicle => + changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory) + } + + case _ => () + } + } + + private def changeLoadoutDeleteOldEquipment( + vehicle: Vehicle, + oldWeapons: Iterable[(Equipment, PlanetSideGUID)], + oldInventory: Iterable[(Equipment, PlanetSideGUID)] + ): Unit = { + vehicle.PassengerInSeat(player) match { + case Some(seatNum) => + //participant: observe changes to equipment + (oldWeapons ++ oldInventory).foreach { + case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, unk1=0)) + } + sessionData.updateWeaponAtSeatPosition(vehicle, seatNum) + case None => + //observer: observe changes to external equipment + oldWeapons.foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, unk1=0)) } } } }