From 40cf783f181ebd6d1c91048605abbb8d64f9d566 Mon Sep 17 00:00:00 2001 From: Fate-JH Date: Fri, 10 Feb 2023 23:40:16 -0500 Subject: [PATCH] Code Style Improvements (#1016) * code style improvements for SessionData file * refactored and simplified methods; added GenericAction case enum for smoother GenericActionMessage behavior; fixed timers for MAX unit * 10 -> 11; suffixes for actions that are 'received' from the client --- .../actors/session/AvatarActor.scala | 18 +- .../actors/session/SessionActor.scala | 4 +- .../support/SessionAvatarHandlers.scala | 14 +- .../actors/session/support/SessionData.scala | 3454 ++++++++--------- .../support/SessionLocalHandlers.scala | 6 +- .../support/SessionMountHandlers.scala | 20 +- .../support/SessionSquadHandlers.scala | 7 +- .../support/SessionVehicleHandlers.scala | 8 +- .../session/support/VehicleOperations.scala | 14 +- .../WeaponAndProjectileOperations.scala | 32 +- .../session/support/ZoningOperations.scala | 54 +- .../net/psforever/objects/avatar/Avatar.scala | 4 +- .../game/AvatarVehicleTimerMessage.scala | 12 +- .../packet/game/GenericActionMessage.scala | 109 +- .../services/local/LocalServiceMessage.scala | 5 +- .../services/local/LocalServiceResponse.scala | 3 +- .../local/support/HackCaptureActor.scala | 4 +- .../scala/game/GenericActionMessageTest.scala | 4 +- 18 files changed, 1792 insertions(+), 1980 deletions(-) diff --git a/src/main/scala/net/psforever/actors/session/AvatarActor.scala b/src/main/scala/net/psforever/actors/session/AvatarActor.scala index 5def0d08..c9db6a2f 100644 --- a/src/main/scala/net/psforever/actors/session/AvatarActor.scala +++ b/src/main/scala/net/psforever/actors/session/AvatarActor.scala @@ -1415,7 +1415,11 @@ class AvatarActor( updatePurchaseTimer( name, cooldown.toSeconds, - item.isInstanceOf[VehicleDefinition] + item match { + case t: ToolDefinition => GlobalDefinitions.isMaxArms(t) + case _: VehicleDefinition => true + case _ => false + } ) case _ => ; } @@ -2574,13 +2578,17 @@ class AvatarActor( avatar.cooldowns.purchase.find { case (name, _) => name.equals(key) } match { case Some((name, purchaseTime)) => val secondsSincePurchase = Seconds.secondsBetween(purchaseTime, LocalDateTime.now()).getSeconds - Avatar.purchaseCooldowns.find(_._1.Name == name) match { + Avatar.purchaseCooldowns.find(_._1.Name.equals(name)) match { case Some((obj, cooldown)) if cooldown.toSeconds - secondsSincePurchase > 0 => val (_, name) = AvatarActor.resolvePurchaseTimeName(avatar.faction, obj) updatePurchaseTimer( name, cooldown.toSeconds - secondsSincePurchase, - DefinitionUtil.fromString(name).isInstanceOf[VehicleDefinition] + obj match { + case t: ToolDefinition => GlobalDefinitions.isMaxArms(t) + case _: VehicleDefinition => true + case _ => false + } ) case _ => @@ -2595,9 +2603,9 @@ class AvatarActor( } } - def updatePurchaseTimer(name: String, time: Long, isActuallyAVehicle: Boolean): Unit = { + def updatePurchaseTimer(name: String, time: Long, isActuallyAMachine: Boolean): Unit = { sessionActor ! SessionActor.SendResponse( - AvatarVehicleTimerMessage(session.get.player.GUID, name, time, isActuallyAVehicle) + AvatarVehicleTimerMessage(session.get.player.GUID, name, time, isActuallyAMachine) ) } diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala index bfaaddfa..ddbcaf16 100644 --- a/src/main/scala/net/psforever/actors/session/SessionActor.scala +++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala @@ -256,10 +256,10 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con sessionFuncs.zoning.spawn.performAvatarAwardMessageDelivery(pkts, delay) case CommonMessages.Progress(rate, finishedAction, stepAction) => - sessionFuncs.SetupProgressChange(rate, finishedAction, stepAction) + sessionFuncs.setupProgressChange(rate, finishedAction, stepAction) case SessionActor.ProgressEvent(delta, finishedAction, stepAction, tick) => - sessionFuncs.HandleProgressChange(delta, finishedAction, stepAction, tick) + sessionFuncs.handleProgressChange(delta, finishedAction, stepAction, tick) case CavernRotationService.CavernRotationServiceKey.Listing(listings) => listings.head ! SendCavernRotationUpdates(context.self) 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 48957943..0ec03be1 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala @@ -114,7 +114,7 @@ class SessionAvatarHandlers( sendResponse(DestroyMessage(victim, killer, weapon, pos)) case AvatarResponse.DestroyDisplay(killer, victim, method, unk) => - sendResponse(sessionData.DestroyDisplayMessage(killer, victim, method, unk)) + sendResponse(sessionData.destroyDisplayMessage(killer, victim, method, unk)) // TODO Temporary thing that should go somewhere else and use proper xp values if (killer.CharId == avatar.id && killer.Faction != victim.Faction) { avatarActor ! AvatarActor.AwardBep((1000 * Config.app.game.bepRate).toLong) @@ -143,7 +143,7 @@ class SessionAvatarHandlers( } case AvatarResponse.DropSpecialItem() => - sessionData.DropSpecialSlotItem() + sessionData.dropSpecialSlotItem() case AvatarResponse.Killed(mount) => val cause = (player.LastDamage match { @@ -162,8 +162,8 @@ class SessionAvatarHandlers( DropEquipmentFromInventory(player)(item) case None => ; } - sessionData.DropSpecialSlotItem() - sessionData.ToggleMaxSpecialState(enable = false) + sessionData.dropSpecialSlotItem() + sessionData.toggleMaxSpecialState(enable = false) if (player.LastDamage match { case Some(damage) => damage.interaction.cause match { case cause: ExplodingEntityReason => cause.entity.isInstanceOf[VehicleSpawnPad] @@ -181,10 +181,10 @@ class SessionAvatarHandlers( case Some(obj: Vehicle) => sessionData.vehicles.ConditionalDriverVehicleControl(obj) sessionData.vehicles.serverVehicleControlVelocity = None - sessionData.UnaccessContainer(obj) + sessionData.unaccessContainer(obj) case _ => ; } - sessionData.PlayerActionsToCancel() + sessionData.playerActionsToCancel() sessionData.terminals.CancelAllProximityUnits() sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel") if (sessionData.shooting.shotsWhileDead > 0) { @@ -504,7 +504,7 @@ class SessionAvatarHandlers( slot = 0 )) } - sessionData.ApplyPurchaseTimersBeforePackingLoadout(player, player, holsters ++ inventory) + sessionData.applyPurchaseTimersBeforePackingLoadout(player, player, holsters ++ inventory) DropLeftovers(player)(drops) } else { //happening to some other player diff --git a/src/main/scala/net/psforever/actors/session/support/SessionData.scala b/src/main/scala/net/psforever/actors/session/support/SessionData.scala index aee06433..359f1f0c 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionData.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionData.scala @@ -17,7 +17,7 @@ import net.psforever.objects.avatar._ import net.psforever.objects.ballistics._ import net.psforever.objects.ce._ import net.psforever.objects.definition._ -import net.psforever.objects.entity.{NoGUIDException, WorldEntity} +import net.psforever.objects.entity.{NoGUIDException,WorldEntity} import net.psforever.objects.equipment._ import net.psforever.objects.guid._ import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem} @@ -38,7 +38,6 @@ import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.objects.serverobject.turret.FacilityTurret -import net.psforever.objects.serverobject.zipline.ZipLinePath import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject, ServerObject} import net.psforever.objects.vehicles.Utility.InternalTelepad import net.psforever.objects.vehicles._ @@ -48,7 +47,7 @@ import net.psforever.objects.vital.interaction.DamageInteraction import net.psforever.objects.zones._ import net.psforever.objects.zones.blockmap.{BlockMap, BlockMapEntity} import net.psforever.packet._ -import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeShortcutBankMessage, CharacterRequestMessage, CreateShortcutMessage, DeployObjectMessage, DisplayedAwardMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FavoritesRequest, FriendsRequest, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, SetChatFilterMessage, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UseItemMessage, ZipLineMessage} +import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeShortcutBankMessage, CharacterRequestMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DisplayedAwardMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FavoritesRequest, FriendsRequest, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, SetChatFilterMessage, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UseItemMessage, ZipLineMessage} import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum import net.psforever.packet.game.objectcreate._ import net.psforever.packet.game._ @@ -87,7 +86,7 @@ class SessionData( private val chatActor: typed.ActorRef[ChatActor.Command] = context.spawnAnonymous(ChatActor(context.self, avatarActor)) private[support] val log = org.log4s.getLogger - private[support] var _session: Session = Session() + private[support] var theSession: Session = Session() private[support] var accountIntermediary: ActorRef = Default.Actor private[support] var accountPersistence: ActorRef = Default.Actor private[support] var galaxyService: ActorRef = Default.Actor @@ -98,12 +97,12 @@ class SessionData( private[session] var connectionState: Int = 25 private var recentTeleportAttempt: Long = 0 private[support] var kitToBeUsed: Option[PlanetSideGUID] = None - private[support] var persistFunc: () => Unit = NoPersistence - private[support] var persist: () => Unit = UpdatePersistenceOnly + private[support] var persistFunc: () => Unit = noPersistence + private[support] var persist: () => Unit = updatePersistenceOnly private[support] var specialItemSlotGuid: Option[PlanetSideGUID] = None // If a special item (e.g. LLU) has been attached to the player the GUID should be stored here, or cleared when dropped, since the drop hotkey doesn't send the GUID of the object to be dropped. private[support] var serverTime: Long = 0 //unused? - private[session] var keepAliveFunc: () => Unit = KeepAlivePersistenceInitial + private[session] var keepAliveFunc: () => Unit = keepAlivePersistenceInitial private[support] var turnCounterFunc: PlanetSideGUID => Unit = SessionData.NoTurnCounterYet private var heightLast: Float = 0f private var heightTrend: Boolean = false //up = true, down = false @@ -127,14 +126,998 @@ class SessionData( new SessionMountHandlers(sessionData=this, avatarActor, context) val terminals: SessionTerminalHandlers = new SessionTerminalHandlers(sessionData=this, avatarActor, context) - private var _vehicleResponse: Option[SessionVehicleHandlers] = None - private var _galaxyResponse: Option[SessionGalaxyHandlers] = None - private var _squadResponse: Option[SessionSquadHandlers] = None - private var _zoning: Option[ZoningOperations] = None - def vehicleResponseOperations: SessionVehicleHandlers = _vehicleResponse.orNull - def galaxyResponseHanders: SessionGalaxyHandlers = _galaxyResponse.orNull - def squad: SessionSquadHandlers = _squadResponse.orNull - def zoning: ZoningOperations = _zoning.orNull + private var vehicleResponseOpt: Option[SessionVehicleHandlers] = None + private var galaxyResponseOpt: Option[SessionGalaxyHandlers] = None + private var squadResponseOpt: Option[SessionSquadHandlers] = None + private var zoningOpt: Option[ZoningOperations] = None + def vehicleResponseOperations: SessionVehicleHandlers = vehicleResponseOpt.orNull + def galaxyResponseHanders: SessionGalaxyHandlers = galaxyResponseOpt.orNull + def squad: SessionSquadHandlers = squadResponseOpt.orNull + def zoning: ZoningOperations = zoningOpt.orNull + + def session: Session = theSession + + def session_=(session: Session): Unit = { + chatActor ! ChatActor.SetSession(session) + avatarActor ! AvatarActor.SetSession(session) + theSession = session + } + + def account: Account = theSession.account + + def continent: Zone = theSession.zone + + def player: Player = theSession.player + + def avatar: Avatar = theSession.avatar + + /* packets */ + + def handleConnectToWorldRequest(pkt: ConnectToWorldRequestMessage)(implicit context: ActorContext): Unit = { + val ConnectToWorldRequestMessage(_, token, majorVersion, minorVersion, revision, buildDate, _) = pkt + log.trace( + s"ConnectToWorldRequestMessage: client with versioning $majorVersion.$minorVersion.$revision, $buildDate has sent a token to the server" + ) + sendResponse(ChatMsg(ChatMessageType.CMT_CULLWATERMARK, wideContents=false, "", "", None)) + import scala.concurrent.ExecutionContext.Implicits.global + clientKeepAlive.cancel() + clientKeepAlive = context.system.scheduler.scheduleWithFixedDelay( + initialDelay = 0.seconds, + delay = 500.milliseconds, + context.self, + SessionActor.PokeClient() + ) + accountIntermediary ! RetrieveAccountData(token) + } + + def handleCharacterCreateRequest(pkt: CharacterCreateRequestMessage): Unit = { + val CharacterCreateRequestMessage(name, head, voice, gender, empire) = pkt + avatarActor ! AvatarActor.CreateAvatar(name, head, voice, gender, empire) + } + + def handleCharacterRequest(pkt: CharacterRequestMessage): Unit = { + val CharacterRequestMessage(charId, action) = pkt + action match { + case CharacterRequestAction.Delete => + avatarActor ! AvatarActor.DeleteAvatar(charId.toInt) + case CharacterRequestAction.Select => + avatarActor ! AvatarActor.SelectAvatar(charId.toInt, context.self) + } + } + + def handlePlayerStateUpstream(pkt: PlayerStateMessageUpstream): Unit = { + val PlayerStateMessageUpstream( + avatarGuid, + pos, + vel, + yaw, + pitch, + yawUpper, + seqTime, + _, + isCrouching, + isJumping, + jumpThrust, + isCloaking, + _, + _ + )= pkt + persist() + turnCounterFunc(avatarGuid) + updateBlockMap(player, continent, pos) + val isMoving = WorldEntity.isMoving(vel) + val isMovingPlus = isMoving || isJumping || jumpThrust + if (isMovingPlus) { + zoning.CancelZoningProcessWithDescriptiveReason("cancel_motion") + } + fallHeightTracker(pos.z) + // if (isCrouching && !player.Crouching) { + // //dev stuff goes here + // } + player.Position = pos + player.Velocity = vel + player.Orientation = Vector3(player.Orientation.x, pitch, yaw) + player.FacingYawUpper = yawUpper + player.Crouching = isCrouching + player.Jumping = isJumping + if (isCloaking && !player.Cloaked) { + zoning.CancelZoningProcessWithDescriptiveReason("cancel_cloak") + } + player.Cloaked = player.ExoSuit == ExoSuitType.Infiltration && isCloaking + maxCapacitorTick(jumpThrust) + if (isMovingPlus && terminals.usingMedicalTerminal.isDefined) { + continent.GUID(terminals.usingMedicalTerminal) match { + case Some(term: Terminal with ProximityUnit) => + terminals.StopUsingProximityUnit(term) + case _ => () + } + } + accessedContainer match { + // Ensure we don't unload the contents of the vehicle trunk for players seated in the vehicle. + // This can happen if PSUM arrives during the mounting process + case Some(veh: Vehicle) if player.VehicleSeated.isEmpty || player.VehicleSeated.get != veh.GUID => + if (isMoving || veh.isMoving(test = 1) || Vector3.DistanceSquared(player.Position, veh.TrunkLocation) > 9) { + val guid = player.GUID + sendResponse(UnuseItemMessage(guid, veh.GUID)) + sendResponse(UnuseItemMessage(guid, guid)) + unaccessContainer(veh) + } + case Some(container) => //just in case + if (isMovingPlus && (player.VehicleSeated.isEmpty || player.VehicleSeated.get != container.GUID)) { + // Ensure we don't close the container if the player is seated in it + val guid = player.GUID + // If the container is a corpse and gets removed just as this runs it can cause a client disconnect, so we'll check the container has a GUID first. + if (container.HasGUID) { + sendResponse(UnuseItemMessage(guid, container.GUID)) + } + sendResponse(UnuseItemMessage(guid, guid)) + unaccessContainer(container) + } + case None => () + } + val wepInHand: Boolean = player.Slot(player.DrawnSlot).Equipment match { + case Some(item) => item.Definition == GlobalDefinitions.bolt_driver + case _ => false + } + continent.AvatarEvents ! AvatarServiceMessage( + continent.id, + AvatarAction.PlayerState( + avatarGuid, + player.Position, + player.Velocity, + yaw, + pitch, + yawUpper, + seqTime, + isCrouching, + isJumping, + jumpThrust, + isCloaking, + player.spectator, + wepInHand + ) + ) + squad.updateSquad() + if (player.death_by == -1) { + kickedByAdministration() + } + player.zoneInteractions() + } + + def handleChat(pkt: ChatMsg): Unit = { + chatActor ! ChatActor.Message(pkt) + } + + def handleChatFilter(pkt: SetChatFilterMessage): Unit = { + val SetChatFilterMessage(_, _, _) = pkt + } + + def handleVoiceHostRequest(pkt: VoiceHostRequest): Unit = { + log.debug(s"$pkt") + sendResponse(VoiceHostKill()) + sendResponse( + ChatMsg(ChatMessageType.CMT_OPEN, wideContents=false, "", "Try our Discord at https://discord.gg/0nRe5TNbTYoUruA4", None) + ) + } + + def handleVoiceHostInfo(pkt: VoiceHostInfo): Unit = { + log.debug(s"$pkt") + sendResponse(VoiceHostKill()) + sendResponse( + ChatMsg(ChatMessageType.CMT_OPEN, wideContents=false, "", "Try our Discord at https://discord.gg/0nRe5TNbTYoUruA4", None) + ) + } + + def handleEmote(pkt: EmoteMsg): Unit = { + val EmoteMsg(avatarGuid, emote) = pkt + sendResponse(EmoteMsg(avatarGuid, emote)) + } + + def handleDropItem(pkt: DropItemMessage): Unit = { + val DropItemMessage(itemGuid) = pkt + (validObject(itemGuid, decorator = "DropItem"), player.FreeHand.Equipment) match { + case (Some(anItem: Equipment), Some(heldItem)) + if (anItem eq heldItem) && continent.GUID(player.VehicleSeated).nonEmpty => + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + RemoveOldEquipmentFromInventory(player)(heldItem) + case (Some(anItem: Equipment), Some(heldItem)) + if anItem eq heldItem => + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + DropEquipmentFromInventory(player)(heldItem) + case (Some(anItem: Equipment), _) + if continent.GUID(player.VehicleSeated).isEmpty => + //suppress the warning message if in a vehicle + log.warn(s"DropItem: ${player.Name} wanted to drop a ${anItem.Definition.Name}, but it wasn't at hand") + case (Some(obj), _) => + log.warn(s"DropItem: ${player.Name} wanted to drop a ${obj.Definition.Name}, but it was not equipment") + case _ => () + } + } + + def handlePickupItem(pkt: PickupItemMessage): Unit = { + val PickupItemMessage(itemGuid, _, _, _) = pkt + validObject(itemGuid, decorator = "PickupItem").collect { + case item: Equipment if player.Fit(item).nonEmpty => + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + PickUpEquipmentFromGround(player)(item) + case _: Equipment => + sendResponse(ActionResultMessage.Fail(16)) //error code? + } + } + + def handleObjectHeld(pkt: ObjectHeldMessage): Unit = { + val ObjectHeldMessage(_, heldHolsters, _) = pkt + player.Actor ! PlayerControl.ObjectHeld(heldHolsters) + } + + def handleAvatarJump(pkt: AvatarJumpMessage): Unit = { + val AvatarJumpMessage(_) = pkt + avatarActor ! AvatarActor.ConsumeStamina(10) + avatarActor ! AvatarActor.SuspendStaminaRegeneration(2.5 seconds) + } + + def handleZipLine(pkt: ZipLineMessage): Unit = { + val ZipLineMessage(playerGuid, forwards, action, pathId, pos) = pkt + continent.zipLinePaths.find(x => x.PathId == pathId) match { + case Some(path) if path.IsTeleporter => + zoning.CancelZoningProcessWithDescriptiveReason("cancel") + val endPoint = path.ZipLinePoints.last + sendResponse(ZipLineMessage(PlanetSideGUID(0), forwards, 0, pathId, pos)) + //todo: send to zone to show teleport animation to all clients + sendResponse(PlayerStateShiftMessage(ShiftState(0, endPoint, (player.Orientation.z + player.FacingYawUpper) % 360f, None))) + case Some(_) => + zoning.CancelZoningProcessWithDescriptiveReason("cancel_motion") + action match { + case 0 => + //travel along the zipline in the direction specified + sendResponse(ZipLineMessage(playerGuid, forwards, action, pathId, pos)) + case 1 => + //disembark from zipline at destination! + sendResponse(ZipLineMessage(playerGuid, forwards, action, 0, pos)) + case 2 => + //get off by force + sendResponse(ZipLineMessage(playerGuid, forwards, action, 0, pos)) + case _ => + log.warn( + s"${player.Name} tried to do something with a zipline but can't handle it. forwards: $forwards action: $action pathId: $pathId zone: ${continent.Number} / ${continent.id}" + ) + } + case _ => + log.warn(s"${player.Name} couldn't find a zipline path $pathId in zone ${continent.id}") + } + } + + def handleRequestDestroy(pkt: RequestDestroyMessage): Unit = { + val RequestDestroyMessage(objectGuid) = pkt + //make sure this is the correct response for all cases + validObject(objectGuid, decorator = "RequestDestroy") match { + case Some(vehicle: Vehicle) => + /* line 1a: player is admin (and overrules other access requirements) */ + /* line 1b: vehicle and player (as the owner) acknowledge each other */ + /* line 1c: vehicle is the same faction as player, is ownable, and either the owner is absent or the vehicle is destroyed */ + /* line 2: vehicle is not mounted in anything or, if it is, its seats are empty */ + if ( + (session.account.gm || + (player.avatar.vehicle.contains(objectGuid) && vehicle.Owner.contains(player.GUID)) || + (player.Faction == vehicle.Faction && + (vehicle.Definition.CanBeOwned.nonEmpty && + (vehicle.Owner.isEmpty || continent.GUID(vehicle.Owner.get).isEmpty) || vehicle.Destroyed))) && + (vehicle.MountedIn.isEmpty || !vehicle.Seats.values.exists(_.isOccupied)) + ) { + vehicle.Actor ! Vehicle.Deconstruct() + //log.info(s"RequestDestroy: vehicle $vehicle") + } else { + log.warn(s"RequestDestroy: ${player.Name} must own vehicle in order to deconstruct it") + } + + case Some(obj: Projectile) => + if (!obj.isResolved) { + obj.Miss() + } + continent.Projectile ! ZoneProjectile.Remove(objectGuid) + + case Some(obj: BoomerTrigger) => + if (findEquipmentToDelete(objectGuid, obj)) { + continent.GUID(obj.Companion) match { + case Some(boomer: BoomerDeployable) => + boomer.Trigger = None + boomer.Actor ! Deployable.Deconstruct() + case Some(thing) => + log.warn(s"RequestDestroy: BoomerTrigger object connected to wrong object - $thing") + case None => () + } + } + + case Some(obj: Deployable) => + if (session.account.gm || obj.Owner.isEmpty || obj.Owner.contains(player.GUID) || obj.Destroyed) { + obj.Actor ! Deployable.Deconstruct() + } else { + log.warn(s"RequestDestroy: ${player.Name} must own the deployable in order to deconstruct it") + } + + case Some(obj: Equipment) => + findEquipmentToDelete(objectGuid, obj) + + case Some(thing) => + log.warn(s"RequestDestroy: not allowed to delete this ${thing.Definition.Name}") + + case None => () + } + } + + def handleMoveItem(pkt: MoveItemMessage): Unit = { + val MoveItemMessage(itemGuid, sourceGuid, destinationGuid, dest, _) = pkt + ( + continent.GUID(sourceGuid), + continent.GUID(destinationGuid), + validObject(itemGuid, decorator = "MoveItem") + ) match { + case ( + Some(source: PlanetSideServerObject with Container), + Some(destination: PlanetSideServerObject with Container), + Some(item: Equipment) + ) => + ContainableMoveItem(player.Name, source, destination, item, destination.SlotMapResolution(dest)) + case (None, _, _) => + log.error( + s"MoveItem: ${player.Name} wanted to move $itemGuid from $sourceGuid, but could not find source object" + ) + case (_, None, _) => + log.error( + s"MoveItem: ${player.Name} wanted to move $itemGuid to $destinationGuid, but could not find destination object" + ) + case (_, _, None) => () + case _ => + log.error( + s"MoveItem: ${player.Name} wanted to move $itemGuid from $sourceGuid to $destinationGuid, but multiple problems were encountered" + ) + } + } + + def handleLootItem(pkt: LootItemMessage): Unit = { + val LootItemMessage(itemGuid, targetGuid) = pkt + (validObject(itemGuid, decorator = "LootItem"), continent.GUID(targetGuid)) match { + case (Some(item: Equipment), Some(destination: PlanetSideServerObject with Container)) => + //figure out the source + ( + { + val findFunc: PlanetSideServerObject with Container => Option[ + (PlanetSideServerObject with Container, Option[Int]) + ] = findInLocalContainer(itemGuid) + findFunc(player.avatar.locker) + .orElse(findFunc(player)) + .orElse(accessedContainer match { + case Some(parent: PlanetSideServerObject) => + findFunc(parent) + case _ => + None + }) + }, + destination.Fit(item) + ) match { + case (Some((source, Some(_))), Some(dest)) => + ContainableMoveItem(player.Name, source, destination, item, dest) + case (None, _) => + log.error(s"LootItem: ${player.Name} can not find where $item is put currently") + case (_, None) => + log.error(s"LootItem: ${player.Name} can not find anywhere to put $item in $destination") + case _ => + log.error( + s"LootItem: ${player.Name}wanted to move $itemGuid to $targetGuid, but multiple problems were encountered" + ) + } + case (Some(obj), _) => + log.error(s"LootItem: item $obj is (probably) not lootable to ${player.Name}") + case (None, _) => () + case (_, None) => + log.error(s"LootItem: ${player.Name} can not find where to put $itemGuid") + } + } + + def handleAvatarImplant(pkt: AvatarImplantMessage): Unit = { + val AvatarImplantMessage(_, action, slot, status) = pkt + if (action == ImplantAction.Activation) { + zoning.CancelZoningProcessWithDescriptiveReason("cancel_implant") + avatar.implants(slot) match { + case Some(implant) => + if (status == 1) { + avatarActor ! AvatarActor.ActivateImplant(implant.definition.implantType) + } else { + avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType) + } + case _ => log.error(s"AvatarImplantMessage: ${player.Name} has an unknown implant in $slot") + } + } + } + + def handleUseItem(pkt: UseItemMessage): Unit = { + val equipment = findContainedEquipment(pkt.item_used_guid) match { + case (o @ Some(_), a) if a.exists(_.isInstanceOf[Tool]) => + shooting.FindEnabledWeaponsToHandleWeaponFireAccountability(o, a.collect { case w: Tool => w })._2.headOption + case (Some(_), a) => + a.headOption + case _ => + None + } + validObject(pkt.object_guid, decorator = "UseItem") match { + case Some(door: Door) => + handleUseDoor(door) + case Some(resourceSilo: ResourceSilo) => + handleUseResourceSilo(resourceSilo, equipment) + case Some(panel: IFFLock) => + handleUseGeneralEntity(panel, equipment) + case Some(obj: Player) => + handleUsePlayer(obj, equipment, pkt) + case Some(locker: Locker) => + handleUseLocker(locker, equipment, pkt) + case Some(gen: Generator) => + handleUseGeneralEntity(gen, equipment) + case Some(mech: ImplantTerminalMech) => + handleUseGeneralEntity(mech, equipment) + case Some(captureTerminal: CaptureTerminal) => + handleUseCaptureTerminal(captureTerminal, equipment) + case Some(obj: FacilityTurret) => + handleUseFacilityTurret(obj, equipment, pkt) + case Some(obj: Vehicle) => + handleUseVehicle(obj, equipment, pkt) + case Some(terminal: Terminal) => + handleUseTerminal(terminal, equipment, pkt) + case Some(obj: SpawnTube) => + handleUseSpawnTube(obj, equipment) + case Some(obj: SensorDeployable) => + handleUseGeneralEntity(obj, equipment) + case Some(obj: TurretDeployable) => + handleUseGeneralEntity(obj, equipment) + case Some(obj: TrapDeployable) => + handleUseGeneralEntity(obj, equipment) + case Some(obj: ShieldGeneratorDeployable) => + handleUseGeneralEntity(obj, equipment) + case Some(obj: TelepadDeployable) => + handleUseTelepadDeployable(obj, equipment, pkt) + case Some(obj: Utility.InternalTelepad) => + handleUseInternalTelepad(obj, pkt) + case Some(obj: CaptureFlag) => + handleUseCaptureFlag(obj) + case Some(_: WarpGate) => + handleUseWarpGate(equipment) + case Some(obj) => + handleUseDefaultEntity(obj, equipment) + case None => () + } + } + + def handleUnuseItem(pkt: UnuseItemMessage): Unit = { + val UnuseItemMessage(_, objectGuid) = pkt + validObject(objectGuid, decorator = "UnuseItem") match { + case Some(obj: Player) => + unaccessContainer(obj) + zoning.spawn.TryDisposeOfLootedCorpse(obj) + case Some(obj: Container) => + // Make sure we don't unload the contents of the vehicle the player is seated in + // An example scenario of this would be closing the trunk contents when rearming at a landing pad + if (player.VehicleSeated.isEmpty || player.VehicleSeated.get != obj.GUID) { + unaccessContainer(obj) + } + case _ => () + } + } + + def handleDeployObject(pkt: DeployObjectMessage): Unit = { + val DeployObjectMessage(guid, _, pos, orient, _) = pkt + player.Holsters().find(slot => slot.Equipment.nonEmpty && slot.Equipment.get.GUID == guid).flatMap { slot => slot.Equipment } match { + case Some(obj: ConstructionItem) => + val ammoType = obj.AmmoType match { + case DeployedItem.portable_manned_turret => GlobalDefinitions.PortableMannedTurret(player.Faction).Item + case dtype => dtype + } + log.info(s"${player.Name} is constructing a $ammoType deployable") + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + val dObj: Deployable = Deployables.Make(ammoType)() + dObj.Position = pos + dObj.Orientation = orient + dObj.Faction = player.Faction + dObj.AssignOwnership(player) + val tasking: TaskBundle = dObj match { + case turret: TurretDeployable => + GUIDTask.registerDeployableTurret(continent.GUID, turret) + case _ => + GUIDTask.registerObject(continent.GUID, dObj) + } + TaskWorkflow.execute(CallBackForTask(tasking, continent.Deployables, Zone.Deployable.BuildByOwner(dObj, player, obj))) + case Some(obj) => + log.warn(s"DeployObject: what is $obj, ${player.Name}? It's not a construction tool!") + case None => + log.error(s"DeployObject: nothing, ${player.Name}? It's not a construction tool!") + } + } + + def handlePlanetsideAttribute(pkt: PlanetsideAttributeMessage): Unit = { + val PlanetsideAttributeMessage(objectGuid, attributeType, attributeValue) = pkt + validObject(objectGuid, decorator = "PlanetsideAttribute") match { + case Some(vehicle: Vehicle) if player.avatar.vehicle.contains(vehicle.GUID) => + vehicle.Actor ! ServerObject.AttributeMsg(attributeType, attributeValue) + case Some(vehicle: Vehicle) => + log.warn(s"PlanetsideAttribute: ${player.Name} does not own vehicle ${vehicle.GUID} and can not change it") + // Cosmetics options + case Some(_: Player) if attributeType == 106 => + avatarActor ! AvatarActor.SetCosmetics(Cosmetic.valuesFromAttributeValue(attributeValue)) + case Some(obj) => + log.trace(s"PlanetsideAttribute: ${player.Name} does not know how to apply unknown attributes behavior $attributeType to ${obj.Definition.Name}") + case _ => () + } + } + + def handleGenericObjectAction(pkt: GenericObjectActionMessage): Unit = { + val GenericObjectActionMessage(objectGuid, code) = pkt + validObject(objectGuid, decorator = "GenericObjectAction") match { + case Some(vehicle: Vehicle) + if vehicle.OwnerName.contains(player.Name) => + vehicle.Actor ! ServerObject.GenericObjectAction(objectGuid, code, Some(player.GUID)) + + case Some(tool: Tool) => + if (code == 35 && + (tool.Definition == GlobalDefinitions.maelstrom || tool.Definition.Name.startsWith("aphelion_laser")) + ) { + //maelstrom primary fire mode discharge (no target) + //aphelion_laser discharge (no target) + shooting.HandleWeaponFireAccountability(objectGuid, PlanetSideGUID(Projectile.baseUID)) + } else { + validObject(player.VehicleSeated, decorator = "GenericObjectAction/Vehicle") match { + case Some(vehicle: Vehicle) + if vehicle.OwnerName.contains(player.Name) => + vehicle.Actor ! ServerObject.GenericObjectAction(objectGuid, code, Some(tool)) + case _ => + } + } + case _ => + log.info(s"${player.Name} - $pkt") + } + } + + def handleGenericObjectActionAtPosition(pkt: GenericObjectActionAtPositionMessage): Unit = { + val GenericObjectActionAtPositionMessage(objectGuid, _, _) = pkt + validObject(objectGuid, decorator = "GenericObjectActionAtPosition") match { + case Some(tool: Tool) if GlobalDefinitions.isBattleFrameNTUSiphon(tool.Definition) => + shooting.FindContainedWeapon match { + case (Some(vehicle: Vehicle), weps) if weps.exists(_.GUID == objectGuid) => + vehicle.Actor ! SpecialEmp.Burst() + case _ => () + } + case _ => + log.info(s"${player.Name} - $pkt") + } + } + + def handleGenericObjectState(pkt: GenericObjectStateMsg): Unit = { + val GenericObjectStateMsg(_, _) = pkt + log.info(s"${player.Name} - $pkt") + } + + def handleGenericAction(pkt: GenericActionMessage): Unit = { + val GenericActionMessage(action) = pkt + if (player == null) { + if (action == GenericAction.AwayFromKeyboard_RCV) { + log.debug("GenericObjectState: AFK state reported during login") + } + } else { + val (toolOpt, definition) = player.Slot(0).Equipment match { + case Some(tool: Tool) => + (Some(tool), tool.Definition) + case _ => + (None, GlobalDefinitions.bullet_9mm) + } + action match { + case GenericAction.DropSpecialItem => + dropSpecialSlotItem() + case GenericAction.MaxAnchorsExtend_RCV => + log.info(s"${player.Name} has anchored ${player.Sex.pronounObject}self to the ground") + player.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored + continent.AvatarEvents ! AvatarServiceMessage( + continent.id, + AvatarAction.PlanetsideAttribute(player.GUID, 19, 1) + ) + definition match { + case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.trhev_burster => + val tool = toolOpt.get + tool.ToFireMode = 1 + sendResponse(ChangeFireModeMessage(tool.GUID, 1)) + case GlobalDefinitions.trhev_pounder => + val tool = toolOpt.get + val convertFireModeIndex = if (tool.FireModeIndex == 0) { 1 } + else { 4 } + tool.ToFireMode = convertFireModeIndex + sendResponse(ChangeFireModeMessage(tool.GUID, convertFireModeIndex)) + case _ => + log.warn(s"GenericObject: ${player.Name} is a MAX with an unexpected attachment - ${definition.Name}") + } + case GenericAction.MaxAnchorsRelease_RCV => + log.info(s"${player.Name} has released the anchors") + player.UsingSpecial = SpecialExoSuitDefinition.Mode.Normal + continent.AvatarEvents ! AvatarServiceMessage( + continent.id, + AvatarAction.PlanetsideAttribute(player.GUID, 19, 0) + ) + definition match { + case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.trhev_burster => + val tool = toolOpt.get + tool.ToFireMode = 0 + sendResponse(ChangeFireModeMessage(tool.GUID, 0)) + case GlobalDefinitions.trhev_pounder => + val tool = toolOpt.get + val convertFireModeIndex = if (tool.FireModeIndex == 1) { 0 } else { 3 } + tool.ToFireMode = convertFireModeIndex + sendResponse(ChangeFireModeMessage(tool.GUID, convertFireModeIndex)) + case _ => + log.warn(s"GenericObject: $player is MAX with an unexpected attachment - ${definition.Name}") + } + case GenericAction.MaxSpecialEffect_RCV => + if (player.ExoSuit == ExoSuitType.MAX) { + toggleMaxSpecialState(enable = true) + } else { + log.warn(s"GenericActionMessage: ${player.Name} can't handle MAX special effect") + } + case GenericAction.StopMaxSpecialEffect_RCV => + if (player.ExoSuit == ExoSuitType.MAX) { + player.Faction match { + case PlanetSideEmpire.NC => + toggleMaxSpecialState(enable = false) + case _ => + log.warn(s"GenericActionMessage: ${player.Name} tried to cancel an uncancellable MAX special ability") + } + } else { + log.warn(s"GenericActionMessage: ${player.Name} can't stop MAX special effect") + } + case GenericAction.AwayFromKeyboard_RCV => + log.info(s"${player.Name} is AFK") + AvatarActor.savePlayerLocation(player) + displayCharSavedMsgThenRenewTimer(fixedLen=1800L, varLen=0L) //~30min + player.AwayFromKeyboard = true + case GenericAction.BackInGame_RCV => + log.info(s"${player.Name} is back") + player.AwayFromKeyboard = false + renewCharSavedTimer( + Config.app.game.savedMsg.renewal.fixed, + Config.app.game.savedMsg.renewal.variable + ) + case GenericAction.LookingForSquad_RCV => //Looking For Squad ON + if (!avatar.lookingForSquad && (squad.squadUI.isEmpty || squad.squadUI(player.CharId).index == 0)) { + avatarActor ! AvatarActor.SetLookingForSquad(true) + } + case GenericAction.NotLookingForSquad_RCV => //Looking For Squad OFF + if (avatar.lookingForSquad && (squad.squadUI.isEmpty || squad.squadUI(player.CharId).index == 0)) { + avatarActor ! AvatarActor.SetLookingForSquad(false) + } + case _ => + log.warn(s"GenericActionMessage: ${player.Name} can't handle $action") + } + } + } + + def handleFavoritesRequest(pkt: FavoritesRequest): Unit = { + val FavoritesRequest(_, loadoutType, action, line, label) = pkt + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + action match { + case FavoritesAction.Save => + avatarActor ! AvatarActor.SaveLoadout(player, loadoutType, label, line) + case FavoritesAction.Delete => + avatarActor ! AvatarActor.DeleteLoadout(player, loadoutType, line) + case FavoritesAction.Unknown => + log.warn(s"FavoritesRequest: ${player.Name} requested an unknown favorites action") + } + } + + def handleGenericCollision(pkt: GenericCollisionMsg): Unit = { + val GenericCollisionMsg(ctype, p, _, ppos, pv, t, _, tpos, tv, _, _, _) = pkt + val fallHeight = { + if (pv.z * pv.z >= (pv.x * pv.x + pv.y * pv.y) * 0.5f) { + if (heightTrend) { + val fall = heightLast - heightHistory + heightHistory = heightLast + fall + } + else { + val fall = heightHistory - heightLast + heightLast = heightHistory + fall + } + } else { + 0f + } + } + val (target1, target2, bailProtectStatus, velocity) = (ctype, validObject(p, decorator = "GenericCollision/Primary")) match { + case (CollisionIs.OfInfantry, out @ Some(user: Player)) + if user == player => + val bailStatus = session.flying || player.spectator || session.speed > 1f || player.BailProtection + player.BailProtection = false + val v = if (player.avatar.implants.exists { + case Some(implant) => implant.definition.implantType == ImplantType.Surge && implant.active + case _ => false + }) { + Vector3.Zero + } else { + pv + } + (out, None, bailStatus, v) + case (CollisionIs.OfGroundVehicle, out @ Some(v: Vehicle)) + if v.Seats(0).occupant.contains(player) => + val bailStatus = v.BailProtection + v.BailProtection = false + (out, validObject(t, decorator = "GenericCollision/GroundVehicle"), bailStatus, pv) + case (CollisionIs.OfAircraft, out @ Some(v: Vehicle)) + if v.Definition.CanFly && v.Seats(0).occupant.contains(player) => + (out, validObject(t, decorator = "GenericCollision/Aircraft"), false, pv) + case (CollisionIs.BetweenThings, _) => + log.warn("GenericCollision: CollisionIs.BetweenThings detected - no handling case") + (None, None, false, Vector3.Zero) + case _ => + (None, None, false, Vector3.Zero) + } + val curr = System.currentTimeMillis() + (target1, t, target2) match { + case (None, _, _) => () + + case (Some(us: PlanetSideServerObject with Vitality with FactionAffinity), PlanetSideGUID(0), _) => + if (collisionHistory.get(us.Actor) match { + case Some(lastCollision) if curr - lastCollision <= 1000L => + false + case _ => + collisionHistory.put(us.Actor, curr) + true + }) { + if (!bailProtectStatus) { + handleDealingDamage( + us, + DamageInteraction( + SourceEntry(us), + CollisionReason(velocity, fallHeight, us.DamageModel), + ppos + ) + ) + } + } + + case ( + Some(us: PlanetSideServerObject with Vitality with FactionAffinity), _, + Some(victim: PlanetSideServerObject with Vitality with FactionAffinity) + ) => + if (collisionHistory.get(victim.Actor) match { + case Some(lastCollision) if curr - lastCollision <= 1000L => + false + case _ => + collisionHistory.put(victim.Actor, curr) + true + }) { + val usSource = SourceEntry(us) + val victimSource = SourceEntry(victim) + //we take damage from the collision + if (!bailProtectStatus) { + handleDealingDamage( + us, + DamageInteraction( + usSource, + CollisionWithReason(CollisionReason(velocity - tv, fallHeight, us.DamageModel), victimSource), + ppos + ) + ) + } + //get dealt damage from our own collision (no protection) + collisionHistory.put(us.Actor, curr) + handleDealingDamage( + victim, + DamageInteraction( + victimSource, + CollisionWithReason(CollisionReason(tv - velocity, 0, victim.DamageModel), usSource), + tpos + ) + ) + } + + case _ => () + } + } + + def handleAvatarFirstTimeEvent(pkt: AvatarFirstTimeEventMessage): Unit = { + val AvatarFirstTimeEventMessage(_, _, _, eventName) = pkt + avatarActor ! AvatarActor.AddFirstTimeEvent(eventName) + } + + def handleBugReport(pkt: PlanetSideGamePacket): Unit = { + val BugReportMessage( + _/*versionMajor*/, + _/*versionMinor*/, + _/*versionDate*/, + _/*bugType*/, + _/*repeatable*/, + _/*location*/, + _/*zone*/, + _/*pos*/, + _/*summary*/, + _/*desc*/ + ) = pkt + log.warn(s"${player.Name} filed a bug report - it might be something important") + log.debug(s"$pkt") + } + + def handleFacilityBenefitShieldChargeRequest(pkt: FacilityBenefitShieldChargeRequestMessage): Unit = { + val FacilityBenefitShieldChargeRequestMessage(_) = pkt + continent.GUID(player.VehicleSeated) match { + case Some(obj) if obj.Destroyed => () //vehicle will try to charge even if destroyed + case Some(obj: Vehicle) => + obj.Actor ! Vehicle.ChargeShields(15) + case Some(_: TurretDeployable) => () //TODO the turret will charge a shield in some circumstances + case None if player.VehicleSeated.nonEmpty => + log.error( + s"FacilityBenefitShieldChargeRequest: ${player.Name} is seated in a vehicle that can not be found in zone ${continent.id}" + ) + case _ => + log.warn(s"FacilityBenefitShieldChargeRequest: ${player.Name} is seated in an imaginary vehicle") + } + } + + def handleBattleplan(pkt: BattleplanMessage): Unit = { + val BattleplanMessage(_, name, _, _) = pkt + val lament: String = s"$name has a brilliant idea that no one will ever see" + log.info(lament) + log.debug(s"Battleplan: $lament - $pkt") + } + + def handleBindPlayer(pkt: BindPlayerMessage): Unit = { + val BindPlayerMessage(_, _, _, _, _, _, _, _) = pkt + } + + def handleCreateShortcut(pkt: CreateShortcutMessage): Unit = { + val CreateShortcutMessage(_, slot, shortcutOpt) = pkt + shortcutOpt match { + case Some(shortcut) => + avatarActor ! AvatarActor.AddShortcut(slot - 1, shortcut) + case None => + avatarActor ! AvatarActor.RemoveShortcut(slot - 1) + } + } + + def handleChangeShortcutBank(pkt: ChangeShortcutBankMessage): Unit = { + val ChangeShortcutBankMessage(_, _) = pkt + } + + def handleFriendRequest(pkt: FriendsRequest): Unit = { + val FriendsRequest(action, name) = pkt + avatarActor ! AvatarActor.MemberListRequest(action, name) + } + + def handleInvalidTerrain(pkt: InvalidTerrainMessage): Unit = { + val InvalidTerrainMessage(_, vehicleGuid, alert, _) = pkt + (continent.GUID(vehicleGuid), continent.GUID(player.VehicleSeated)) match { + case (Some(packetVehicle: Vehicle), Some(playerVehicle: Vehicle)) if packetVehicle eq playerVehicle => + if (alert == TerrainCondition.Unsafe) { + log.info(s"${player.Name}'s ${packetVehicle.Definition.Name} is approaching terrain unsuitable for idling") + } + case (Some(packetVehicle: Vehicle), Some(_: Vehicle)) => + if (alert == TerrainCondition.Unsafe) { + log.info(s"${packetVehicle.Definition.Name}@${packetVehicle.GUID} is approaching terrain unsuitable for idling, but is not ${player.Name}'s vehicle") + } + case (Some(_: Vehicle), _) => + log.warn(s"InvalidTerrain: ${player.Name} is not seated in a(ny) vehicle near unsuitable terrain") + case (Some(packetThing), _) => + log.warn(s"InvalidTerrain: ${player.Name} thinks that ${packetThing.Definition.Name}@${packetThing.GUID} is near unsuitable terrain") + case _ => + log.error(s"InvalidTerrain: ${player.Name} is complaining about a thing@$vehicleGuid that can not be found") + } + } + + def handleActionCancel(pkt: ActionCancelMessage): Unit = { + val ActionCancelMessage(_, _, _) = pkt + progressBarUpdate.cancel() + progressBarValue = None + } + + def handleTrade(pkt: TradeMessage): Unit = { + val TradeMessage(trade) = pkt + log.trace(s"${player.Name} wants to trade for some reason - $trade") + } + + def handleDisplayedAward(pkt: DisplayedAwardMessage): Unit = { + val DisplayedAwardMessage(_, ribbon, bar) = pkt + log.trace(s"${player.Name} changed the $bar displayed award ribbon to $ribbon") + avatarActor ! AvatarActor.SetRibbon(ribbon, bar) + } + + def handleObjectDetected(pkt: ObjectDetectedMessage): Unit = { + val ObjectDetectedMessage(_, _, _, targets) = pkt + shooting.FindWeapon.foreach { + case weapon if weapon.Projectile.AutoLock => + //projectile with auto-lock instigates a warning on the target + val detectedTargets = shooting.FindDetectedProjectileTargets(targets) + val mode = 7 + (weapon.Projectile == GlobalDefinitions.wasp_rocket_projectile) + detectedTargets.foreach { target => + continent.AvatarEvents ! AvatarServiceMessage(target, AvatarAction.ProjectileAutoLockAwareness(mode)) + } + case _ => () + } + } + + def handleTargetingImplantRequest(pkt: TargetingImplantRequest): Unit = { + val TargetingImplantRequest(list) = pkt + val targetInfo: List[TargetInfo] = list.flatMap { x => + continent.GUID(x.target_guid) match { + case Some(player: Player) => + val health = player.Health.toFloat / player.MaxHealth + val armor = if (player.MaxArmor > 0) { + player.Armor.toFloat / player.MaxArmor + } else { + 0 + } + Some(TargetInfo(player.GUID, health, armor)) + case _ => + log.warn( + s"TargetingImplantRequest: the info that ${player.Name} requested for target ${x.target_guid} is not for a player" + ) + None + } + } + sendResponse(TargetingInfoMessage(targetInfo)) + } + + def handleHitHint(pkt: HitHint): Unit = { + val HitHint(_, _) = pkt + } + + /* messages */ + + def handleSetAvatar(avatar: Avatar): Unit = { + session = session.copy(avatar = avatar) + if (session.player != null) { + session.player.avatar = avatar + } + LivePlayerList.Update(avatar.id, avatar) + } + + def handleReceiveAccountData(account: Account): Unit = { + log.trace(s"ReceiveAccountData $account") + session = session.copy(account = account) + avatarActor ! AvatarActor.SetAccount(account) + } + + def handleUpdateIgnoredPlayers: PlanetSideGamePacket => Unit = { + case msg: FriendsResponse => + sendResponse(msg) + msg.friends.foreach { f => + galaxyService ! GalaxyServiceMessage(GalaxyAction.LogStatusChange(f.name)) + } + case _ => () + } + + def handleUseCooldownRenew: BasicDefinition => Unit = { + case _: KitDefinition => kitToBeUsed = None + case _ => () + } + + def handleAvatarResponse(avatar: Avatar): Unit = { + session = session.copy(avatar = avatar) + accountPersistence ! AccountPersistenceService.Login(avatar.name, avatar.id) + } + + def handleSetSpeed(speed: Float): Unit = { + session = session.copy(speed = speed) + } + + def handleSetFlying(flying: Boolean): Unit = { + session = session.copy(flying = flying) + } + + def handleSetSpectator(spectator: Boolean): Unit = { + session.player.spectator = spectator + } + + def handleKick(player: Player, time: Option[Long]): Unit = { + administrativeKick(player) + accountPersistence ! AccountPersistenceService.Kick(player.Name, time) + } + + def handleSilenced(isSilenced: Boolean): Unit = { + player.silenced = isSilenced + } + + /* supporting functions */ val sessionSupervisorStrategy: SupervisorStrategy = { import net.psforever.objects.inventory.InventoryDisarrayException @@ -155,43 +1138,39 @@ class SessionData( case e: Equipment => ( player.Holsters().zipWithIndex.flatMap { case (o, i) => - o.Equipment match { - case Some(e) => Some((player, InventoryItem(e, i))) - case None => None - } + o.Equipment.flatMap { item => Some((player, InventoryItem(item, i))) } }.toList ++ - player.Inventory.Items.map { o => (player, o) } ++ { - player.FreeHand.Equipment match { - case Some(_) => List((player, InventoryItem(e, Player.FreeHandSlot))) - case _ => Nil - } - } ++ - (ValidObject(player.VehicleSeated) match { + player.Inventory.Items.map { o => (player, o) } ++ + player.FreeHand.Equipment.flatMap { item => Some((player, InventoryItem(item, Player.FreeHandSlot))) }.toList ++ + (validObject(player.VehicleSeated) match { case Some(v: Vehicle) => v.Trunk.Items.map { o => (v, o) } case _ => Nil }) ) .find { case (_, InventoryItem(o, _)) => o eq e } match { - case Some((c: Container, InventoryItem(obj, index))) => - if (!obj.HasGUID) { - c.Slot(index).Equipment = None - } - c match { - case _: Player => - attemptRecoveryFromNoGuidExceptionAsPlayer(player, nge) - case v: Vehicle => - if (v.PassengerInSeat(player).contains(0)) { - attemptRecoveryFromNoGuidExceptionAsPlayer(player, nge) - } - SupervisorStrategy.resume - case _ => - writeLogExceptionAndStop(nge) - } + case Some((p: Player, InventoryItem(obj, index))) if !obj.HasGUID => + p.Slot(index).Equipment = None + attemptRecoveryFromNoGuidExceptionAsPlayer(p, nge) + case Some((p: Player, _)) => + attemptRecoveryFromNoGuidExceptionAsPlayer(p, nge) + + case Some((v: Vehicle, InventoryItem(obj, index))) if !obj.HasGUID && v.PassengerInSeat(player).contains(0) => + v.Slot(index).Equipment = None + attemptRecoveryFromNoGuidExceptionAsPlayer(player, nge) + SupervisorStrategy.resume + case Some((v: Vehicle, InventoryItem(obj, index))) if !obj.HasGUID => + v.Slot(index).Equipment = None + SupervisorStrategy.resume + case Some((v: Vehicle, _)) if v.PassengerInSeat(player).contains(0) => + attemptRecoveryFromNoGuidExceptionAsPlayer(player, nge) + SupervisorStrategy.resume + case Some((_: Vehicle, _)) => + SupervisorStrategy.resume + case _ => //did not discover or resolve the situation writeLogExceptionAndStop(nge) } - case _ => SupervisorStrategy.resume } @@ -236,13 +1215,12 @@ class SessionData( case Some(false) => zoning.RequestSanctuaryZoneSpawn(p, continent.Number) SupervisorStrategy.resume + case None if player.Zone eq Zone.Nowhere => + zoning.RequestSanctuaryZoneSpawn(p, continent.Number) + SupervisorStrategy.resume case None => - if (player.Zone eq Zone.Nowhere) { - zoning.RequestSanctuaryZoneSpawn(p, continent.Number) - } else { - zoning.zoneReload = true - zoning.LoadZoneAsPlayer(player, player.Zone.id) - } + zoning.zoneReload = true + zoning.LoadZoneAsPlayer(player, player.Zone.id) SupervisorStrategy.resume case _ => writeLogExceptionAndStop(e) @@ -254,7 +1232,8 @@ class SessionData( def attemptRecoveryFromInventoryDisarrayException(inv: GridInventory): Unit = { inv.ElementsInListCollideInGrid() match { - case Nil => ; + case Nil => () + case list if list.isEmpty => () case overlaps => val previousItems = inv.Clear() val allOverlaps = overlaps.flatten.sortBy { entry => @@ -284,33 +1263,27 @@ class SessionData( InfantryLoadout.DetermineSubtypeA(player.ExoSuit, equipmentInHand) )) //redraw item in free hand (if) - player.FreeHand.Equipment match { - case Some(item) => - sendResponse(ObjectCreateDetailedMessage( - item.Definition.ObjectId, - item.GUID, - ObjectCreateMessageParent(pguid, Player.FreeHandSlot), - item.Definition.Packet.DetailedConstructorData(item).get - )) - case _ => ; + player.FreeHand.Equipment.foreach { item => + sendResponse(ObjectCreateDetailedMessage( + item.Definition.ObjectId, + item.GUID, + ObjectCreateMessageParent(pguid, Player.FreeHandSlot), + item.Definition.Packet.DetailedConstructorData(item).get + )) } //redraw items in holsters player.Holsters().zipWithIndex.foreach { case (slot, _) => - slot.Equipment match { - case Some(item) => - sendResponse(ObjectCreateDetailedMessage( - item.Definition.ObjectId, - item.GUID, - item.Definition.Packet.DetailedConstructorData(item).get - )) - case _ => ; + slot.Equipment.foreach { item => + sendResponse(ObjectCreateDetailedMessage( + item.Definition.ObjectId, + item.GUID, + item.Definition.Packet.DetailedConstructorData(item).get + )) } } //redraw raised hand (if) - equipmentInHand match { - case Some(_) => - sendResponse(ObjectHeldMessage(pguid, player.DrawnSlot, unk1 = true)) - case _ => ; + equipmentInHand.foreach { _ => + sendResponse(ObjectHeldMessage(pguid, player.DrawnSlot, unk1 = true)) } //redraw inventory items val recoveredItems = inv.Items @@ -348,1384 +1321,76 @@ class SessionData( def writeLogExceptionAndStop(e: Throwable): SupervisorStrategy.Directive = { writeLogException(e) - ImmediateDisconnect() + immediateDisconnect() SupervisorStrategy.stop } - def session: Session = _session - - def session_=(session: Session): Unit = { - chatActor ! ChatActor.SetSession(session) - avatarActor ! AvatarActor.SetSession(session) - _session = session - } - - def account: Account = _session.account - - def continent: Zone = _session.zone - - def player: Player = _session.player - - def avatar: Avatar = _session.avatar - - /* packets */ - - def handleConnectToWorldRequest(pkt: PlanetSideGamePacket)(implicit context: ActorContext): Unit = { - pkt match { - case ConnectToWorldRequestMessage(_, token, majorVersion, minorVersion, revision, buildDate, _) => - log.trace( - s"ConnectToWorldRequestMessage: client with versioning $majorVersion.$minorVersion.$revision, $buildDate has sent a token to the server" - ) - sendResponse(ChatMsg(ChatMessageType.CMT_CULLWATERMARK, wideContents=false, "", "", None)) - import scala.concurrent.ExecutionContext.Implicits.global - clientKeepAlive.cancel() - clientKeepAlive = context.system.scheduler.scheduleWithFixedDelay( - initialDelay = 0.seconds, - delay = 500.milliseconds, - context.self, - SessionActor.PokeClient() - ) - accountIntermediary ! RetrieveAccountData(token) - case _ => ; + def buildDependentOperationsForGalaxy(galaxyActor: ActorRef): Unit = { + if (vehicleResponseOpt.isEmpty && galaxyActor != Default.Actor) { + galaxyResponseOpt = Some(new SessionGalaxyHandlers(sessionData=this, avatarActor, galaxyActor, context)) + vehicleResponseOpt = Some(new SessionVehicleHandlers(sessionData=this, avatarActor, galaxyActor, context)) } } - def handleCharacterCreateRequest(pkt: CharacterCreateRequestMessage): Unit = { - val CharacterCreateRequestMessage(name, head, voice, gender, empire) = pkt - avatarActor ! AvatarActor.CreateAvatar(name, head, voice, gender, empire) - } - - def handleCharacterRequest(pkt: CharacterRequestMessage): Unit = { - val CharacterRequestMessage(charId, action) = pkt - action match { - case CharacterRequestAction.Delete => - avatarActor ! AvatarActor.DeleteAvatar(charId.toInt) - case CharacterRequestAction.Select => - avatarActor ! AvatarActor.SelectAvatar(charId.toInt, context.self) + def buildDependentOperations(galaxyActor: ActorRef, clusterActor: typed.ActorRef[ICS.Command]): Unit = { + if (zoningOpt.isEmpty && galaxyActor != Default.Actor && clusterActor != Default.typed.Actor) { + zoningOpt = Some(new ZoningOperations(sessionData=this, avatarActor, galaxyActor, clusterActor, context)) } } - def handlePlayerStateUpstream(pkt: PlayerStateMessageUpstream): Unit = { - val PlayerStateMessageUpstream( - avatar_guid, - pos, - vel, - yaw, - pitch, - yaw_upper, - seq_time, - _, - is_crouching, - is_jumping, - jump_thrust, - is_cloaking, - _, - _ - )= pkt - persist() - turnCounterFunc(avatar_guid) - updateBlockMap(player, continent, pos) - val isMoving = WorldEntity.isMoving(vel) - val isMovingPlus = isMoving || is_jumping || jump_thrust - if (isMovingPlus) { - zoning.CancelZoningProcessWithDescriptiveReason("cancel_motion") - } - fallHeightTracker(pos.z) - // if (is_crouching && !player.Crouching) { - // //dev stuff goes here - // } - player.Position = pos - player.Velocity = vel - player.Orientation = Vector3(player.Orientation.x, pitch, yaw) - player.FacingYawUpper = yaw_upper - player.Crouching = is_crouching - player.Jumping = is_jumping - if (is_cloaking && !player.Cloaked) { - zoning.CancelZoningProcessWithDescriptiveReason("cancel_cloak") - } - player.Cloaked = player.ExoSuit == ExoSuitType.Infiltration && is_cloaking - CapacitorTick(jump_thrust) - if (isMovingPlus && terminals.usingMedicalTerminal.isDefined) { - continent.GUID(terminals.usingMedicalTerminal) match { - case Some(term: Terminal with ProximityUnit) => - terminals.StopUsingProximityUnit(term) - case _ => ; - } - } - accessedContainer match { - // Ensure we don't unload the contents of the vehicle trunk for players seated in the vehicle. - // This can happen if PSUM arrives during the mounting process - case Some(veh: Vehicle) if player.VehicleSeated.isEmpty || player.VehicleSeated.get != veh.GUID => - if ( - isMoving || veh.isMoving(1) || Vector3.DistanceSquared( - player.Position, - veh.TrunkLocation - ) > 9 - ) { - val guid = player.GUID - sendResponse(UnuseItemMessage(guid, veh.GUID)) - sendResponse(UnuseItemMessage(guid, guid)) - UnaccessContainer(veh) - } - case Some(container) => //just in case - if (isMovingPlus && (player.VehicleSeated.isEmpty || player.VehicleSeated.get != container.GUID)) { // Ensure we don't close the container if the player is seated in it - val guid = player.GUID - // If the container is a corpse and gets removed just as this runs it can cause a client disconnect, so we'll check the container has a GUID first. - if (container.HasGUID) { - sendResponse(UnuseItemMessage(guid, container.GUID)) - } - sendResponse(UnuseItemMessage(guid, guid)) - UnaccessContainer(container) - } - case None => ; - } - val wepInHand: Boolean = player.Slot(player.DrawnSlot).Equipment match { - case Some(item) => item.Definition == GlobalDefinitions.bolt_driver - case None => false - } - continent.AvatarEvents ! AvatarServiceMessage( - continent.id, - AvatarAction.PlayerState( - avatar_guid, - player.Position, - player.Velocity, - yaw, - pitch, - yaw_upper, - seq_time, - is_crouching, - is_jumping, - jump_thrust, - is_cloaking, - player.spectator, - wepInHand - ) - ) - squad.updateSquad() - if (player.death_by == -1) { - KickedByAdministration() - } - player.zoneInteractions() - } - - def handleChat(pkt: ChatMsg): Unit = { - chatActor ! ChatActor.Message(pkt) - } - - def handleChatFilter(pkt: SetChatFilterMessage): Unit = { - val SetChatFilterMessage(_, _, _) = pkt - } - - def handleVoiceHostRequest(pkt: VoiceHostRequest): Unit = { - log.debug(s"$pkt") - sendResponse(VoiceHostKill()) - sendResponse( - ChatMsg(ChatMessageType.CMT_OPEN, wideContents=false, "", "Try our Discord at https://discord.gg/0nRe5TNbTYoUruA4", None) - ) - } - - def handleVoiceHostInfo(pkt: VoiceHostInfo): Unit = { - log.debug(s"$pkt") - sendResponse(VoiceHostKill()) - sendResponse( - ChatMsg(ChatMessageType.CMT_OPEN, wideContents=false, "", "Try our Discord at https://discord.gg/0nRe5TNbTYoUruA4", None) - ) - } - - def handleEmote(pkt: EmoteMsg): Unit = { - val EmoteMsg(avatar_guid, emote) = pkt - sendResponse(EmoteMsg(avatar_guid, emote)) - } - - def handleDropItem(pkt: DropItemMessage): Unit = { - val DropItemMessage(item_guid) = pkt - ValidObject(item_guid, decorator = "DropItem") match { - case Some(anItem: Equipment) => - player.FreeHand.Equipment match { - case Some(item) => - if (item.GUID == item_guid) { - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - continent.GUID(player.VehicleSeated) match { - case Some(_) => - RemoveOldEquipmentFromInventory(player)(item) - case None => - DropEquipmentFromInventory(player)(item) - } - } - case None => - continent.GUID(player.VehicleSeated) match { - case Some(_) => ; //in a vehicle, suppress the warning message - case None => - log.warn(s"DropItem: ${player.Name} wanted to drop a $anItem, but it wasn't at hand") - } - } - case Some(obj) => - log.warn(s"DropItem: ${player.Name} wanted to drop a $obj, but that isn't possible") - case None => ; + def buildDependentOperationsForSquad(squadActor: ActorRef): Unit = { + if (squadResponseOpt.isEmpty && squadActor != Default.Actor) { + squadResponseOpt = Some(new SessionSquadHandlers(sessionData=this, avatarActor, chatActor, squadActor, context)) } } - def handlePickupItem(pkt: PickupItemMessage): Unit = { - val PickupItemMessage(item_guid, _, _, _) = pkt - ValidObject(item_guid, decorator = "PickupItem") match { - case Some(item: Equipment) => - player.Fit(item) match { - case Some(_) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - PickUpEquipmentFromGround(player)(item) - case None => //skip - sendResponse(ActionResultMessage.Fail(16)) //error code? - } - case _ => ; - } - } + def assignEventBus(msg: Any): Boolean = { + msg match { + case LookupResult("accountIntermediary", endpoint) => + accountIntermediary = endpoint + true + case LookupResult("accountPersistence", endpoint) => + accountPersistence = endpoint + true + case LookupResult("galaxy", endpoint) => + galaxyService = endpoint + buildDependentOperationsForGalaxy(endpoint) + buildDependentOperations(endpoint, cluster) + true + case LookupResult("squad", endpoint) => + squadService = endpoint + buildDependentOperationsForSquad(endpoint) + true + case ICS.InterstellarClusterServiceKey.Listing(listings) => + cluster = listings.head + buildDependentOperations(galaxyService, cluster) + true - def handleObjectHeld(pkt: ObjectHeldMessage): Unit = { - val ObjectHeldMessage(_, held_holsters, _) = pkt - player.Actor ! PlayerControl.ObjectHeld(held_holsters) - } - - def handleAvatarJump(pkt: AvatarJumpMessage): Unit = { - val AvatarJumpMessage(_) = pkt - avatarActor ! AvatarActor.ConsumeStamina(10) - avatarActor ! AvatarActor.SuspendStaminaRegeneration(2.5 seconds) - } - - def handleZipLine(pkt: ZipLineMessage): Unit = { - val ZipLineMessage(player_guid, forwards, action, path_id, pos) = pkt - val (isTeleporter: Boolean, path: Option[ZipLinePath]) = continent.zipLinePaths.find(x => x.PathId == path_id) match { - case Some(x) => - (x.IsTeleporter, Some(x)) case _ => - log.warn(s"${player.Name} couldn't find a zipline path $path_id in zone ${continent.id}") - (false, None) - } - if (isTeleporter) { - zoning.CancelZoningProcessWithDescriptiveReason("cancel") - val endPoint = path.get.ZipLinePoints.last - sendResponse( - ZipLineMessage(PlanetSideGUID(0), forwards, 0, path_id, pos) - ) // todo: send to zone to show teleport animation to all clients - sendResponse(PlayerStateShiftMessage(ShiftState(0, endPoint, player.Orientation.z, None))) - } else { - zoning.CancelZoningProcessWithDescriptiveReason("cancel_motion") - action match { - case 0 => - // Travel along the zipline in the direction specified - sendResponse(ZipLineMessage(player_guid, forwards, action, path_id, pos)) - case 1 => - //disembark from zipline at destination ! - sendResponse(ZipLineMessage(player_guid, forwards, action, 0, pos)) - case 2 => - //get off by force - sendResponse(ZipLineMessage(player_guid, forwards, action, 0, pos)) - case _ => - log.warn( - s"${player.Name} tried to do something with a zipline but can't handle it. forwards: $forwards action: $action path_id: $path_id zone: ${continent.Number} / ${continent.id}" - ) - } + false } } - def handleRequestDestroy(pkt: RequestDestroyMessage): Unit = { - val RequestDestroyMessage(object_guid) = pkt - //make sure this is the correct response for all cases - ValidObject(object_guid, decorator = "RequestDestroy") match { - case Some(vehicle: Vehicle) => - /* line 1a: player is admin (and overrules other access requirements) */ - /* line 1b: vehicle and player (as the owner) acknowledge each other */ - /* line 1c: vehicle is the same faction as player, is ownable, and either the owner is absent or the vehicle is destroyed */ - /* line 2: vehicle is not mounted in anything or, if it is, its seats are empty */ - if ( - (session.account.gm || - (player.avatar.vehicle.contains(object_guid) && vehicle.Owner.contains(player.GUID)) || - (player.Faction == vehicle.Faction && - (vehicle.Definition.CanBeOwned.nonEmpty && - (vehicle.Owner.isEmpty || continent.GUID(vehicle.Owner.get).isEmpty) || vehicle.Destroyed))) && - (vehicle.MountedIn.isEmpty || !vehicle.Seats.values.exists(_.isOccupied)) - ) { - vehicle.Actor ! Vehicle.Deconstruct() - //log.info(s"RequestDestroy: vehicle $vehicle") - } else { - log.warn(s"RequestDestroy: ${player.Name} must own vehicle in order to deconstruct it") - } - - case Some(obj: Projectile) => - if (!obj.isResolved) { - obj.Miss() - } - continent.Projectile ! ZoneProjectile.Remove(object_guid) - - case Some(obj: BoomerTrigger) => - if (FindEquipmentToDelete(object_guid, obj)) { - continent.GUID(obj.Companion) match { - case Some(boomer: BoomerDeployable) => - boomer.Trigger = None - boomer.Actor ! Deployable.Deconstruct() - case Some(thing) => - log.warn(s"RequestDestroy: BoomerTrigger object connected to wrong object - $thing") - case None => ; - } - } - - case Some(obj: Deployable) => - if (session.account.gm || obj.Owner.isEmpty || obj.Owner.contains(player.GUID) || obj.Destroyed) { - obj.Actor ! Deployable.Deconstruct() - } else { - log.warn(s"RequestDestroy: ${player.Name} must own the deployable in order to deconstruct it") - } - - case Some(obj: Equipment) => - FindEquipmentToDelete(object_guid, obj) - - case Some(thing) => - log.warn(s"RequestDestroy: not allowed to delete this ${thing.Definition.Name}") - - case None => ; - } + def whenAllEventBusesLoaded(): Boolean = { + accountIntermediary != Default.Actor && + accountPersistence != Default.Actor && + vehicleResponseOpt.nonEmpty && + galaxyResponseOpt.nonEmpty && + squadResponseOpt.nonEmpty && + zoningOpt.nonEmpty } - def handleMoveItem(pkt: MoveItemMessage): Unit = { - val MoveItemMessage(item_guid, source_guid, destination_guid, dest, _) = pkt - ( - continent.GUID(source_guid), - continent.GUID(destination_guid), - ValidObject(item_guid, decorator = "MoveItem") - ) match { - case ( - Some(source: PlanetSideServerObject with Container), - Some(destination: PlanetSideServerObject with Container), - Some(item: Equipment) - ) => - ContainableMoveItem(player.Name, source, destination, item, destination.SlotMapResolution(dest)) - case (None, _, _) => - log.error( - s"MoveItem: ${player.Name} wanted to move $item_guid from $source_guid, but could not find source object" - ) - case (_, None, _) => - log.error( - s"MoveItem: ${player.Name} wanted to move $item_guid to $destination_guid, but could not find destination object" - ) - case (_, _, None) => ; - case _ => - log.error( - s"MoveItem: ${player.Name} wanted to move $item_guid from $source_guid to $destination_guid, but multiple problems were encountered" - ) - } - } + def validObject(id: Int): Option[PlanetSideGameObject] = validObject(Some(PlanetSideGUID(id)), decorator = "") - def handleLootItem(pkt: LootItemMessage): Unit = { - val LootItemMessage(item_guid, target_guid) = pkt - (ValidObject(item_guid, decorator = "LootItem"), continent.GUID(target_guid)) match { - case (Some(item: Equipment), Some(destination: PlanetSideServerObject with Container)) => - //figure out the source - ( - { - val findFunc: PlanetSideServerObject with Container => Option[ - (PlanetSideServerObject with Container, Option[Int]) - ] = FindInLocalContainer(item_guid) - findFunc(player.avatar.locker) - .orElse(findFunc(player)) - .orElse(accessedContainer match { - case Some(parent: PlanetSideServerObject) => - findFunc(parent) - case _ => - None - }) - }, - destination.Fit(item) - ) match { - case (Some((source, Some(_))), Some(dest)) => - ContainableMoveItem(player.Name, source, destination, item, dest) - case (None, _) => - log.error(s"LootItem: ${player.Name} can not find where $item is put currently") - case (_, None) => - log.error(s"LootItem: ${player.Name} can not find anywhere to put $item in $destination") - case _ => - log.error( - s"LootItem: ${player.Name}wanted to move $item_guid to $target_guid, but multiple problems were encountered" - ) - } - case (Some(obj), _) => - log.error(s"LootItem: item $obj is (probably) not lootable to ${player.Name}") - case (None, _) => ; - case (_, None) => - log.error(s"LootItem: ${player.Name} can not find where to put $item_guid") - } - } + def validObject(id: Int, decorator: String): Option[PlanetSideGameObject] = validObject(Some(PlanetSideGUID(id)), decorator) - def handleAvatarImplant(pkt: AvatarImplantMessage): Unit = { - val AvatarImplantMessage(_, action, slot, status) = pkt - if (action == ImplantAction.Activation) { - zoning.CancelZoningProcessWithDescriptiveReason("cancel_implant") - avatar.implants(slot) match { - case Some(implant) => - if (status == 1) { - avatarActor ! AvatarActor.ActivateImplant(implant.definition.implantType) - } else { - avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType) - } - case _ => log.error(s"AvatarImplantMessage: ${player.Name} has an unknown implant in $slot") - } - } - } + def validObject(id: PlanetSideGUID): Option[PlanetSideGameObject] = validObject(Some(id), decorator = "") - def handleUseItem(pkt: UseItemMessage): Unit = { - val UseItemMessage( - avatar_guid, - item_used_guid, - object_guid, - unk2, - unk3, - unk4, - unk5, - unk6, - unk7, - unk8, - itemType - ) = pkt - // TODO: Not all fields in the response are identical to source in real packet logs (but seems to be ok) - val equipment = FindContainedEquipment(item_used_guid) match { - case (o @ Some(_), a) - if a.exists(_.isInstanceOf[Tool]) => - shooting.FindEnabledWeaponsToHandleWeaponFireAccountability(o, a.collect { case w: Tool => w })._2.headOption - case (Some(_), a) => - a.headOption - case _ => - None - } - ValidObject(object_guid, decorator = "UseItem") match { - case Some(door: Door) => - door.Actor ! CommonMessages.Use(player) + def validObject(id: PlanetSideGUID, decorator: String): Option[PlanetSideGameObject] = validObject(Some(id), decorator) - case Some(resourceSilo: ResourceSilo) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - (continent.GUID(player.VehicleSeated), equipment) match { - case (Some(vehicle: Vehicle), Some(item)) - if GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition) && - GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) => - resourceSilo.Actor ! CommonMessages.Use(player, equipment) - case _ => - resourceSilo.Actor ! CommonMessages.Use(player) - } + def validObject(id: Option[PlanetSideGUID]): Option[PlanetSideGameObject] = validObject(id, decorator = "") - case Some(panel: IFFLock) => - equipment match { - case Some(item) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - panel.Actor ! CommonMessages.Use(player, Some(item)) - case _ => ; - } - - case Some(obj: Player) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - if (obj.isBackpack) { - if (equipment.isEmpty) { - log.info(s"${player.Name} is looting the corpse of ${obj.Name}") - sendResponse( - UseItemMessage( - avatar_guid, - item_used_guid, - object_guid, - unk2, - unk3, - unk4, - unk5, - unk6, - unk7, - unk8, - itemType - ) - ) - AccessContainer(obj) - } - } else if (!unk3 && player.isAlive) { //potential kit use - (continent.GUID(item_used_guid), kitToBeUsed) match { - case (Some(kit: Kit), None) => - kitToBeUsed = Some(item_used_guid) - player.Actor ! CommonMessages.Use(player, Some(kit)) - case (Some(_: Kit), Some(_)) | (None, Some(_)) => - //a kit is already queued to be used; ignore this request - sendResponse(ChatMsg(ChatMessageType.UNK_225, wideContents=false, "", "Please wait ...", None)) - case (Some(item), _) => - log.error(s"UseItem: ${player.Name} looking for Kit to use, but found $item instead") - case (None, None) => - log.warn(s"UseItem: anticipated a Kit $item_used_guid for ${player.Name}, but can't find it") } - } else if (itemType == ObjectClass.avatar && unk3) { - equipment match { - case Some(tool: Tool) if tool.Definition == GlobalDefinitions.bank => - obj.Actor ! CommonMessages.Use(player, equipment) - - case Some(tool: Tool) if tool.Definition == GlobalDefinitions.medicalapplicator => - obj.Actor ! CommonMessages.Use(player, equipment) - case _ => ; - } - } - - case Some(locker: Locker) => - equipment match { - case Some(item) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - locker.Actor ! CommonMessages.Use(player, Some(item)) - case None if locker.Faction == player.Faction || locker.HackedBy.nonEmpty => - log.info(s"${player.Name} is accessing a locker") - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - val playerLocker = player.avatar.locker - sendResponse( - UseItemMessage( - avatar_guid, - item_used_guid, - playerLocker.GUID, - unk2, - unk3, - unk4, - unk5, - unk6, - unk7, - unk8, - 456 - ) - ) - AccessContainer(playerLocker) - case _ => ; - } - - case Some(gen: Generator) => - equipment match { - case Some(item) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - gen.Actor ! CommonMessages.Use(player, Some(item)) - case None => ; - } - - case Some(mech: ImplantTerminalMech) => - equipment match { - case Some(item) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - mech.Actor ! CommonMessages.Use(player, Some(item)) - case None => ; - } - - case Some(captureTerminal: CaptureTerminal) => - equipment match { - case Some(item) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - captureTerminal.Actor ! CommonMessages.Use(player, Some(item)) - case _ if specialItemSlotGuid.nonEmpty => - continent.GUID(specialItemSlotGuid) match { - case Some(llu: CaptureFlag) => - if (llu.Target.GUID == captureTerminal.Owner.GUID) { - continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.LluCaptured(llu)) - } else { - log.info( - s"LLU target is not this base. Target GUID: ${llu.Target.GUID} This base: ${captureTerminal.Owner.GUID}" - ) - } - case _ => log.warn("Item in specialItemSlotGuid is not registered with continent or is not a LLU") - } - case _ => ; - } - - case Some(obj: FacilityTurret) => - equipment match { - case Some(item) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - obj.Actor ! CommonMessages.Use(player, Some(item)) //try generic - obj.Actor ! CommonMessages.Use(player, Some((item, unk2.toInt))) //try upgrade path - case _ => ; - } - - case Some(obj: Vehicle) => - equipment match { - case Some(item) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - obj.Actor ! CommonMessages.Use(player, Some(item)) - - case None if player.Faction == obj.Faction => - //access to trunk - if ( - obj.AccessingTrunk.isEmpty && - (!obj.PermissionGroup(AccessPermissionGroup.Trunk.id).contains(VehicleLockState.Locked) || obj.Owner - .contains(player.GUID)) - ) { - log.info(s"${player.Name} is looking in the ${obj.Definition.Name}'s trunk") - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - obj.AccessingTrunk = player.GUID - AccessContainer(obj) - sendResponse( - UseItemMessage( - avatar_guid, - item_used_guid, - object_guid, - unk2, - unk3, - unk4, - unk5, - unk6, - unk7, - unk8, - itemType - ) - ) - } - case _ => ; - } - - case Some(terminal: Terminal) => - equipment match { - case Some(item) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - terminal.Actor ! CommonMessages.Use(player, Some(item)) - - case None - if terminal.Owner == Building.NoBuilding || terminal.Faction == player.Faction || terminal.HackedBy.nonEmpty => - val tdef = terminal.Definition - if (tdef.isInstanceOf[MatrixTerminalDefinition]) { - //TODO matrix spawn point; for now, just blindly bind to show work (and hope nothing breaks) - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - sendResponse( - BindPlayerMessage(BindStatus.Bind, "", display_icon=true, logging=true, SpawnGroup.Sanctuary, 0, 0, terminal.Position) - ) - } else if ( - tdef == GlobalDefinitions.multivehicle_rearm_terminal || tdef == GlobalDefinitions.bfr_rearm_terminal || - tdef == GlobalDefinitions.air_rearm_terminal || tdef == GlobalDefinitions.ground_rearm_terminal - ) { - FindLocalVehicle match { - case Some(vehicle) => - log.info( - s"${player.Name} is accessing a ${terminal.Definition.Name} for ${player.Sex.possessive} ${vehicle.Definition.Name}" - ) - sendResponse( - UseItemMessage( - avatar_guid, - item_used_guid, - object_guid, - unk2, - unk3, - unk4, - unk5, - unk6, - unk7, - unk8, - itemType - ) - ) - sendResponse( - UseItemMessage( - avatar_guid, - item_used_guid, - vehicle.GUID, - unk2, - unk3, - unk4, - unk5, - unk6, - unk7, - unk8, - vehicle.Definition.ObjectId - ) - ) - case None => - log.error(s"UseItem: Expecting a seated vehicle, ${player.Name} found none") - } - } else if (tdef == GlobalDefinitions.teleportpad_terminal) { - //explicit request - log.info(s"${player.Name} is purchasing a router telepad") - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - terminal.Actor ! Terminal.Request( - player, - ItemTransactionMessage(object_guid, TransactionType.Buy, 0, "router_telepad", 0, PlanetSideGUID(0)) - ) - } else if (tdef == GlobalDefinitions.targeting_laser_dispenser) { - //explicit request - log.info(s"${player.Name} is purchasing a targeting laser") - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - terminal.Actor ! Terminal.Request( - player, - ItemTransactionMessage(object_guid, TransactionType.Buy, 0, "flail_targeting_laser", 0, PlanetSideGUID(0)) - ) - } else { - log.info(s"${player.Name} is accessing a ${terminal.Definition.Name}") - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - sendResponse( - UseItemMessage( - avatar_guid, - item_used_guid, - object_guid, - unk2, - unk3, - unk4, - unk5, - unk6, - unk7, - unk8, - itemType - ) - ) - } - - case _ => ; - } - - case Some(obj: SpawnTube) => - equipment match { - case Some(item) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - obj.Actor ! CommonMessages.Use(player, Some(item)) - case None if player.Faction == obj.Faction => - //deconstruction - log.info(s"${player.Name} is deconstructing at the ${obj.Owner.Definition.Name}'s spawns") - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - PlayerActionsToCancel() - terminals.CancelAllProximityUnits() - zoning.spawn.GoToDeploymentMap() - case _ => ; - } - - case Some(obj: SensorDeployable) => - equipment match { - case Some(item) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - obj.Actor ! CommonMessages.Use(player, Some(item)) - case _ => ; - } - - case Some(obj: TurretDeployable) => - equipment match { - case Some(item) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - obj.Actor ! CommonMessages.Use(player, Some(item)) - case _ => ; - } - - case Some(obj: TrapDeployable) => - equipment match { - case Some(item) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - obj.Actor ! CommonMessages.Use(player, Some(item)) - case _ => ; - } - - case Some(obj: ShieldGeneratorDeployable) => - equipment match { - case Some(item) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - obj.Actor ! CommonMessages.Use(player, Some(item)) - case _ => ; - } - - case Some(obj: TelepadDeployable) => - if (equipment.isEmpty) { - continent.GUID(obj.Router) match { - case Some(vehicle: Vehicle) => - vehicle.Utility(UtilityType.internal_router_telepad_deployable) match { - case Some(util: Utility.InternalTelepad) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel") - UseRouterTelepadSystem( - router = vehicle, - internalTelepad = util, - remoteTelepad = obj, - src = obj, - dest = util - ) - case _ => - log.error( - s"telepad@${object_guid.guid} is not linked to a router - ${vehicle.Definition.Name}, ${obj.Router}" - ) - } - case Some(o) => - log.error( - s"telepad@${object_guid.guid} is linked to wrong kind of object - ${o.Definition.Name}, ${obj.Router}" - ) - obj.Actor ! Deployable.Deconstruct() - case None => ; - } - } - - case Some(obj: Utility.InternalTelepad) => - continent.GUID(obj.Telepad) match { - case Some(pad: TelepadDeployable) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel") - UseRouterTelepadSystem( - router = obj.Owner.asInstanceOf[Vehicle], - internalTelepad = obj, - remoteTelepad = pad, - src = obj, - dest = pad - ) - case Some(o) => - log.error( - s"internal telepad@${object_guid.guid} is not linked to a remote telepad - ${o.Definition.Name}@${o.GUID.guid}" - ) - case None => ; - } - - case Some(obj: CaptureFlag) => - // LLU can normally only be picked up the faction that owns it - if (specialItemSlotGuid.isEmpty) { - if (obj.Faction == player.Faction) { - specialItemSlotGuid = Some(obj.GUID) - player.Carrying = SpecialCarry.CaptureFlag - continent.LocalEvents ! CaptureFlagManager.PickupFlag(obj, player) - } else { - log.warn( - s"Player ${player.toString} tried to pick up LLU ${obj.GUID} - ${obj.Faction} that doesn't belong to their faction" - ) - } - } else if (specialItemSlotGuid.get != obj.GUID) { // Ignore duplicate pickup requests - log.warn( - s"Player ${player.toString} tried to pick up LLU ${obj.GUID} - ${obj.Faction} but their special slot already contains $specialItemSlotGuid" - ) - } - - case Some(_: WarpGate) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - (continent.GUID(player.VehicleSeated), equipment) match { - case (Some(vehicle: Vehicle), Some(item)) - if GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition) && - GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) => - vehicle.Actor ! CommonMessages.Use(player, equipment) - case _ => ; - } - - case Some(obj) => - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - equipment match { - case Some(item) - if GlobalDefinitions.isBattleFrameArmorSiphon(item.Definition) || - GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) => ; - - case _ => - log.warn(s"UseItem: ${player.Name} does not know how to handle $obj") - - } - - case None => ; - } - } - - def handleUnuseItem(pkt: UnuseItemMessage): Unit = { - val UnuseItemMessage(_, object_guid) = pkt - ValidObject(object_guid, decorator = "UnuseItem") match { - case Some(obj: Player) => - UnaccessContainer(obj) - zoning.spawn.TryDisposeOfLootedCorpse(obj) - - case Some(obj: Container) => - // Make sure we don't unload the contents of the vehicle the player is seated in - // An example scenario of this would be closing the trunk contents when rearming at a landing pad - if (player.VehicleSeated.isEmpty || player.VehicleSeated.get != obj.GUID) { - UnaccessContainer(obj) - } - - case _ => ; - } - } - - def handleDeployObject(pkt: DeployObjectMessage): Unit = { - val DeployObjectMessage(guid, _, pos, orient, _) = pkt - (player.Holsters().find(slot => slot.Equipment.nonEmpty && slot.Equipment.get.GUID == guid) match { - case Some(slot) => slot.Equipment - case None => None - }) match { - case Some(obj: ConstructionItem) => - val ammoType = obj.AmmoType match { - case DeployedItem.portable_manned_turret => GlobalDefinitions.PortableMannedTurret(player.Faction).Item - case dtype => dtype - } - log.info(s"${player.Name} is constructing a $ammoType deployable") - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - val dObj: Deployable = Deployables.Make(ammoType)() - dObj.Position = pos - dObj.Orientation = orient - dObj.Faction = player.Faction - dObj.AssignOwnership(player) - val tasking: TaskBundle = dObj match { - case turret: TurretDeployable => - GUIDTask.registerDeployableTurret(continent.GUID, turret) - case _ => - GUIDTask.registerObject(continent.GUID, dObj) - } - TaskWorkflow.execute(CallBackForTask(tasking, continent.Deployables, Zone.Deployable.BuildByOwner(dObj, player, obj))) - - case Some(obj) => - log.warn(s"DeployObject: what is $obj, ${player.Name}? It's not a construction tool!") - case None => - log.error(s"DeployObject: nothing, ${player.Name}? It's not a construction tool!") - } - } - - def handlePlanetsideAttribute(pkt: PlanetsideAttributeMessage): Unit = { - val PlanetsideAttributeMessage(object_guid, attribute_type, attribute_value) = pkt - ValidObject(object_guid, decorator = "PlanetsideAttribute") match { - case Some(vehicle: Vehicle) if player.avatar.vehicle.contains(vehicle.GUID) => - vehicle.Actor ! ServerObject.AttributeMsg(attribute_type, attribute_value) - case Some(vehicle: Vehicle) => - log.warn( - s"PlanetsideAttribute: ${player.Name} does not own vehicle ${vehicle.GUID} and can not change it" - ) - // Cosmetics options - case Some(_: Player) if attribute_type == 106 => - avatarActor ! AvatarActor.SetCosmetics(Cosmetic.valuesFromAttributeValue(attribute_value)) - - case Some(obj) => - log.trace(s"PlanetsideAttribute: ${player.Name} does not know how to apply unknown attributes behavior $attribute_type to ${obj.Definition.Name}") - - case _ => ; - } - } - - def handleGenericObjectAction(pkt: GenericObjectActionMessage): Unit = { - val GenericObjectActionMessage(object_guid, code) = pkt - ValidObject(object_guid, decorator = "GenericObjectAction") match { - case Some(vehicle: Vehicle) - if vehicle.OwnerName.contains(player.Name) => - vehicle.Actor ! ServerObject.GenericObjectAction(object_guid, code, Some(player.GUID)) - - case Some(tool: Tool) => - if (code == 35 && - (tool.Definition == GlobalDefinitions.maelstrom || tool.Definition.Name.startsWith("aphelion_laser")) - ) { - //maelstrom primary fire mode discharge (no target) - //aphelion_laser discharge (no target) - shooting.HandleWeaponFireAccountability(object_guid, PlanetSideGUID(Projectile.baseUID)) - } else { - ValidObject(player.VehicleSeated, decorator = "GenericObjectAction/Vehicle") match { - case Some(vehicle: Vehicle) - if vehicle.OwnerName.contains(player.Name) => - vehicle.Actor ! ServerObject.GenericObjectAction(object_guid, code, Some(tool)) - case _ => - } - } - case _ => - log.info(s"${player.Name} - $pkt") - } - } - - def handleGenericObjectActionAtPosition(pkt: GenericObjectActionAtPositionMessage): Unit = { - val GenericObjectActionAtPositionMessage(object_guid, _, _) = pkt - ValidObject(object_guid, decorator = "GenericObjectActionAtPosition") match { - case Some(tool: Tool) if GlobalDefinitions.isBattleFrameNTUSiphon(tool.Definition) => - shooting.FindContainedWeapon match { - case (Some(vehicle: Vehicle), weps) if weps.exists(_.GUID == object_guid) => - vehicle.Actor ! SpecialEmp.Burst() - case _ => ; - } - case _ => - log.info(s"${player.Name} - $pkt") - } - } - - def handleGenericObjectState(pkt: GenericObjectStateMsg): Unit = { - val GenericObjectStateMsg(_, _) = pkt - log.info(s"${player.Name} - $pkt") - } - - def handleGenericAction(pkt: GenericActionMessage): Unit = { - val GenericActionMessage(action) = pkt - if (player == null) { - if (action == 29) { - log.debug("GenericObjectState: AFK state reported during login") - } - } else { - val (toolOpt, definition) = player.Slot(0).Equipment match { - case Some(tool: Tool) => - (Some(tool), tool.Definition) - case _ => - (None, GlobalDefinitions.bullet_9mm) - } - if (action == 29) { - log.info(s"${player.Name} is AFK") - AvatarActor.savePlayerLocation(player) - displayCharSavedMsgThenRenewTimer(fixedLen=1800L, varLen=0L) //~30min - player.AwayFromKeyboard = true - } else if (action == 30) { - log.info(s"${player.Name} is back") - player.AwayFromKeyboard = false - renewCharSavedTimer( - Config.app.game.savedMsg.renewal.fixed, - Config.app.game.savedMsg.renewal.variable - ) - } - if (action == GenericActionEnum.DropSpecialItem.id) { - DropSpecialSlotItem() - } - if (action == 15) { //max deployment - log.info(s"${player.Name} has anchored ${player.Sex.pronounObject}self to the ground") - player.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored - continent.AvatarEvents ! AvatarServiceMessage( - continent.id, - AvatarAction.PlanetsideAttribute(player.GUID, 19, 1) - ) - definition match { - case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.trhev_burster => - val tool = toolOpt.get - tool.ToFireMode = 1 - sendResponse(ChangeFireModeMessage(tool.GUID, 1)) - case GlobalDefinitions.trhev_pounder => - val tool = toolOpt.get - val convertFireModeIndex = if (tool.FireModeIndex == 0) { 1 } - else { 4 } - tool.ToFireMode = convertFireModeIndex - sendResponse(ChangeFireModeMessage(tool.GUID, convertFireModeIndex)) - case _ => - log.warn(s"GenericObject: ${player.Name} is a MAX with an unexpected attachment - ${definition.Name}") - } - } else if (action == 16) { //max deployment - log.info(s"${player.Name} has released the anchors") - player.UsingSpecial = SpecialExoSuitDefinition.Mode.Normal - continent.AvatarEvents ! AvatarServiceMessage( - continent.id, - AvatarAction.PlanetsideAttribute(player.GUID, 19, 0) - ) - definition match { - case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.trhev_burster => - val tool = toolOpt.get - tool.ToFireMode = 0 - sendResponse(ChangeFireModeMessage(tool.GUID, 0)) - case GlobalDefinitions.trhev_pounder => - val tool = toolOpt.get - val convertFireModeIndex = if (tool.FireModeIndex == 1) { 0 } - else { 3 } - tool.ToFireMode = convertFireModeIndex - sendResponse(ChangeFireModeMessage(tool.GUID, convertFireModeIndex)) - case _ => - log.warn(s"GenericObject: $player is MAX with an unexpected attachment - ${definition.Name}") - } - } else if (action == 20) { - if (player.ExoSuit == ExoSuitType.MAX) { - ToggleMaxSpecialState(enable = true) - } else { - log.warn(s"GenericActionMessage: ${player.Name} can't handle action code 20") - } - } else if (action == 21) { - if (player.ExoSuit == ExoSuitType.MAX) { - player.Faction match { - case PlanetSideEmpire.NC => - ToggleMaxSpecialState(enable = false) - case _ => - log.warn(s"GenericActionMessage: ${player.Name} tried to cancel an uncancellable MAX special ability") - } - } else { - log.warn(s"GenericActionMessage: ${player.Name} can't handle action code 21") - } - } else if (action == 36) { //Looking For Squad ON - if (squad.squadUI.nonEmpty) { - if (!squad.lfsm && squad.squadUI(player.CharId).index == 0) { - squad.lfsm = true - continent.AvatarEvents ! AvatarServiceMessage( - s"${player.Faction}", - AvatarAction.PlanetsideAttribute(player.GUID, 53, 1) - ) - } - } else if (!avatar.lookingForSquad) { - avatarActor ! AvatarActor.SetLookingForSquad(true) - } - } else if (action == 37) { //Looking For Squad OFF - if (squad.squadUI.nonEmpty) { - if (squad.lfsm && squad.squadUI(player.CharId).index == 0) { - squad.lfsm = false - continent.AvatarEvents ! AvatarServiceMessage( - s"${player.Faction}", - AvatarAction.PlanetsideAttribute(player.GUID, 53, 0) - ) - } - } else if (avatar.lookingForSquad) { - avatarActor ! AvatarActor.SetLookingForSquad(false) - } - } else { - log.info(s"${player.Name} - $pkt") - } - } - } - - def handleFavoritesRequest(pkt: FavoritesRequest): Unit = { - val FavoritesRequest(_, loadoutType, action, line, label) = pkt - zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") - action match { - case FavoritesAction.Save => - avatarActor ! AvatarActor.SaveLoadout(player, loadoutType, label, line) - case FavoritesAction.Delete => - avatarActor ! AvatarActor.DeleteLoadout(player, loadoutType, line) - case FavoritesAction.Unknown => - log.warn(s"FavoritesRequest: ${player.Name} requested an unknown favorites action") - } - } - - def handleGenericCollision(pkt: GenericCollisionMsg): Unit = { - val GenericCollisionMsg(ctype, p, _, ppos, pv, t, _, tpos, tv, _, _, _) = pkt - val fallHeight = { - if (pv.z * pv.z >= (pv.x * pv.x + pv.y * pv.y) * 0.5f) { - if (heightTrend) { - val fall = heightLast - heightHistory - heightHistory = heightLast - fall - } - else { - val fall = heightHistory - heightLast - heightLast = heightHistory - fall - } - } else { - 0f - } - } - val (target1, target2, bailProtectStatus, velocity) = (ctype, ValidObject(p, decorator = "GenericCollision/Primary")) match { - case (CollisionIs.OfInfantry, out @ Some(user: Player)) - if user == player => - val bailStatus = session.flying || player.spectator || session.speed > 1f || player.BailProtection - player.BailProtection = false - val v = if (player.avatar.implants.exists { - case Some(implant) => implant.definition.implantType == ImplantType.Surge && implant.active - case _ => false - }) { - Vector3.Zero - } else { - pv - } - (out, None, bailStatus, v) - case (CollisionIs.OfGroundVehicle, out @ Some(v: Vehicle)) - if v.Seats(0).occupant.contains(player) => - val bailStatus = v.BailProtection - v.BailProtection = false - (out, ValidObject(t, decorator = "GenericCollision/GroundVehicle"), bailStatus, pv) - case (CollisionIs.OfAircraft, out @ Some(v: Vehicle)) - if v.Definition.CanFly && v.Seats(0).occupant.contains(player) => - (out, ValidObject(t, decorator = "GenericCollision/Aircraft"), false, pv) - case (CollisionIs.BetweenThings, _) => - log.warn("GenericCollision: CollisionIs.BetweenThings detected - no handling case") - (None, None, false, Vector3.Zero) - case _ => - (None, None, false, Vector3.Zero) - } - val curr = System.currentTimeMillis() - (target1, t, target2) match { - case (None, _, _) => ; - - case (Some(us: PlanetSideServerObject with Vitality with FactionAffinity), PlanetSideGUID(0), _) => - if (collisionHistory.get(us.Actor) match { - case Some(lastCollision) if curr - lastCollision <= 1000L => - false - case _ => - collisionHistory.put(us.Actor, curr) - true - }) { - if (!bailProtectStatus) { - HandleDealingDamage( - us, - DamageInteraction( - SourceEntry(us), - CollisionReason(velocity, fallHeight, us.DamageModel), - ppos - ) - ) - } - } - - case ( - Some(us: PlanetSideServerObject with Vitality with FactionAffinity), _, - Some(victim: PlanetSideServerObject with Vitality with FactionAffinity) - ) => - if (collisionHistory.get(victim.Actor) match { - case Some(lastCollision) if curr - lastCollision <= 1000L => - false - case _ => - collisionHistory.put(victim.Actor, curr) - true - }) { - val usSource = SourceEntry(us) - val victimSource = SourceEntry(victim) - //we take damage from the collision - if (!bailProtectStatus) { - HandleDealingDamage( - us, - DamageInteraction( - usSource, - CollisionWithReason(CollisionReason(velocity - tv, fallHeight, us.DamageModel), victimSource), - ppos - ) - ) - } - //get dealt damage from our own collision (no protection) - collisionHistory.put(us.Actor, curr) - HandleDealingDamage( - victim, - DamageInteraction( - victimSource, - CollisionWithReason(CollisionReason(tv - velocity, 0, victim.DamageModel), usSource), - tpos - ) - ) - } - - case _ => ; - } - } - - def handleAvatarFirstTimeEvent(pkt: AvatarFirstTimeEventMessage): Unit = { - val AvatarFirstTimeEventMessage(_, _, _, eventName) = pkt - avatarActor ! AvatarActor.AddFirstTimeEvent(eventName) - } - - def handleBugReport(pkt: PlanetSideGamePacket): Unit = { - val BugReportMessage( - _/*version_major*/, - _/*version_minor*/, - _/*version_date*/, - _/*bug_type*/, - _/*repeatable*/, - _/*location*/, - _/*zone*/, - _/*pos*/, - _/*summary*/, - _/*desc*/ - ) = pkt - log.warn(s"${player.Name} filed a bug report - it might be something important") - log.debug(s"$pkt") - } - - def handleFacilityBenefitShieldChargeRequest(pkt: FacilityBenefitShieldChargeRequestMessage): Unit = { - val FacilityBenefitShieldChargeRequestMessage(_) = pkt - player.VehicleSeated match { - case Some(vehicleGUID) => - continent.GUID(vehicleGUID) match { - case Some(obj: Vehicle) => - if (!obj.Destroyed) { //vehicle will try to charge even if destroyed - obj.Actor ! Vehicle.ChargeShields(15) - } - case _ => - log.warn( - s"FacilityBenefitShieldChargeRequest: ${player.Name} can not find vehicle ${vehicleGUID.guid} in zone ${continent.id}" - ) - } - case None => - log.warn(s"FacilityBenefitShieldChargeRequest: ${player.Name} is not seated in a vehicle") - } - } - - def handleBattleplan(pkt: BattleplanMessage): Unit = { - val BattleplanMessage(_, name, _, _) = pkt - val lament: String = s"$name has a brilliant idea that no one will ever see" - log.info(lament) - log.debug(s"Battleplan: $lament - $pkt") - } - - def handleBindPlayer(pkt: BindPlayerMessage): Unit = { - val BindPlayerMessage(_, _, _, _, _, _, _, _) = pkt - } - - def handleCreateShortcut(pkt: CreateShortcutMessage): Unit = { - val CreateShortcutMessage(_, slot, shortcutOpt) = pkt - shortcutOpt match { - case Some(shortcut) => - avatarActor ! AvatarActor.AddShortcut(slot - 1, shortcut) - case None => - avatarActor ! AvatarActor.RemoveShortcut(slot - 1) - } - } - - def handleChangeShortcutBank(pkt: ChangeShortcutBankMessage): Unit = { - val ChangeShortcutBankMessage(_, _) = pkt - } - - def handleFriendRequest(pkt: FriendsRequest): Unit = { - val FriendsRequest(action, name) = pkt - avatarActor ! AvatarActor.MemberListRequest(action, name) - } - - def handleInvalidTerrain(pkt: InvalidTerrainMessage): Unit = { - val InvalidTerrainMessage(_, vehicle_guid, alert, _) = pkt - (continent.GUID(vehicle_guid), continent.GUID(player.VehicleSeated)) match { - case (Some(packetVehicle: Vehicle), Some(playerVehicle: Vehicle)) if packetVehicle eq playerVehicle => - if (alert == TerrainCondition.Unsafe) { - log.info(s"${player.Name}'s ${packetVehicle.Definition.Name} is approaching terrain unsuitable for idling") - } - case (Some(packetVehicle: Vehicle), Some(_: Vehicle)) => - if (alert == TerrainCondition.Unsafe) { - log.info(s"${packetVehicle.Definition.Name}@${packetVehicle.GUID} is approaching terrain unsuitable for idling, but is not ${player.Name}'s vehicle") - } - case (Some(_: Vehicle), _) => - log.warn(s"InvalidTerrain: ${player.Name} is not seated in a(ny) vehicle near unsuitable terrain") - case (Some(packetThing), _) => - log.warn(s"InvalidTerrain: ${player.Name} thinks that ${packetThing.Definition.Name}@${packetThing.GUID} is near unsuitable terrain") - case _ => - log.error(s"InvalidTerrain: ${player.Name} is complaining about a thing@$vehicle_guid that can not be found") - } - } - - def handleActionCancel(pkt: ActionCancelMessage): Unit = { - val ActionCancelMessage(_, _, _) = pkt - progressBarUpdate.cancel() - progressBarValue = None - } - - def handleTrade(pkt: TradeMessage): Unit = { - val TradeMessage(trade) = pkt - log.trace(s"${player.Name} wants to trade for some reason - $trade") - } - - def handleDisplayedAward(pkt: DisplayedAwardMessage): Unit = { - val DisplayedAwardMessage(_, ribbon, bar) = pkt - log.trace(s"${player.Name} changed the $bar displayed award ribbon to $ribbon") - avatarActor ! AvatarActor.SetRibbon(ribbon, bar) - } - - def handleObjectDetected(pkt: ObjectDetectedMessage): Unit = { - val ObjectDetectedMessage(_, _, _, targets) = pkt - shooting.FindWeapon.foreach { - case weapon if weapon.Projectile.AutoLock => - //projectile with auto-lock instigates a warning on the target - val detectedTargets = shooting.FindDetectedProjectileTargets(targets) - if (detectedTargets.nonEmpty) { - val mode = 7 + (weapon.Projectile == GlobalDefinitions.wasp_rocket_projectile) - detectedTargets.foreach { target => - continent.AvatarEvents ! AvatarServiceMessage(target, AvatarAction.ProjectileAutoLockAwareness(mode)) - } - } - case _ => ; - } - } - - def handleTargetingImplantRequest(pkt: TargetingImplantRequest): Unit = { - val TargetingImplantRequest(list) = pkt - val targetInfo: List[TargetInfo] = list.flatMap { x => - continent.GUID(x.target_guid) match { - case Some(player: Player) => - val health = player.Health.toFloat / player.MaxHealth - val armor = if (player.MaxArmor > 0) { - player.Armor.toFloat / player.MaxArmor - } else { - 0 - } - - Some(TargetInfo(player.GUID, health, armor)) - case _ => - log.warn( - s"TargetingImplantRequest: the info that ${player.Name} requested for target ${x.target_guid} is not for a player" - ) - None - } - } - sendResponse(TargetingInfoMessage(targetInfo)) - } - - def handleHitHint(pkt: HitHint): Unit = { - val HitHint(_, _) = pkt - } - - /* supporting functions */ - - def ValidObject(id: Int): Option[PlanetSideGameObject] = ValidObject(Some(PlanetSideGUID(id)), decorator = "") - - def ValidObject(id: Int, decorator: String): Option[PlanetSideGameObject] = ValidObject(Some(PlanetSideGUID(id)), decorator) - - def ValidObject(id: PlanetSideGUID): Option[PlanetSideGameObject] = ValidObject(Some(id), decorator = "") - - def ValidObject(id: PlanetSideGUID, decorator: String): Option[PlanetSideGameObject] = ValidObject(Some(id), decorator) - - def ValidObject(id: Option[PlanetSideGUID]): Option[PlanetSideGameObject] = ValidObject(id, decorator = "") - - def ValidObject(id: Option[PlanetSideGUID], decorator: String): Option[PlanetSideGameObject] = { + def validObject(id: Option[PlanetSideGUID], decorator: String): Option[PlanetSideGameObject] = { val elevatedDecorator = if (decorator.nonEmpty) decorator else "ValidObject" id match { case Some(guid) => @@ -1736,7 +1401,7 @@ class SessionData( case Some(_: LocalLockerItem) => player.avatar.locker.Inventory.hasItem(guid) match { - case out@Some(_) => + case out @ Some(_) => contextSafeEntity = guid out case None if contextSafeEntity == guid => @@ -1760,11 +1425,12 @@ class SessionData( ) None - case out@Some(obj) if obj.HasGUID => + case out @ Some(obj) if obj.HasGUID => out case None if !id.contains(PlanetSideGUID(0)) => //delete stale entity reference from client + //deleting guid=0 will cause BAD things to happen log.error(s"$elevatedDecorator: ${player.Name} has an invalid reference to $hint with GUID $guid in zone ${continent.id}") sendResponse(ObjectDeleteMessage(guid, 0)) None @@ -1782,22 +1448,281 @@ class SessionData( } } - def buildDependentOperationsForGalaxy(galaxyActor: ActorRef): Unit = { - if (_vehicleResponse.isEmpty && galaxyActor != Default.Actor) { - _galaxyResponse = Some(new SessionGalaxyHandlers(sessionData=this, avatarActor, galaxyActor, context)) - _vehicleResponse = Some(new SessionVehicleHandlers(sessionData=this, avatarActor, galaxyActor, context)) + private def handleUseDoor(door: Door): Unit = { + door.Actor ! CommonMessages.Use(player) + } + + private def handleUseResourceSilo(resourceSilo: ResourceSilo, equipment: Option[Equipment]): Unit = { + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + (continent.GUID(player.VehicleSeated), equipment) match { + case (Some(vehicle: Vehicle), Some(item)) + if GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition) && + GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) => + resourceSilo.Actor ! CommonMessages.Use(player, equipment) + case _ => + resourceSilo.Actor ! CommonMessages.Use(player) } } - def buildDependentOperations(galaxyActor: ActorRef, clusterActor: typed.ActorRef[ICS.Command]): Unit = { - if (_zoning.isEmpty && galaxyActor != Default.Actor && clusterActor != Default.typed.Actor) { - _zoning = Some(new ZoningOperations(sessionData=this, avatarActor, galaxyActor, clusterActor, context)) + private def handleUsePlayer(obj: Player, equipment: Option[Equipment], msg: UseItemMessage): Unit = { + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + if (obj.isBackpack) { + if (equipment.isEmpty) { + log.info(s"${player.Name} is looting the corpse of ${obj.Name}") + sendResponse(msg) + accessContainer(obj) + } + } else if (!msg.unk3 && player.isAlive) { //potential kit use + (continent.GUID(msg.item_used_guid), kitToBeUsed) match { + case (Some(kit: Kit), None) => + kitToBeUsed = Some(msg.item_used_guid) + player.Actor ! CommonMessages.Use(player, Some(kit)) + case (Some(_: Kit), Some(_)) | (None, Some(_)) => + //a kit is already queued to be used; ignore this request + sendResponse(ChatMsg(ChatMessageType.UNK_225, wideContents=false, "", "Please wait ...", None)) + case (Some(item), _) => + log.error(s"UseItem: ${player.Name} looking for Kit to use, but found $item instead") + case (None, None) => + log.warn(s"UseItem: anticipated a Kit ${msg.item_used_guid} for ${player.Name}, but can't find it") } + } else if (msg.object_id == ObjectClass.avatar && msg.unk3) { + equipment match { + case Some(tool: Tool) if tool.Definition == GlobalDefinitions.bank => + obj.Actor ! CommonMessages.Use(player, equipment) + + case Some(tool: Tool) if tool.Definition == GlobalDefinitions.medicalapplicator => + obj.Actor ! CommonMessages.Use(player, equipment) + case _ => () + } } } - def buildDependentOperationsForSquad(squadActor: ActorRef): Unit = { - if (_squadResponse.isEmpty && squadActor != Default.Actor) { - _squadResponse = Some(new SessionSquadHandlers(sessionData=this, avatarActor, chatActor, squadActor, context)) + private def handleUseLocker(locker: Locker, equipment: Option[Equipment], msg: UseItemMessage): Unit = { + equipment match { + case Some(item) => + sendUseGeneralEntityMessage(locker, item) + case None if locker.Faction == player.Faction || locker.HackedBy.nonEmpty => + log.info(s"${player.Name} is accessing a locker") + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + val playerLocker = player.avatar.locker + sendResponse(msg.copy(object_guid = playerLocker.GUID, object_id = 456)) + accessContainer(playerLocker) + case _ => () + } + } + + private def handleUseCaptureTerminal(captureTerminal: CaptureTerminal, equipment: Option[Equipment]): Unit = { + equipment match { + case Some(item) => + sendUseGeneralEntityMessage(captureTerminal, item) + case _ if specialItemSlotGuid.nonEmpty => + continent.GUID(specialItemSlotGuid) match { + case Some(llu: CaptureFlag) => + if (llu.Target.GUID == captureTerminal.Owner.GUID) { + continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.LluCaptured(llu)) + } else { + log.info( + s"LLU target is not this base. Target GUID: ${llu.Target.GUID} This base: ${captureTerminal.Owner.GUID}" + ) + } + case _ => log.warn("Item in specialItemSlotGuid is not registered with continent or is not a LLU") + } + case _ => () + } + } + + private def handleUseFacilityTurret(obj: FacilityTurret, equipment: Option[Equipment], msg: UseItemMessage): Unit = { + equipment.foreach { item => + sendUseGeneralEntityMessage(obj, item) + obj.Actor ! CommonMessages.Use(player, Some((item, msg.unk2.toInt))) //try upgrade path + } + } + + private def handleUseVehicle(obj: Vehicle, equipment: Option[Equipment], msg: UseItemMessage): Unit = { + equipment match { + case Some(item) => + sendUseGeneralEntityMessage(obj, item) + case None if player.Faction == obj.Faction => + //access to trunk + if ( + obj.AccessingTrunk.isEmpty && + (!obj.PermissionGroup(AccessPermissionGroup.Trunk.id).contains(VehicleLockState.Locked) || obj.Owner + .contains(player.GUID)) + ) { + log.info(s"${player.Name} is looking in the ${obj.Definition.Name}'s trunk") + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + obj.AccessingTrunk = player.GUID + accessContainer(obj) + sendResponse(msg) + } + case _ => () + } + } + + private def handleUseTerminal(terminal: Terminal, equipment: Option[Equipment], msg: UseItemMessage): Unit = { + equipment match { + case Some(item) => + sendUseGeneralEntityMessage(terminal, item) + case None + if terminal.Owner == Building.NoBuilding || terminal.Faction == player.Faction || terminal.HackedBy.nonEmpty => + val tdef = terminal.Definition + if (tdef.isInstanceOf[MatrixTerminalDefinition]) { + //TODO matrix spawn point; for now, just blindly bind to show work (and hope nothing breaks) + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + sendResponse( + BindPlayerMessage(BindStatus.Bind, "", display_icon=true, logging=true, SpawnGroup.Sanctuary, 0, 0, terminal.Position) + ) + } else if ( + tdef == GlobalDefinitions.multivehicle_rearm_terminal || tdef == GlobalDefinitions.bfr_rearm_terminal || + tdef == GlobalDefinitions.air_rearm_terminal || tdef == GlobalDefinitions.ground_rearm_terminal + ) { + findLocalVehicle match { + case Some(vehicle) => + log.info( + s"${player.Name} is accessing a ${terminal.Definition.Name} for ${player.Sex.possessive} ${vehicle.Definition.Name}" + ) + sendResponse(msg) + sendResponse(msg.copy(object_guid = vehicle.GUID, object_id = vehicle.Definition.ObjectId)) + case None => + log.error(s"UseItem: Expecting a seated vehicle, ${player.Name} found none") + } + } else if (tdef == GlobalDefinitions.teleportpad_terminal) { + //explicit request + log.info(s"${player.Name} is purchasing a router telepad") + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + terminal.Actor ! Terminal.Request( + player, + ItemTransactionMessage(msg.object_guid, TransactionType.Buy, 0, "router_telepad", 0, PlanetSideGUID(0)) + ) + } else if (tdef == GlobalDefinitions.targeting_laser_dispenser) { + //explicit request + log.info(s"${player.Name} is purchasing a targeting laser") + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + terminal.Actor ! Terminal.Request( + player, + ItemTransactionMessage(msg.object_guid, TransactionType.Buy, 0, "flail_targeting_laser", 0, PlanetSideGUID(0)) + ) + } else { + log.info(s"${player.Name} is accessing a ${terminal.Definition.Name}") + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + sendResponse(msg) + } + case _ => () + } + } + + private def handleUseSpawnTube(obj: SpawnTube, equipment: Option[Equipment]): Unit = { + equipment match { + case Some(item) => + sendUseGeneralEntityMessage(obj, item) + case None if player.Faction == obj.Faction => + //deconstruction + log.info(s"${player.Name} is deconstructing at the ${obj.Owner.Definition.Name}'s spawns") + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + playerActionsToCancel() + terminals.CancelAllProximityUnits() + zoning.spawn.GoToDeploymentMap() + case _ => () + } + } + + private def handleUseTelepadDeployable(obj: TelepadDeployable, equipment: Option[Equipment], msg: UseItemMessage): Unit = { + if (equipment.isEmpty) { + (continent.GUID(obj.Router) match { + case Some(vehicle: Vehicle) => Some((vehicle, vehicle.Utility(UtilityType.internal_router_telepad_deployable))) + case Some(vehicle) => Some(vehicle, None) + case None => None + }) match { + case Some((vehicle: Vehicle, Some(util: Utility.InternalTelepad))) => + zoning.CancelZoningProcessWithDescriptiveReason("cancel") + useRouterTelepadSystem( + router = vehicle, + internalTelepad = util, + remoteTelepad = obj, + src = obj, + dest = util + ) + case Some((vehicle: Vehicle, None)) => + log.error( + s"telepad@${msg.object_guid.guid} is not linked to a router - ${vehicle.Definition.Name}" + ) + case Some((o, _)) => + log.error( + s"telepad@${msg.object_guid.guid} is linked to wrong kind of object - ${o.Definition.Name}, ${obj.Router}" + ) + obj.Actor ! Deployable.Deconstruct() + case _ => () + } + } + } + + private def handleUseInternalTelepad(obj: InternalTelepad, msg: UseItemMessage): Unit = { + continent.GUID(obj.Telepad) match { + case Some(pad: TelepadDeployable) => + zoning.CancelZoningProcessWithDescriptiveReason("cancel") + useRouterTelepadSystem( + router = obj.Owner.asInstanceOf[Vehicle], + internalTelepad = obj, + remoteTelepad = pad, + src = obj, + dest = pad + ) + case Some(o) => + log.error( + s"internal telepad@${msg.object_guid.guid} is not linked to a remote telepad - ${o.Definition.Name}@${o.GUID.guid}" + ) + case None => () + } + } + + private def handleUseCaptureFlag(obj: CaptureFlag): Unit = { + // LLU can normally only be picked up the faction that owns it + specialItemSlotGuid match { + case None if obj.Faction == player.Faction => + specialItemSlotGuid = Some(obj.GUID) + player.Carrying = SpecialCarry.CaptureFlag + continent.LocalEvents ! CaptureFlagManager.PickupFlag(obj, player) + case None => + log.warn(s"${player.Faction} player ${player.toString} tried to pick up a ${obj.Faction} LLU - ${obj.GUID}") + case Some(guid) if guid != obj.GUID => + // Ignore duplicate pickup requests + log.warn( + s"${player.Faction} player ${player.toString} tried to pick up a ${obj.Faction} LLU, but their special slot already contains $guid" + ) + case _ => () + } + } + + private def handleUseWarpGate(equipment: Option[Equipment]): Unit = { + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + (continent.GUID(player.VehicleSeated), equipment) match { + case (Some(vehicle: Vehicle), Some(item)) + if GlobalDefinitions.isBattleFrameVehicle(vehicle.Definition) && + GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) => + vehicle.Actor ! CommonMessages.Use(player, equipment) + case _ => () + } + } + + private def handleUseGeneralEntity(obj: PlanetSideServerObject, equipment: Option[Equipment]): Unit = { + equipment.foreach { item => + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + obj.Actor ! CommonMessages.Use(player, Some(item)) + } + } + + private def sendUseGeneralEntityMessage(obj: PlanetSideServerObject, equipment: Equipment): Unit = { + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + obj.Actor ! CommonMessages.Use(player, Some(equipment)) + } + + private def handleUseDefaultEntity(obj: PlanetSideGameObject, equipment: Option[Equipment]): Unit = { + zoning.CancelZoningProcessWithDescriptiveReason("cancel_use") + equipment match { + case Some(item) + if GlobalDefinitions.isBattleFrameArmorSiphon(item.Definition) || + GlobalDefinitions.isBattleFrameNTUSiphon(item.Definition) => () + case _ => + log.warn(s"UseItem: ${player.Name} does not know how to handle $obj") } } @@ -1805,7 +1730,7 @@ class SessionData( * Update this player avatar for persistence. * Set to `persist` initially. */ - def UpdatePersistenceOnly(): Unit = { + def updatePersistenceOnly(): Unit = { persistFunc() } @@ -1813,27 +1738,25 @@ class SessionData( * Do not update this player avatar for persistence. * Set to `persistFunc` initially. */ - def NoPersistence(): Unit = { } + def noPersistence(): Unit = { } - def DropSpecialSlotItem(): Unit = { - specialItemSlotGuid match { - case Some(guid: PlanetSideGUID) => - specialItemSlotGuid = None - player.Carrying = None - continent.GUID(guid) match { - case Some(llu: CaptureFlag) => - llu.Carrier match { - case Some(carrier: Player) if carrier.GUID == player.GUID => - continent.LocalEvents ! CaptureFlagManager.DropFlag(llu) - case Some(carrier: Player) => - log.warn(s"${player.toString} tried to drop LLU, but it is currently held by ${carrier.toString}") - case _ => - log.warn(s"${player.toString} tried to drop LLU, but nobody is holding it.") - } - case _ => - log.warn(s"${player.toString} Tried to drop a special item that wasn't recognized. GUID: $guid") - } - case _ => ; // Nothing to drop, do nothing. + def dropSpecialSlotItem(): Unit = { + specialItemSlotGuid.foreach { guid => + specialItemSlotGuid = None + player.Carrying = None + (continent.GUID(guid) match { + case Some(llu: CaptureFlag) => Some((llu, llu.Carrier)) + case _ => None + }) match { + case Some((llu, Some(carrier: Player))) if carrier.GUID == player.GUID => + continent.LocalEvents ! CaptureFlagManager.DropFlag(llu) + case Some((_, Some(carrier: Player))) => + log.warn(s"${player.toString} tried to drop LLU, but it is currently held by ${carrier.toString}") + case Some((_, None)) => + log.warn(s"${player.toString} tried to drop LLU, but nobody is holding it.") + case None => + log.warn(s"${player.toString} tried to drop a special item that wasn't recognized. GUID: $guid") + } } } @@ -1845,24 +1768,24 @@ class SessionData( * @param target the location in which the equipment will be stowed * @param slots the equipment, in the standard object-slot format container */ - def ApplyPurchaseTimersBeforePackingLoadout( + def applyPurchaseTimersBeforePackingLoadout( player: Player, target: PlanetSideServerObject with Container, slots: List[InventoryItem] ): Unit = { slots.foreach { item => player.avatar.purchaseCooldown(item.obj.Definition) match { - case Some(_) => ; + case Some(_) => () + case None if Avatar.purchaseCooldowns.contains(item.obj.Definition) => + avatarActor ! AvatarActor.UpdatePurchaseTime(item.obj.Definition) + TaskWorkflow.execute(PutLoadoutEquipmentInInventory(target)(item.obj, item.start)) case None => - if (Avatar.purchaseCooldowns.contains(item.obj.Definition)) { - avatarActor ! AvatarActor.UpdatePurchaseTime(item.obj.Definition) - } TaskWorkflow.execute(PutLoadoutEquipmentInInventory(target)(item.obj, item.start)) } } } - def SetupProgressChange(rate: Float, finishedAction: () => Unit, stepAction: Float => Boolean): Unit = { + def setupProgressChange(rate: Float, finishedAction: () => Unit, stepAction: Float => Boolean): Unit = { if (progressBarValue.isEmpty) { progressBarValue = Some(-rate) context.self ! SessionActor.ProgressEvent(rate, finishedAction, stepAction) @@ -1892,112 +1815,50 @@ class SessionData( * @param tickAction an optional action is is performed for each tick of progress; * also performs a continuity check to determine if the process has been disrupted */ - def HandleProgressChange( + def handleProgressChange( delta: Float, completionAction: () => Unit, tickAction: Float => Boolean, tick: Long ): Unit = { progressBarUpdate.cancel() - progressBarValue match { - case Some(value) => - val next = value + delta - if (value >= 100f) { - //complete - progressBarValue = None - tickAction(100) - completionAction() - } else if (value < 100f && next >= 100f) { - if (tickAction(99)) { - //will complete after this turn - progressBarValue = Some(next) - import scala.concurrent.ExecutionContext.Implicits.global - progressBarUpdate = context.system.scheduler.scheduleOnce( - 100 milliseconds, - context.self, - SessionActor.ProgressEvent(delta, completionAction, tickAction) - ) - } else { - progressBarValue = None - } + progressBarValue.foreach { value => + val next = value + delta + if (value >= 100f) { + //complete + progressBarValue = None + tickAction(100) + completionAction() + } else if (value < 100f && next >= 100f) { + if (tickAction(99)) { + //will complete after this turn + progressBarValue = Some(next) + import scala.concurrent.ExecutionContext.Implicits.global + progressBarUpdate = context.system.scheduler.scheduleOnce( + delay = 100 milliseconds, + context.self, + SessionActor.ProgressEvent(delta, completionAction, tickAction) + ) } else { - if (tickAction(next)) { - //normal progress activity - progressBarValue = Some(next) - import scala.concurrent.ExecutionContext.Implicits.global - progressBarUpdate = context.system.scheduler.scheduleOnce( - tick milliseconds, - context.self, - SessionActor.ProgressEvent(delta, completionAction, tickAction, tick) - ) - } else { - progressBarValue = None - } + progressBarValue = None } - case None => ; - } - } - - /* */ - - def handleSetAvatar(avatar: Avatar): Unit = { - session = session.copy(avatar = avatar) - if (session.player != null) { - session.player.avatar = avatar - } - LivePlayerList.Update(avatar.id, avatar) - } - - def handleReceiveAccountData(account: Account): Unit = { - log.trace(s"ReceiveAccountData $account") - session = session.copy(account = account) - avatarActor ! AvatarActor.SetAccount(account) - } - - def handleUpdateIgnoredPlayers(pkt: PlanetSideGamePacket): Unit = { - pkt match { - case msg: FriendsResponse => - sendResponse(msg) - msg.friends.foreach { f => - galaxyService ! GalaxyServiceMessage(GalaxyAction.LogStatusChange(f.name)) + } else { + if (tickAction(next)) { + //normal progress activity + progressBarValue = Some(next) + import scala.concurrent.ExecutionContext.Implicits.global + progressBarUpdate = context.system.scheduler.scheduleOnce( + tick.milliseconds, + context.self, + SessionActor.ProgressEvent(delta, completionAction, tickAction, tick) + ) + } else { + progressBarValue = None } - case _ => ; + } } } - def handleUseCooldownRenew(definition: BasicDefinition): Unit = { - definition match { - case _: KitDefinition => kitToBeUsed = None - case _ => ; - } - } - - def handleAvatarResponse(avatar: Avatar): Unit = { - session = session.copy(avatar = avatar) - accountPersistence ! AccountPersistenceService.Login(avatar.name, avatar.id) - } - - def handleSetSpeed(speed: Float): Unit = { - session = session.copy(speed = speed) - } - - def handleSetFlying(flying: Boolean): Unit = { - session = session.copy(flying = flying) - } - - def handleSetSpectator(spectator: Boolean): Unit = { - session.player.spectator = spectator - } - - def handleKick(player: Player, time: Option[Long]): Unit = { - AdministrativeKick(player) - accountPersistence ! AccountPersistenceService.Kick(player.Name, time) - } - - def handleSilenced(isSilenced: Boolean): Unit = { - player.silenced = isSilenced - } - /** * Construct tasking that registers all aspects of a `Player` avatar * as if that player is only just being introduced. @@ -2060,9 +1921,7 @@ class SessionData( override def description(): String = s"register a ${localVehicle.Definition.Name}" - def action(): Future[Any] = { - Future(true) - } + def action(): Future[Any] = Future(true) }, List(GUIDTask.registerVehicle(continent.GUID, vehicle)) ) @@ -2096,31 +1955,29 @@ class SessionData( override def description(): String = s"unregister a ${localVehicle.Definition.Name} driven by ${localDriver.Name}" - def action(): Future[Any] = { - Future(true) - } + def action(): Future[Any] = Future(true) }, List(GUIDTask.unregisterAvatar(continent.GUID, driver), GUIDTask.unregisterVehicle(continent.GUID, vehicle)) ) } - def AccessContainer(container: Container): Unit = { + def accessContainer(container: Container): Unit = { container match { case v: Vehicle => - AccessVehicleContents(v) + accessVehicleContents(v) case o: LockerContainer => - AccessGenericContainer(o) + accessGenericContainer(o) case p: Player if p.isBackpack => - AccessCorpseContents(p) + accessCorpseContents(p) case p: PlanetSideServerObject with Container => accessedContainer = Some(p) - case _ => ; + case _ => () } } - def AccessGenericContainer(container: PlanetSideServerObject with Container): Unit = { + def accessGenericContainer(container: PlanetSideServerObject with Container): Unit = { accessedContainer = Some(container) - DisplayContainerContents(container.GUID, container.Inventory.Items) + displayContainerContents(container.GUID, container.Inventory.Items) } /** @@ -2131,10 +1988,10 @@ class SessionData( * @see `GridInventory.Items` * @param vehicle the vehicle */ - def AccessVehicleContents(vehicle: Vehicle): Unit = { + def accessVehicleContents(vehicle: Vehicle): Unit = { accessedContainer = Some(vehicle) - AccessContainerChannel(continent.VehicleEvents, vehicle.Actor.toString) - DisplayContainerContents(vehicle.GUID, vehicle.Inventory.Items) + accessContainerChannel(continent.VehicleEvents, vehicle.Actor.toString) + displayContainerContents(vehicle.GUID, vehicle.Inventory.Items) } /** @@ -2146,11 +2003,11 @@ class SessionData( * @see `Player.HolsterItems` * @param tplayer the corpse */ - def AccessCorpseContents(tplayer: Player): Unit = { + def accessCorpseContents(tplayer: Player): Unit = { accessedContainer = Some(tplayer) - AccessContainerChannel(continent.AvatarEvents, tplayer.Actor.toString) - DisplayContainerContents(tplayer.GUID, tplayer.HolsterItems()) - DisplayContainerContents(tplayer.GUID, tplayer.Inventory.Items) + accessContainerChannel(continent.AvatarEvents, tplayer.Actor.toString) + displayContainerContents(tplayer.GUID, tplayer.HolsterItems()) + displayContainerContents(tplayer.GUID, tplayer.Inventory.Items) } /** @@ -2158,7 +2015,7 @@ class SessionData( * @param events the event system bus to which to subscribe * @param channel the channel name */ - def AccessContainerChannel(events: ActorRef, channel: String): Unit = { + def accessContainerChannel(events: ActorRef, channel: String): Unit = { events ! Service.Join(channel) } @@ -2171,7 +2028,7 @@ class SessionData( * @param containerId the container's unique identifier * @param items a list of the entities to be depicted */ - def DisplayContainerContents(containerId: PlanetSideGUID, items: Iterable[InventoryItem]): Unit = { + def displayContainerContents(containerId: PlanetSideGUID, items: Iterable[InventoryItem]): Unit = { items.foreach(entry => { val obj = entry.obj val objDef = obj.Definition @@ -2190,34 +2047,31 @@ class SessionData( * For whatever conatiner the character considers itself accessing, * initiate protocol to release it from "access". */ - def UnaccessContainer(): Unit = { - accessedContainer match { - case Some(container) => UnaccessContainer(container) - case _ => ; - } + def unaccessContainer(): Unit = { + accessedContainer.foreach { container => unaccessContainer(container) } } /** * For the target container, initiate protocol to release it from "access". */ - def UnaccessContainer(container: Container): Unit = { + def unaccessContainer(container: Container): Unit = { container match { case v: Vehicle => - UnaccessVehicleContainer(v) + unaccessVehicleContainer(v) case o: LockerContainer => - UnaccessGenericContainer(o) + unaccessGenericContainer(o) avatarActor ! AvatarActor.SaveLocker() case p: Player if p.isBackpack => - UnaccessCorpseContainer(p) + unaccessCorpseContainer(p) case _: PlanetSideServerObject with Container => accessedContainer = None - case _ => ; + case _ => () } } - def UnaccessGenericContainer(container: Container): Unit = { + def unaccessGenericContainer(container: Container): Unit = { accessedContainer = None - HideContainerContents(container.Inventory.Items) + hideContainerContents(container.Inventory.Items) } /** @@ -2226,13 +2080,13 @@ class SessionData( * Deconstruct every object in the vehicle's inventory. * @param vehicle the vehicle */ - def UnaccessVehicleContainer(vehicle: Vehicle): Unit = { + def unaccessVehicleContainer(vehicle: Vehicle): Unit = { accessedContainer = None if (vehicle.AccessingTrunk.contains(player.GUID)) { vehicle.AccessingTrunk = None } - UnaccessContainerChannel(continent.VehicleEvents, vehicle.Actor.toString) - HideContainerContents(vehicle.Inventory.Items) + unaccessContainerChannel(continent.VehicleEvents, vehicle.Actor.toString) + hideContainerContents(vehicle.Inventory.Items) } /** @@ -2241,11 +2095,11 @@ class SessionData( * Deconstruct every object in the backpack's inventory. * @param tplayer the corpse */ - def UnaccessCorpseContainer(tplayer: Player): Unit = { + def unaccessCorpseContainer(tplayer: Player): Unit = { accessedContainer = None - UnaccessContainerChannel(continent.AvatarEvents, tplayer.Actor.toString) - HideContainerContents(tplayer.HolsterItems()) - HideContainerContents(tplayer.Inventory.Items) + unaccessContainerChannel(continent.AvatarEvents, tplayer.Actor.toString) + hideContainerContents(tplayer.HolsterItems()) + hideContainerContents(tplayer.Inventory.Items) } /** @@ -2253,7 +2107,7 @@ class SessionData( * @param events the event system bus to which to subscribe * @param channel the channel name */ - def UnaccessContainerChannel(events: ActorRef, channel: String): Unit = { + def unaccessContainerChannel(events: ActorRef, channel: String): Unit = { events ! Service.Leave(Some(channel)) } @@ -2263,7 +2117,7 @@ class SessionData( * @see `ObjectDeleteMessage` * @param items a list of the entities to be depicted */ - def HideContainerContents(items: List[InventoryItem]): Unit = { + def hideContainerContents(items: List[InventoryItem]): Unit = { items.foreach { entry => sendResponse(ObjectDeleteMessage(entry.obj.GUID, 0)) } @@ -2287,12 +2141,12 @@ class SessionData( * the first value is a `Container` object; * the second value is an `Equipment` object in the former */ - def FindContainedEquipment(): (Option[PlanetSideGameObject with Container], Set[Equipment]) = { + def findContainedEquipment(): (Option[PlanetSideGameObject with Container], Set[Equipment]) = { continent.GUID(player.VehicleSeated) match { case Some(vehicle: Mountable with MountableWeapons with Container) => vehicle.PassengerInSeat(player) match { - case Some(seat_num) => - (Some(vehicle), vehicle.WeaponControlledFromSeat(seat_num)) + case Some(seatNum) => + (Some(vehicle), vehicle.WeaponControlledFromSeat(seatNum)) case None => (None, Set.empty) } @@ -2310,10 +2164,10 @@ class SessionData( * Check two locations for a controlled piece of equipment that is associated with the `player` * and has the specified global unique identifier number. */ - def FindContainedEquipment( + def findContainedEquipment( guid: PlanetSideGUID ): (Option[PlanetSideGameObject with Container], Set[Equipment]) = { - val (o, equipment) = FindContainedEquipment() + val (o, equipment) = findContainedEquipment() equipment.find { _.GUID == guid } match { case Some(equip) => (o, Set(equip)) case None => (None, Set.empty) @@ -2324,31 +2178,24 @@ class SessionData( * Runs `FindContainedEquipment` but ignores the `Container` object output. * @return an `Equipment` object */ - def FindEquipment(): Set[Equipment] = FindContainedEquipment()._2 + def findEquipment(): Set[Equipment] = findContainedEquipment()._2 /** * Runs `FindContainedEquipment` but ignores the `Container` object output * and only discovers `Equipment` with the specified global unique identifier number. * @return an `Equipment` object */ - def FindEquipment(guid: PlanetSideGUID): Option[Equipment] = FindEquipment().find { _.GUID == guid } + def findEquipment(guid: PlanetSideGUID): Option[Equipment] = findEquipment().find { _.GUID == guid } /** * Get the current `Vehicle` object that the player is riding/driving. * The vehicle must be found solely through use of `player.VehicleSeated`. * @return the vehicle */ - def FindLocalVehicle: Option[Vehicle] = { - player.VehicleSeated match { - case Some(vehicle_guid) => - continent.GUID(vehicle_guid) match { - case Some(obj: Vehicle) => - Some(obj) - case _ => - None - } - case None => - None + def findLocalVehicle: Option[Vehicle] = { + continent.GUID(player.VehicleSeated) match { + case Some(obj: Vehicle) => Some(obj) + case _ => None } } @@ -2363,28 +2210,22 @@ class SessionData( * curried for callback * @param item the item */ - def NormalItemDrop(obj: PlanetSideServerObject with Container, zone: Zone)(item: Equipment): Unit = { + def normalItemDrop(obj: PlanetSideServerObject with Container, zone: Zone)(item: Equipment): Unit = { zone.Ground.tell(Zone.Ground.DropItem(item, obj.Position, Vector3.z(obj.Orientation.z)), obj.Actor) } /** * Given an object globally unique identifier, search in a given location for it. - * @param object_guid the object + * @param objectGuid the object * @param parent a `Container` object wherein to search * @return an optional tuple that contains two values; * the first value is the container that matched correctly with the object's GUID; * the second value is the slot position of the object */ - def FindInLocalContainer( - object_guid: PlanetSideGUID + def findInLocalContainer( + objectGuid: PlanetSideGUID )(parent: PlanetSideServerObject with Container): Option[(PlanetSideServerObject with Container, Option[Int])] = { - val slot: Option[Int] = parent.Find(object_guid) - slot match { - case Some(_) => - Some(parent, slot) - case None => - None - } + parent.Find(objectGuid).flatMap { slot => Some((parent, Some(slot))) } } /** @@ -2393,11 +2234,11 @@ class SessionData( * @param state the `DriveState` that could not be promoted * @param reason a string explaining why the state can not or will not change */ - def CanNotChangeDeployment( - obj: PlanetSideServerObject with Deployment, - state: DriveState.Value, - reason: String - ): Unit = { + def youCanNotChangeDeployment( + obj: PlanetSideServerObject with Deployment, + state: DriveState.Value, + reason: String + ): Unit = { val mobileShift: String = if (obj.DeploymentState != DriveState.Mobile) { obj.DeploymentState = DriveState.Mobile sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Mobile, 0, unk3=false, Vector3.Zero)) @@ -2414,26 +2255,26 @@ class SessionData( /** * 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(0, targetGuid, PlanetSideGUID(0), 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 attributeNumber The attribute number + * @param attributeValue The attribute value */ - def SendPlanetsideAttributeMessage( - target_guid: PlanetSideGUID, - attribute_number: PlanetsideAttributeEnum, - attribute_value: Long + def sendPlanetsideAttributeMessage( + targetGuid: PlanetSideGUID, + attributeNumber: PlanetsideAttributeEnum, + attributeValue: Long ): Unit = { - sendResponse(PlanetsideAttributeMessage(target_guid, attribute_number, attribute_value)) + sendResponse(PlanetsideAttributeMessage(targetGuid, attributeNumber, attributeValue)) } /** @@ -2457,7 +2298,7 @@ class SessionData( * - if the player is anchored
* This is not a complete list but, for the purpose of enforcement, some pointers will be documented here. */ - def PlayerActionsToCancel(): Unit = { + def playerActionsToCancel(): Unit = { shooting.shootingStart.clear() shooting.shootingStop.clear() progressBarUpdate.cancel() @@ -2476,17 +2317,17 @@ class SessionData( sendResponse(UnuseItemMessage(player.GUID, vguid)) } } else { - UnaccessContainer(v) + unaccessContainer(v) } } case Some(o) => - UnaccessContainer(o) + unaccessContainer(o) if (player.isAlive) { sendResponse(UnuseItemMessage(player.GUID, o.GUID)) } - case None => ; + case None => () } (shooting.prefire ++ shooting.shooting).foreach { guid => sendResponse(ChangeFireStateMessage_Stop(guid)) @@ -2498,9 +2339,11 @@ class SessionData( shooting.prefire.clear() shooting.shooting.clear() if (session.flying) { + session = session.copy(flying = false) chatActor ! ChatActor.Message(ChatMsg(ChatMessageType.CMT_FLY, wideContents=false, "", "off", None)) } if (session.speed > 1) { + session = session.copy(speed = 1) chatActor ! ChatActor.Message(ChatMsg(ChatMessageType.CMT_SPEED, wideContents=false, "", "1.000", None)) } } @@ -2517,7 +2360,7 @@ class SessionData( * @param target a valid game object that is known to the server * @param data a projectile that will affect the target */ - def HandleDealingDamage(target: PlanetSideGameObject with Vitality, data: DamageInteraction): Unit = { + def handleDealingDamage(target: PlanetSideGameObject with Vitality, data: DamageInteraction): Unit = { val func = data.calculate() target match { case obj: Player if obj.CanDamage && obj.Actor != Default.Actor => @@ -2528,7 +2371,7 @@ class SessionData( } // auto kick players damaging spectators if (obj.spectator && obj != player) { - AdministrativeKick(player) + administrativeKick(player) } else { obj.Actor ! Vitality.Damage(func) } @@ -2556,7 +2399,7 @@ class SessionData( } obj.Actor ! Vitality.Damage(func) - case _ => ; + case _ => () } } @@ -2571,17 +2414,17 @@ class SessionData( * defaults to 121, the object id of `avatar` * @return a `DestroyDisplayMessage` packet that is properly formatted */ - def DestroyDisplayMessage( + def destroyDisplayMessage( killer: SourceEntry, victim: SourceEntry, method: Int, unk: Int = 121 ): DestroyDisplayMessage = { - val killer_seated = killer match { + val killerSeated = killer match { case obj: PlayerSource => obj.Seated case _ => false } - val victim_seated = victim match { + val victimSeated = victim match { case obj: PlayerSource => obj.Seated case _ => false } @@ -2589,13 +2432,13 @@ class SessionData( killer.Name, killer.CharId, killer.Faction, - killer_seated, + killerSeated, unk, method, victim.Name, victim.CharId, victim.Faction, - victim_seated + victimSeated ) } @@ -2615,14 +2458,14 @@ class SessionData( * first pair is current quantity; * second pair is maximum quantity */ - def UpdateDeployableUIElements(list: List[(Int, Int, Int, Int)]): Unit = { + def updateDeployableUIElements(list: List[(Int, Int, Int, Int)]): Unit = { val guid = PlanetSideGUID(0) - list.foreach({ + list.foreach { case (currElem, curr, maxElem, max) => //fields must update in ordered pairs: max, curr sendResponse(PlanetsideAttributeMessage(guid, maxElem, max)) sendResponse(PlanetsideAttributeMessage(guid, currElem, curr)) - }) + } } /** @@ -2633,9 +2476,9 @@ class SessionData( * the owner has no other means of controlling the created object that it is associated with. * @param obj the `Deployable` object to be built */ - def DeployableBuildActivity(obj: Deployable): Unit = { + def deployableBuildActivity(obj: Deployable): Unit = { sendResponse(GenericObjectActionMessage(obj.GUID, 21)) //reset build cooldown - UpdateDeployableUIElements(avatar.deployables.UpdateUIElement(obj.Definition.Item)) + updateDeployableUIElements(avatar.deployables.UpdateUIElement(obj.Definition.Item)) } /** @@ -2646,16 +2489,16 @@ class SessionData( * If the target object is discovered, it is removed from its current location and is completely destroyed. * @see `RequestDestroyMessage` * @see `Zone.ItemIs.Where` - * @param object_guid the target object's globally unique identifier; + * @param objectGuid the target object's globally unique identifier; * it is not expected that the object will be unregistered, but it is also not gauranteed * @param obj the target object * @return `true`, if the target object was discovered and removed; * `false`, otherwise */ - def FindEquipmentToDelete(object_guid: PlanetSideGUID, obj: Equipment): Boolean = { + def findEquipmentToDelete(objectGuid: PlanetSideGUID, obj: Equipment): Boolean = { val findFunc : PlanetSideServerObject with Container => Option[(PlanetSideServerObject with Container, Option[Int])] = - FindInLocalContainer(object_guid) + findInLocalContainer(objectGuid) findFunc(player) .orElse(accessedContainer match { @@ -2664,7 +2507,7 @@ class SessionData( case _ => None }) - .orElse(FindLocalVehicle match { + .orElse(findLocalVehicle match { case Some(parent: PlanetSideServerObject) => findFunc(parent) case _ => @@ -2674,29 +2517,26 @@ class SessionData( obj.Position = Vector3.Zero RemoveOldEquipmentFromInventory(parent)(obj) true - + case _ if player.avatar.locker.Inventory.Remove(objectGuid) => + sendResponse(ObjectDeleteMessage(objectGuid, 0)) + true + case _ if continent.EquipmentOnGround.contains(obj) => + obj.Position = Vector3.Zero + continent.Ground ! Zone.Ground.RemoveItem(objectGuid) + continent.AvatarEvents ! AvatarServiceMessage.Ground(RemoverActor.ClearSpecific(List(obj), continent)) + true case _ => - if (player.avatar.locker.Inventory.Remove(object_guid)) { - sendResponse(ObjectDeleteMessage(object_guid, 0)) - true - } else if (continent.EquipmentOnGround.contains(obj)) { - obj.Position = Vector3.Zero - continent.Ground ! Zone.Ground.RemoveItem(object_guid) - continent.AvatarEvents ! AvatarServiceMessage.Ground(RemoverActor.ClearSpecific(List(obj), continent)) - true - } else { - Zone.EquipmentIs.Where(obj, object_guid, continent) match { - case None => - true - case Some(Zone.EquipmentIs.Orphaned()) => - if (obj.HasGUID) { - TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) - } - true - case _ => - log.warn(s"RequestDestroy: equipment $obj exists, but ${player.Name} can not reach it to dispose of it") - false - } + Zone.EquipmentIs.Where(obj, objectGuid, continent) match { + case None => + true + case Some(Zone.EquipmentIs.Orphaned()) if obj.HasGUID => + TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) + true + case Some(Zone.EquipmentIs.Orphaned()) => + true + case _ => + log.warn(s"RequestDestroy: equipment $obj exists, but ${player.Name} can not reach it to dispose of it") + false } } } @@ -2709,7 +2549,7 @@ class SessionData( * @param orient the previous orientation of the deployable * @param deletionType the value passed to `ObjectDeleteMessage` concerning the deconstruction animation */ - def DeconstructDeployable( + def deconstructDeployable( obj: Deployable, guid: PlanetSideGUID, pos: Vector3, @@ -2726,7 +2566,7 @@ class SessionData( * and remove all `BoomerTrigger` objects, both functionally and visually. * @return all discovered `BoomTrigger` objects */ - def RemoveBoomerTriggersFromInventory(): List[BoomerTrigger] = { + def removeBoomerTriggersFromInventory(): List[BoomerTrigger] = { val events = continent.AvatarEvents val zoneId = continent.id (player.Inventory.Items ++ player.HolsterItems()) @@ -2734,7 +2574,7 @@ class SessionData( player.Slot(index).Equipment = None continent.GUID(obj.Companion) match { case Some(mine: BoomerDeployable) => mine.Actor ! Deployable.Ownership(None) - case _ => ; + case _ => () } if (player.VisibleSlots.contains(index)) { events ! AvatarServiceMessage( @@ -2757,7 +2597,7 @@ class SessionData( * @param systemPlan specific object identification of the two endpoints of the teleportation system; * if absent, the knowable endpoint is deleted from the client reflexively */ - def ToggleTeleportSystem(router: Vehicle, systemPlan: Option[(Utility.InternalTelepad, TelepadDeployable)]): Unit = { + def toggleTeleportSystem(router: Vehicle, systemPlan: Option[(Utility.InternalTelepad, TelepadDeployable)]): Unit = { systemPlan match { case Some((internalTelepad, remoteTelepad)) => internalTelepad.Telepad = remoteTelepad.GUID //necessary; backwards link to the (new) telepad @@ -2767,7 +2607,7 @@ class SessionData( router.Utility(UtilityType.internal_router_telepad_deployable) match { case Some(util: Utility.InternalTelepad) => sendResponse(ObjectDeleteMessage(util.GUID, 0)) - case _ => ; + case _ => () } } } @@ -2780,7 +2620,7 @@ class SessionData( * @param src the origin of the teleportation (where the player starts) * @param dest the destination of the teleportation (where the player is going) */ - def UseRouterTelepadSystem( + def useRouterTelepadSystem( router: Vehicle, internalTelepad: InternalTelepad, remoteTelepad: TelepadDeployable, @@ -2789,13 +2629,15 @@ class SessionData( ): Unit = { val time = System.nanoTime if ( - time - recentTeleportAttempt > (2 seconds).toNanos && router.DeploymentState == DriveState.Deployed && internalTelepad.Active && remoteTelepad.Active + time - recentTeleportAttempt > (2 seconds).toNanos && router.DeploymentState == DriveState.Deployed && + internalTelepad.Active && + remoteTelepad.Active ) { val pguid = player.GUID val sguid = src.GUID val dguid = dest.GUID sendResponse(PlayerStateShiftMessage(ShiftState(0, dest.Position, player.Orientation.z))) - UseRouterTelepadEffect(pguid, sguid, dguid) + useRouterTelepadEffect(pguid, sguid, dguid) continent.LocalEvents ! LocalServiceMessage( continent.id, LocalAction.RouterTelepadTransport(pguid, pguid, sguid, dguid) @@ -2813,7 +2655,7 @@ class SessionData( * @param srcGUID the origin of the teleportation * @param destGUID the destination of the teleportation */ - def UseRouterTelepadEffect(playerGUID: PlanetSideGUID, srcGUID: PlanetSideGUID, destGUID: PlanetSideGUID): Unit = { + def useRouterTelepadEffect(playerGUID: PlanetSideGUID, srcGUID: PlanetSideGUID, destGUID: PlanetSideGUID): Unit = { sendResponse(PlanetsideAttributeMessage(playerGUID, 64, 1)) //what does this do? sendResponse(GenericObjectActionMessage(srcGUID, 31)) sendResponse(GenericObjectActionMessage(destGUID, 32)) @@ -2824,7 +2666,7 @@ class SessionData( * @param objWithSeat the object that owns seats (and weaponry) * @param seatNum the mount */ - def UpdateWeaponAtSeatPosition(objWithSeat: MountableWeapons, seatNum: Int): Unit = { + def updateWeaponAtSeatPosition(objWithSeat: MountableWeapons, seatNum: Int): Unit = { objWithSeat.WeaponControlledFromSeat(seatNum) foreach { case weapon: Tool => //update mounted weapon belonging to mount @@ -2833,81 +2675,90 @@ class SessionData( val magazine = slot.Box sendResponse(InventoryStateMessage(magazine.GUID, weapon.GUID, magazine.Capacity.toLong)) }) - case _ => ; //no weapons to update + case _ => () //no weapons to update } } - def CapacitorTick(jump_thrust: Boolean): Unit = { + def maxCapacitorTick(jumpThrust: Boolean): Unit = { if (player.ExoSuit == ExoSuitType.MAX) { - //Discharge - if (jump_thrust || player.isOverdrived || player.isShielded) { - if (player.CapacitorState == CapacitorStateType.Discharging) { - // Previous tick was already discharging, calculate how much energy to drain from time between the two ticks - val timeDiff = (System.currentTimeMillis() - player.CapacitorLastUsedMillis).toFloat / 1000 - val drainAmount = player.ExoSuitDef.CapacitorDrainPerSecond.toFloat * timeDiff - player.Capacitor -= drainAmount - sendResponse(PlanetsideAttributeMessage(player.GUID, 7, player.Capacitor.toInt)) - } else { - // Start discharging - player.CapacitorState = CapacitorStateType.Discharging - } - } - // Charge - else if ( - player.Capacitor < player.ExoSuitDef.MaxCapacitor - && (player.CapacitorState == CapacitorStateType.Idle || player.CapacitorState == CapacitorStateType.Charging || (player.CapacitorState == CapacitorStateType.ChargeDelay && System - .currentTimeMillis() - player.CapacitorLastUsedMillis > player.ExoSuitDef.CapacitorRechargeDelayMillis)) - ) { - if (player.CapacitorState == CapacitorStateType.Charging) { - val timeDiff = (System.currentTimeMillis() - player.CapacitorLastChargedMillis).toFloat / 1000 - val chargeAmount = player.ExoSuitDef.CapacitorRechargePerSecond * timeDiff - player.Capacitor += chargeAmount - sendResponse(PlanetsideAttributeMessage(player.GUID, 7, player.Capacitor.toInt)) - } else { - player.CapacitorState = CapacitorStateType.Charging - } + val activate = (jumpThrust || player.isOverdrived || player.isShielded) && player.Capacitor > 0 + player.CapacitorState match { + case CapacitorStateType.Idle => maxCapacitorTickIdle(activate) + case CapacitorStateType.Discharging => maxCapacitorTickDischarging(activate) + case CapacitorStateType.ChargeDelay => maxCapacitorTickChargeDelay(activate) + case CapacitorStateType.Charging => maxCapacitorTickCharging(activate) } + } else if (player.CapacitorState != CapacitorStateType.Idle) { + player.CapacitorState = CapacitorStateType.Idle + } + } - if (player.Faction == PlanetSideEmpire.VS) { - // Start charge delay for VS when not boosting - if (!jump_thrust && player.CapacitorState == CapacitorStateType.Discharging) { - player.CapacitorState = CapacitorStateType.ChargeDelay - } - } else { - // Start charge delay for other factions if capacitor is empty or special ability is off - if ( - player.CapacitorState == CapacitorStateType.Discharging && (player.Capacitor == 0 || (!player.isOverdrived && !player.isShielded)) - ) { - player.CapacitorState = CapacitorStateType.ChargeDelay - ToggleMaxSpecialState(enable = false) - } + private def maxCapacitorTickIdle(activate: Boolean): Unit = { + if (activate) { + player.CapacitorState = CapacitorStateType.Discharging + //maxCapacitorTickDischarging(activate) + } else if (player.Capacitor < player.ExoSuitDef.MaxCapacitor) { + player.CapacitorState = CapacitorStateType.ChargeDelay + maxCapacitorTickChargeDelay(activate) + } + } + + private def maxCapacitorTickDischarging(activate: Boolean): Unit = { + if (activate) { + val timeDiff = (System.currentTimeMillis() - player.CapacitorLastUsedMillis).toFloat / 1000 + val drainAmount = player.ExoSuitDef.CapacitorDrainPerSecond.toFloat * timeDiff + player.Capacitor -= drainAmount + sendResponse(PlanetsideAttributeMessage(player.GUID, 7, player.Capacitor.toInt)) + } else if (player.Capacitor < player.ExoSuitDef.MaxCapacitor) { + if (player.Faction != PlanetSideEmpire.VS) { + toggleMaxSpecialState(enable = false) } + player.CapacitorState = CapacitorStateType.ChargeDelay + maxCapacitorTickChargeDelay(activate) } else { - if (player.CapacitorState != CapacitorStateType.Idle) { - player.CapacitorState = CapacitorStateType.Idle - } + player.CapacitorState = CapacitorStateType.Idle } } - def ToggleMaxSpecialState(enable: Boolean): Unit = { + private def maxCapacitorTickChargeDelay(activate: Boolean): Unit = { + if (activate) { + player.CapacitorState = CapacitorStateType.Discharging + //maxCapacitorTickDischarging(activate) + } else if (player.Capacitor == player.ExoSuitDef.MaxCapacitor) { + player.CapacitorState = CapacitorStateType.Idle + } else if (System.currentTimeMillis() - player.CapacitorLastUsedMillis > player.ExoSuitDef.CapacitorRechargeDelayMillis) { + player.CapacitorState = CapacitorStateType.Charging + //maxCapacitorTickCharging(activate) + } + } + + private def maxCapacitorTickCharging(activate: Boolean): Unit = { + if (activate) { + player.CapacitorState = CapacitorStateType.Discharging + //maxCapacitorTickDischarging(activate) + } else if (player.Capacitor < player.ExoSuitDef.MaxCapacitor) { + val timeDiff = (System.currentTimeMillis() - player.CapacitorLastChargedMillis).toFloat / 1000 + val chargeAmount = player.ExoSuitDef.CapacitorRechargePerSecond * timeDiff + player.Capacitor += chargeAmount + sendResponse(PlanetsideAttributeMessage(player.GUID, 7, player.Capacitor.toInt)) + } else { + player.CapacitorState = CapacitorStateType.Idle + } + } + + def toggleMaxSpecialState(enable: Boolean): Unit = { if (player.ExoSuit == ExoSuitType.MAX) { - if (enable) { + if (enable && player.UsingSpecial == SpecialExoSuitDefinition.Mode.Normal) { player.Faction match { - case PlanetSideEmpire.TR => - if (player.Capacitor == player.ExoSuitDef.MaxCapacitor) - player.UsingSpecial = SpecialExoSuitDefinition.Mode.Overdrive - case PlanetSideEmpire.NC => - if (player.Capacitor > 0) player.UsingSpecial = SpecialExoSuitDefinition.Mode.Shielded - case _ => + case PlanetSideEmpire.TR if player.Capacitor == player.ExoSuitDef.MaxCapacitor => + player.UsingSpecial = SpecialExoSuitDefinition.Mode.Overdrive + activateMaxSpecialStateMessage() + case PlanetSideEmpire.NC if player.Capacitor > 0 => + player.UsingSpecial = SpecialExoSuitDefinition.Mode.Shielded + activateMaxSpecialStateMessage() + case PlanetSideEmpire.VS => log.warn(s"${player.Name} tried to use a MAX special ability but their faction doesn't have one") - } - if ( - player.UsingSpecial == SpecialExoSuitDefinition.Mode.Overdrive || player.UsingSpecial == SpecialExoSuitDefinition.Mode.Shielded - ) { - continent.AvatarEvents ! AvatarServiceMessage( - continent.id, - AvatarAction.PlanetsideAttributeToAll(player.GUID, 8, 1) - ) + case _ => () } } else { player.UsingSpecial = SpecialExoSuitDefinition.Mode.Normal @@ -2919,6 +2770,13 @@ class SessionData( } } + private def activateMaxSpecialStateMessage(): Unit = { + continent.AvatarEvents ! AvatarServiceMessage( + continent.id, + AvatarAction.PlanetsideAttributeToAll(player.GUID, 8, 1) + ) + } + /** * The atypical response to receiving a `KeepAliveMessage` packet from the client.
*
@@ -2929,7 +2787,7 @@ class SessionData( * @see `turnCounterFunc` * @see `persist` */ - def KeepAlivePersistence(): Unit = { + def keepAlivePersistence(): Unit = { zoning.spawn.interimUngunnedVehicle = None persist() if (player.HasGUID) { @@ -2943,14 +2801,14 @@ class SessionData( * A really atypical response to receiving a `KeepAliveMessage` packet from the client * that applies only during the character select portion and part of the first zone load activity. */ - def KeepAlivePersistenceInitial(): Unit = { + def keepAlivePersistenceInitial(): Unit = { persist() if (player != null && player.HasGUID) { - keepAliveFunc = KeepAlivePersistence + keepAliveFunc = keepAlivePersistence } } - def AdministrativeKick(tplayer: Player): Unit = { + def administrativeKick(tplayer: Player): Unit = { log.warn(s"${tplayer.Name} has been kicked by ${player.Name}") tplayer.death_by = -1 accountPersistence ! AccountPersistenceService.Kick(tplayer.Name) @@ -2963,11 +2821,11 @@ class SessionData( continent.id, VehicleAction.KickPassenger(tplayer.GUID, seatNum, unk2=false, obj.GUID) ) - case _ => ; + case _ => () } } - def KickedByAdministration(): Unit = { + def kickedByAdministration(): Unit = { sendResponse(DisconnectMessage("@kick_w")) context.system.scheduler.scheduleOnce( delay = 300 milliseconds, @@ -2976,7 +2834,7 @@ class SessionData( ) } - def ImmediateDisconnect(): Unit = { + def immediateDisconnect(): Unit = { if (avatar != null) { accountPersistence ! AccountPersistenceService.Logout(avatar.name) } @@ -2984,14 +2842,12 @@ class SessionData( } def updateBlockMap(target: BlockMapEntity, zone: Zone, newCoords: Vector3): Unit = { - target.blockMapEntry match { - case Some(entry) => - if (BlockMap.findSectorIndices(continent.blockMap, newCoords, entry.rangeX, entry.rangeY).toSet.equals(entry.sectors)) { - target.updateBlockMapEntry(newCoords) //soft update - } else { - zone.actor ! ZoneActor.UpdateBlockMap(target, newCoords) //hard update - } - case None => ; + target.blockMapEntry.foreach { entry => + if (BlockMap.findSectorIndices(continent.blockMap, newCoords, entry.rangeX, entry.rangeY).toSet.equals(entry.sectors)) { + target.updateBlockMapEntry(newCoords) //soft update + } else { + zone.actor ! ZoneActor.UpdateBlockMap(target, newCoords) //hard update + } } } @@ -3043,12 +2899,6 @@ class SessionData( if ((heightTrend && heightLast - zHeight >= 0.5f) || (!heightTrend && zHeight - heightLast >= 0.5f)) { heightTrend = !heightTrend - // if (heightTrend) { - // GetMountableAndSeat(None, player, continent) match { - // case (Some(v: Vehicle), _) => v.BailProtection = false - // case _ => player.BailProtection = false - // } - // } heightHistory = zHeight } heightLast = zHeight @@ -3078,42 +2928,6 @@ class SessionData( middlewareActor ! MiddlewareActor.Send(packet) } - def assignEventBus(msg: Any): Boolean = { - msg match { - case LookupResult("accountIntermediary", endpoint) => - accountIntermediary = endpoint - true - case LookupResult("accountPersistence", endpoint) => - accountPersistence = endpoint - true - case LookupResult("galaxy", endpoint) => - galaxyService = endpoint - buildDependentOperationsForGalaxy(endpoint) - buildDependentOperations(endpoint, cluster) - true - case LookupResult("squad", endpoint) => - squadService = endpoint - buildDependentOperationsForSquad(endpoint) - true - case ICS.InterstellarClusterServiceKey.Listing(listings) => - cluster = listings.head - buildDependentOperations(galaxyService, cluster) - true - - case _ => - false - } - } - - def whenAllEventBusesLoaded(): Boolean = { - accountIntermediary != Default.Actor && - accountPersistence != Default.Actor && - _vehicleResponse.nonEmpty && - _galaxyResponse.nonEmpty && - _squadResponse.nonEmpty && - _zoning.nonEmpty - } - def stop(): Unit = { continent.AvatarEvents ! Service.Leave() continent.LocalEvents ! Service.Leave() @@ -3133,9 +2947,9 @@ class SessionData( localResponse.stop() mountResponse.stop() terminals.stop() - _vehicleResponse.foreach { _.stop() } - _galaxyResponse.foreach { _.stop() } - _squadResponse.foreach { _.stop() } - _zoning.foreach { _.stop() } + vehicleResponseOpt.foreach { _.stop() } + galaxyResponseOpt.foreach { _.stop() } + squadResponseOpt.foreach { _.stop() } + zoningOpt.foreach { _.stop() } } } 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 111a2692..fb9f72a0 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala @@ -30,7 +30,7 @@ class SessionLocalHandlers( } case LocalResponse.DeployableUIFor(item) => - sessionData.UpdateDeployableUIElements(avatar.deployables.UpdateUIElement(item)) + sessionData.updateDeployableUIElements(avatar.deployables.UpdateUIElement(item)) case LocalResponse.Detonate(dguid, _: BoomerDeployable) => sendResponse(TriggerEffectMessage(dguid, "detonate_boomer")) @@ -160,7 +160,7 @@ class SessionLocalHandlers( sendResponse(ChatMsg(ChatMessageType.UNK_229, wideContents=false, "", msg, None)) case LocalResponse.RouterTelepadTransport(passenger_guid, src_guid, dest_guid) => - sessionData.UseRouterTelepadEffect(passenger_guid, src_guid, dest_guid) + sessionData.useRouterTelepadEffect(passenger_guid, src_guid, dest_guid) case LocalResponse.SendResponse(msg) => sendResponse(msg) @@ -189,7 +189,7 @@ class SessionLocalHandlers( sendResponse(VehicleStateMessage(sguid, 0, pos, orient, None, Some(state), 0, 0, 15, is_decelerating=false, is_cloaked=false)) case LocalResponse.ToggleTeleportSystem(router, system_plan) => - sessionData.ToggleTeleportSystem(router, system_plan) + sessionData.toggleTeleportSystem(router, system_plan) case LocalResponse.TriggerEffect(target_guid, effect, effectInfo, triggerLocation) => sendResponse(TriggerEffectMessage(target_guid, effect, effectInfo, triggerLocation)) 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 8657e759..b34a4210 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionMountHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionMountHandlers.scala @@ -36,14 +36,14 @@ class SessionMountHandlers( log.info(s"${player.Name} mounts an implant terminal") sessionData.terminals.CancelAllProximityUnits() MountingAction(tplayer, obj, seat_number) - sessionData.keepAliveFunc = sessionData.KeepAlivePersistence + sessionData.keepAliveFunc = sessionData.keepAlivePersistence case Mountable.CanMount(obj: Vehicle, seat_number, _) 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) - sessionData.keepAliveFunc = sessionData.KeepAlivePersistence + sessionData.keepAliveFunc = sessionData.keepAlivePersistence case Mountable.CanMount(obj: Vehicle, seat_number, _) => sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount") @@ -72,10 +72,10 @@ class SessionMountHandlers( } sendResponse(GenericObjectActionMessage(obj_guid, 11)) } else if (obj.WeaponControlledFromSeat(seat_number).isEmpty) { - sessionData.keepAliveFunc = sessionData.KeepAlivePersistence + sessionData.keepAliveFunc = sessionData.keepAlivePersistence } - sessionData.AccessContainer(obj) - sessionData.UpdateWeaponAtSeatPosition(obj, seat_number) + sessionData.accessContainer(obj) + sessionData.updateWeaponAtSeatPosition(obj, seat_number) MountingAction(tplayer, obj, seat_number) case Mountable.CanMount(obj: FacilityTurret, seat_number, _) => @@ -86,7 +86,7 @@ class SessionMountHandlers( 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) + sessionData.updateWeaponAtSeatPosition(obj, seat_number) MountingAction(tplayer, obj, seat_number) } else { log.warn( @@ -98,7 +98,7 @@ class SessionMountHandlers( 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) + sessionData.updateWeaponAtSeatPosition(obj, seat_number) MountingAction(tplayer, obj, seat_number) case Mountable.CanMount(obj: Mountable, _, _) => @@ -152,7 +152,7 @@ class SessionMountHandlers( case Mountable.CanDismount(obj: Vehicle, seat_num, _) if obj.Definition == GlobalDefinitions.droppod => log.info(s"${tplayer.Name} has landed on ${continent.id}") - sessionData.UnaccessContainer(obj) + sessionData.unaccessContainer(obj) DismountAction(tplayer, obj, seat_num) obj.Actor ! Vehicle.Deconstruct() @@ -168,7 +168,7 @@ class SessionMountHandlers( } }") sessionData.vehicles.ConditionalDriverVehicleControl(obj) - sessionData.UnaccessContainer(obj) + sessionData.unaccessContainer(obj) DismountAction(tplayer, obj, seat_num) } else { continent.VehicleEvents ! VehicleServiceMessage( @@ -213,7 +213,7 @@ class SessionMountHandlers( def MountingAction(tplayer: Player, obj: PlanetSideGameObject with Mountable, seatNum: Int): Unit = { val player_guid: PlanetSideGUID = tplayer.GUID val obj_guid: PlanetSideGUID = obj.GUID - sessionData.PlayerActionsToCancel() + sessionData.playerActionsToCancel() avatarActor ! AvatarActor.DeactivateActiveImplants() avatarActor ! AvatarActor.SuspendStaminaRegeneration(3 seconds) sendResponse(ObjectAttachMessage(obj_guid, player_guid, seatNum)) diff --git a/src/main/scala/net/psforever/actors/session/support/SessionSquadHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionSquadHandlers.scala index 88d6a9d5..132924a1 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionSquadHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionSquadHandlers.scala @@ -46,7 +46,6 @@ class SessionSquadHandlers( * Upon leaving or disbanding a squad, this value is made false. * Control switching between the `Avatar`-local and the `WorldSessionActor`-local variable is contingent on `squadUI` being populated. */ - private[support] var lfsm: Boolean = false private[support] var squadSetup: () => Unit = FirstTimeSquadSetup private var squadUpdateCounter: Int = 0 private val queuedSquadActions: Seq[() => Unit] = Seq(SquadUpdates, NoSquadUpdates, NoSquadUpdates, NoSquadUpdates) @@ -263,7 +262,6 @@ class SessionSquadHandlers( GiveSquadColorsToSelf(value = 0) sendResponse(PlanetsideAttributeMessage(playerGuid, 32, 0)) //disassociate with member position in squad? sendResponse(PlanetsideAttributeMessage(playerGuid, 34, 4294967295L)) //unknown, perhaps unrelated? - lfsm = false avatarActor ! AvatarActor.SetLookingForSquad(false) //a finalization? what does this do? sendResponse(SquadDefinitionActionMessage(PlanetSideGUID(0), 0, SquadAction.Unknown(18))) @@ -291,9 +289,8 @@ class SessionSquadHandlers( case SquadResponse.PromoteMember(squad, promotedPlayer, from_index) => if (promotedPlayer != player.CharId) { //demoted from leader; no longer lfsm - if (lfsm) { - lfsm = false - AvatarActor.displayLookingForSquad(session, state = 0) + if (player.avatar.lookingForSquad) { + avatarActor ! AvatarActor.SetLookingForSquad(false) } } sendResponse(SquadMemberEvent(MemberEvent.Promote, squad.GUID.guid, promotedPlayer, position = 0)) 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 cd441ba1..df296388 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionVehicleHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionVehicleHandlers.scala @@ -107,7 +107,7 @@ class SessionVehicleHandlers( if (tplayer_guid == guid) { val typeOfRide = continent.GUID(vehicle_guid) match { case Some(obj: Vehicle) => - sessionData.UnaccessContainer(obj) + sessionData.unaccessContainer(obj) s"the ${obj.Definition.Name}'s seat by ${obj.OwnerName.getOrElse("the pilot")}" case _ => s"${player.Sex.possessive} ride" @@ -236,7 +236,7 @@ class SessionVehicleHandlers( case VehicleResponse.StartPlayerSeatedInVehicle(vehicle, _) => val vehicle_guid = vehicle.GUID - sessionData.PlayerActionsToCancel() + sessionData.playerActionsToCancel() sessionData.vehicles.serverVehicleControlVelocity = Some(0) sessionData.terminals.CancelAllProximityUnits() if (player.VisibleSlots.contains(player.DrawnSlot)) { @@ -298,7 +298,7 @@ class SessionVehicleHandlers( sendResponse(ObjectDeleteMessage(eguid, 0)) TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) } - sessionData.ApplyPurchaseTimersBeforePackingLoadout(player, vehicle, added_weapons ++ new_inventory) + sessionData.applyPurchaseTimersBeforePackingLoadout(player, vehicle, added_weapons ++ new_inventory) //jammer or unjamm new weapons based on vehicle status val vehicleJammered = vehicle.Jammed added_weapons @@ -320,7 +320,7 @@ class SessionVehicleHandlers( (old_weapons ++ old_inventory).foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, 0)) } - sessionData.UpdateWeaponAtSeatPosition(vehicle, seatNum) + sessionData.updateWeaponAtSeatPosition(vehicle, seatNum) case None => //observer: observe changes to external equipment old_weapons.foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, 0)) } diff --git a/src/main/scala/net/psforever/actors/session/support/VehicleOperations.scala b/src/main/scala/net/psforever/actors/session/support/VehicleOperations.scala index b823abf2..66847c56 100644 --- a/src/main/scala/net/psforever/actors/session/support/VehicleOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/VehicleOperations.scala @@ -99,7 +99,7 @@ class VehicleOperations( case _ => ; } if (player.death_by == -1) { - sessionData.KickedByAdministration() + sessionData.kickedByAdministration() } } @@ -197,7 +197,7 @@ class VehicleOperations( case _ => ; } if (player.death_by == -1) { - sessionData.KickedByAdministration() + sessionData.kickedByAdministration() } } @@ -231,13 +231,13 @@ class VehicleOperations( } //TODO status condition of "playing getting out of vehicle to allow for late packets without warning if (player.death_by == -1) { - sessionData.KickedByAdministration() + sessionData.kickedByAdministration() } } def handleVehicleSubState(pkt: VehicleSubStateMessage): Unit = { val VehicleSubStateMessage(vehicle_guid, _, pos, ang, vel, unk1, _) = pkt - sessionData.ValidObject(vehicle_guid, decorator = "VehicleSubState") match { + sessionData.validObject(vehicle_guid, decorator = "VehicleSubState") match { case Some(obj: Vehicle) => import net.psforever.login.WorldSession.boolToInt obj.Position = pos @@ -268,7 +268,7 @@ class VehicleOperations( def handleMountVehicle(pkt: MountVehicleMsg): Unit = { val MountVehicleMsg(_, mountable_guid, entry_point) = pkt - sessionData.ValidObject(mountable_guid, decorator = "MountVehicle") match { + sessionData.validObject(mountable_guid, decorator = "MountVehicle") match { case Some(obj: Mountable) => obj.Actor ! Mountable.TryMount(player, entry_point) case Some(_) => @@ -341,8 +341,8 @@ class VehicleOperations( case Some(obj_guid) => ( ( - sessionData.ValidObject(obj_guid, decorator = "DismountVehicle/Vehicle"), - sessionData.ValidObject(player_guid, decorator = "DismountVehicle/Player") + sessionData.validObject(obj_guid, decorator = "DismountVehicle/Vehicle"), + sessionData.validObject(player_guid, decorator = "DismountVehicle/Player") ) match { case (vehicle @ Some(obj: Vehicle), tplayer) => if (obj.MountedIn.isEmpty) (vehicle, tplayer) else (None, None) diff --git a/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala b/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala index 7c1d1876..329b2d67 100644 --- a/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala @@ -106,7 +106,7 @@ private[support] class WeaponAndProjectileOperations( def handleChangeFireStateStart(pkt: ChangeFireStateMessage_Start)(implicit context: ActorContext): Unit = { val ChangeFireStateMessage_Start(item_guid) = pkt if (shooting.isEmpty) { - sessionData.FindEquipment(item_guid) match { + sessionData.findEquipment(item_guid) match { case Some(tool: Tool) => if (tool.FireMode.RoundsPerShot == 0 || tool.Magazine > 0 || prefire.contains(item_guid)) { prefire -= item_guid @@ -156,7 +156,7 @@ private[support] class WeaponAndProjectileOperations( shootingStop += item_guid -> System.currentTimeMillis() shooting -= item_guid val pguid = player.GUID - sessionData.FindEquipment(item_guid) match { + sessionData.findEquipment(item_guid) match { case Some(tool: Tool) => //the decimator does not send a ChangeFireState_Start on the last shot; heaven knows why if ( @@ -259,7 +259,7 @@ private[support] class WeaponAndProjectileOperations( def handleChangeAmmo(pkt: ChangeAmmoMessage): Unit = { val ChangeAmmoMessage(item_guid, _) = pkt - val (thing, equipment) = sessionData.FindContainedEquipment() + val (thing, equipment) = sessionData.findContainedEquipment() if (equipment.isEmpty) { log.warn(s"ChangeAmmo: either can not find $item_guid or the object found was not Equipment") } else { @@ -286,7 +286,7 @@ private[support] class WeaponAndProjectileOperations( def handleChangeFireMode(pkt: ChangeFireModeMessage): Unit = { val ChangeFireModeMessage(item_guid, _/*fire_mode*/) = pkt - sessionData.FindEquipment(item_guid) match { + sessionData.findEquipment(item_guid) match { case Some(obj: PlanetSideGameObject with FireModeSwitch[_]) => val originalModeIndex = obj.FireModeIndex if (obj match { @@ -376,7 +376,7 @@ private[support] class WeaponAndProjectileOperations( (hit_info match { case Some(hitInfo) => val hitPos = hitInfo.hit_pos - sessionData.ValidObject(hitInfo.hitobject_guid, decorator = "Hit/hitInfo") match { + sessionData.validObject(hitInfo.hitobject_guid, decorator = "Hit/hitInfo") match { case _ if projectile.profile == GlobalDefinitions.flail_projectile => val radius = projectile.profile.DamageRadius * projectile.profile.DamageRadius val targets = Zone.findAllTargets(hitPos)(continent, player, projectile.profile) @@ -410,7 +410,7 @@ private[support] class WeaponAndProjectileOperations( ) => ResolveProjectileInteraction(proj, DamageResolution.Hit, target, hitPos) match { case Some(resprojectile) => - sessionData.HandleDealingDamage(target, resprojectile) + sessionData.handleDealingDamage(target, resprojectile) case None => ; } case _ => ; @@ -443,24 +443,24 @@ private[support] class WeaponAndProjectileOperations( (DamageResolution.Splash, DamageResolution.Splash) } //direct_victim_uid - sessionData.ValidObject(direct_victim_uid, decorator = "SplashHit/direct_victim") match { + sessionData.validObject(direct_victim_uid, decorator = "SplashHit/direct_victim") match { case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) => CheckForHitPositionDiscrepancy(projectile_guid, target.Position, target) ResolveProjectileInteraction(projectile, resolution1, target, target.Position) match { case Some(_projectile) => - sessionData.HandleDealingDamage(target, _projectile) + sessionData.handleDealingDamage(target, _projectile) case None => ; } case _ => ; } //other victims targets.foreach(elem => { - sessionData.ValidObject(elem.uid, decorator = "SplashHit/other_victims") match { + sessionData.validObject(elem.uid, decorator = "SplashHit/other_victims") match { case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) => CheckForHitPositionDiscrepancy(projectile_guid, explosion_pos, target) ResolveProjectileInteraction(projectile, resolution2, target, explosion_pos) match { case Some(_projectile) => - sessionData.HandleDealingDamage(target, _projectile) + sessionData.handleDealingDamage(target, _projectile) case None => ; } case _ => ; @@ -495,12 +495,12 @@ private[support] class WeaponAndProjectileOperations( def handleLashHit(pkt: LashMessage): Unit = { val LashMessage(_, _, victim_guid, projectile_guid, hit_pos, _) = pkt - sessionData.ValidObject(victim_guid, decorator = "Lash") match { + sessionData.validObject(victim_guid, decorator = "Lash") match { case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) => CheckForHitPositionDiscrepancy(projectile_guid, hit_pos, target) ResolveProjectileInteraction(projectile_guid, DamageResolution.Lash, target, hit_pos) match { case Some(projectile) => - sessionData.HandleDealingDamage(target, projectile) + sessionData.handleDealingDamage(target, projectile) case None => ; } case _ => ; @@ -606,7 +606,7 @@ private[support] class WeaponAndProjectileOperations( sessionData.zoning.CancelZoningProcessWithDescriptiveReason("cancel_fire") if (player.isShielded) { // Cancel NC MAX shield if it's active - sessionData.ToggleMaxSpecialState(enable = false) + sessionData.toggleMaxSpecialState(enable = false) } val (o, tools) = FindContainedWeapon val (_, enabledTools) = FindEnabledWeaponsToHandleWeaponFireAccountability(o, tools) @@ -909,7 +909,7 @@ private[support] class WeaponAndProjectileOperations( case Some(_) => stowFunc(previousBox) case None => - sessionData.NormalItemDrop(player, continent)(previousBox) + sessionData.normalItemDrop(player, continent)(previousBox) } AmmoBox.Split(previousBox) match { case Nil | List(_) => ; //done (the former case is technically not possible) @@ -935,7 +935,7 @@ private[support] class WeaponAndProjectileOperations( */ def FindDetectedProjectileTargets(targets: Iterable[PlanetSideGUID]): Iterable[String] = { targets - .map { sessionData.ValidObject(_, decorator="FindDetectedProjectileTargets") } + .map { sessionData.validObject(_, decorator="FindDetectedProjectileTargets") } .flatMap { case Some(obj: Vehicle) if !obj.Cloaked => //TODO hint: vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.ProjectileAutoLockAwareness(mode)) @@ -1179,7 +1179,7 @@ private[support] class WeaponAndProjectileOperations( * the second value is an `Tool` object in the former */ def FindContainedWeapon: (Option[PlanetSideGameObject with Container], Set[Tool]) = { - sessionData.FindContainedEquipment() match { + sessionData.findContainedEquipment() match { case (container, equipment) => (container, equipment collect { case t: Tool => t }) case _ => diff --git a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala index 46384de8..601801b5 100644 --- a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala @@ -436,7 +436,7 @@ class ZoningOperations( //the router won't work if it doesn't completely deploy sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deploying, 0, unk3=false, Vector3.Zero)) sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Deployed, 0, unk3=false, Vector3.Zero)) - sessionData.ToggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent)) + sessionData.toggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent)) } ServiceManager.serviceManager .ask(Lookup("hart"))(Timeout(2 seconds)) @@ -709,9 +709,9 @@ class ZoningOperations( } val previousZoningType = ztype CancelZoningProcess() - sessionData.PlayerActionsToCancel() + sessionData.playerActionsToCancel() sessionData.terminals.CancelAllProximityUnits() - sessionData.DropSpecialSlotItem() + sessionData.dropSpecialSlotItem() continent.Population ! Zone.Population.Release(avatar) spawn.resolveZoningSpawnPointLoad(response, previousZoningType) } @@ -812,13 +812,13 @@ class ZoningOperations( zoningStatus = Zoning.Status.Request beginZoningCountdown(() => { log.info(s"Good-bye, ${player.Name}") - sessionData.ImmediateDisconnect() + sessionData.immediateDisconnect() }) } def handleSetZone(zoneId: String, position: Vector3): Unit = { if (sessionData.vehicles.serverVehicleControlVelocity.isEmpty) { - sessionData.PlayerActionsToCancel() + sessionData.playerActionsToCancel() continent.GUID(player.VehicleSeated) match { case Some(vehicle: Vehicle) if vehicle.MountedIn.isEmpty => vehicle.PassengerInSeat(player) match { @@ -1092,13 +1092,13 @@ class ZoningOperations( //sync hack state amenity.Definition match { case GlobalDefinitions.capture_terminal => - sessionData.SendPlanetsideAttributeMessage( + sessionData.sendPlanetsideAttributeMessage( amenity.GUID, PlanetsideAttributeEnum.ControlConsoleHackUpdate, HackCaptureActor.GetHackUpdateAttributeValue(amenity.asInstanceOf[CaptureTerminal], isResecured = false) ) case _ => - sessionData.HackObject(amenity.GUID, 1114636288L, 8L) //generic hackable object + sessionData.hackObject(amenity.GUID, 1114636288L, 8L) //generic hackable object } // sync capture flags @@ -1284,7 +1284,7 @@ class ZoningOperations( ICS.FindZone(_.id == zoneId, context.self) )) } else { - sessionData.UnaccessContainer(vehicle) + sessionData.unaccessContainer(vehicle) LoadZoneCommonTransferActivity() player.VehicleSeated = vehicle.GUID player.Continent = zoneId //forward-set the continent id to perform a test @@ -1430,7 +1430,7 @@ class ZoningOperations( } avatarActor ! AvatarActor.SetVehicle(None) } - sessionData.RemoveBoomerTriggersFromInventory().foreach(obj => { + sessionData.removeBoomerTriggersFromInventory().foreach(obj => { TaskWorkflow.execute(GUIDTask.unregisterObject(continent.GUID, obj)) }) Deployables.Disown(continent, avatar, context.self) @@ -1452,7 +1452,7 @@ class ZoningOperations( if (currentZone == Zones.sanctuaryZoneNumber(tplayer.Faction)) { log.error(s"RequestSanctuaryZoneSpawn: ${player.Name} is already in faction sanctuary zone.") sendResponse(DisconnectMessage("RequestSanctuaryZoneSpawn: player is already in sanctuary.")) - sessionData.ImmediateDisconnect() + sessionData.immediateDisconnect() } else { continent.GUID(player.VehicleSeated) match { case Some(obj: Vehicle) if !obj.Destroyed => @@ -1485,7 +1485,7 @@ class ZoningOperations( def LoadZoneLaunchDroppod(zone: Zone, spawnPosition: Vector3): Unit = { log.info(s"${player.Name} is launching to ${zone.id} in ${player.Sex.possessive} droppod") CancelZoningProcess() - sessionData.PlayerActionsToCancel() + sessionData.playerActionsToCancel() sessionData.terminals.CancelAllProximityUnits() //droppod action val droppod = Vehicle(GlobalDefinitions.droppod) @@ -1780,7 +1780,7 @@ class ZoningOperations( ) match { case (_, Some(p)) if p.death_by == -1 => //player is not allowed - sessionData.KickedByAdministration() + sessionData.kickedByAdministration() case (Some(a), Some(p)) if p.isAlive => //rejoin current avatar/player @@ -1831,7 +1831,7 @@ class ZoningOperations( def handleLoginCanNot(name: String, reason: PlayerToken.DeniedLoginReason.Value): Unit = { log.warn(s"LoginInfo: $name is denied login for reason - $reason") reason match { - case PlayerToken.DeniedLoginReason.Kicked => sessionData.KickedByAdministration() + case PlayerToken.DeniedLoginReason.Kicked => sessionData.kickedByAdministration() case _ => sendResponse(DisconnectMessage("You will be logged out.")) } } @@ -1870,9 +1870,9 @@ class ZoningOperations( } val previousZoningType = ztype CancelZoningProcess() - sessionData.PlayerActionsToCancel() + sessionData.playerActionsToCancel() sessionData.terminals.CancelAllProximityUnits() - sessionData.DropSpecialSlotItem() + sessionData.dropSpecialSlotItem() continent.Population ! Zone.Population.Release(avatar) resolveZoningSpawnPointLoad(response, previousZoningType) } @@ -1995,7 +1995,7 @@ class ZoningOperations( * @param zone na */ def HandleReleaseAvatar(tplayer: Player, zone: Zone): Unit = { - sessionData.keepAliveFunc = sessionData.KeepAlivePersistence + sessionData.keepAliveFunc = sessionData.keepAlivePersistence tplayer.Release tplayer.VehicleSeated match { case None => @@ -2010,7 +2010,7 @@ class ZoningOperations( def handleSetPosition(position: Vector3): Unit = { if (sessionData.vehicles.serverVehicleControlVelocity.isEmpty) { - sessionData.PlayerActionsToCancel() + sessionData.playerActionsToCancel() continent.GUID(player.VehicleSeated) match { case Some(vehicle: Vehicle) if vehicle.MountedIn.isEmpty => vehicle.PassengerInSeat(player) match { @@ -2178,8 +2178,8 @@ class ZoningOperations( sendResponse(ObjectCreateDetailedMessage(pdef.ObjectId, pguid, pdata)) if (seat == 0 || vehicle.WeaponControlledFromSeat(seat).nonEmpty) { sendResponse(ObjectAttachMessage(vguid, pguid, seat)) - sessionData.AccessContainer(vehicle) - sessionData.UpdateWeaponAtSeatPosition(vehicle, seat) + sessionData.accessContainer(vehicle) + sessionData.updateWeaponAtSeatPosition(vehicle, seat) } else { interimUngunnedVehicle = Some(vguid) interimUngunnedVehicleSeat = Some(seat) @@ -2253,8 +2253,8 @@ class ZoningOperations( log.debug(s"AvatarRejoin: ${player.Name} - $pguid -> $pdata") if (seat == 0 || vehicle.WeaponControlledFromSeat(seat).nonEmpty) { sendResponse(ObjectAttachMessage(vguid, pguid, seat)) - sessionData.AccessContainer(vehicle) - sessionData.UpdateWeaponAtSeatPosition(vehicle, seat) + sessionData.accessContainer(vehicle) + sessionData.updateWeaponAtSeatPosition(vehicle, seat) } else { interimUngunnedVehicle = Some(vguid) interimUngunnedVehicleSeat = Some(seat) @@ -2322,7 +2322,7 @@ class ZoningOperations( case Some(_) | None => ; } }) - sessionData.RemoveBoomerTriggersFromInventory().foreach(trigger => { sessionData.NormalItemDrop(obj, continent)(trigger) }) + sessionData.removeBoomerTriggersFromInventory().foreach(trigger => { sessionData.normalItemDrop(obj, continent)(trigger) }) } } @@ -2647,7 +2647,7 @@ class ZoningOperations( sessionData.keepAliveFunc = sessionData.vehicles.GetMountableAndSeat(None, player, continent) match { case (Some(v: Vehicle), Some(seatNumber)) if seatNumber > 0 && v.WeaponControlledFromSeat(seatNumber).isEmpty => - sessionData.KeepAlivePersistence + sessionData.keepAlivePersistence case _ => NormalKeepAlive } @@ -2674,7 +2674,7 @@ class ZoningOperations( log.trace(s"HandleSetCurrentAvatar - ${tplayer.Name}") session = session.copy(player = tplayer) val guid = tplayer.GUID - sessionData.UpdateDeployableUIElements(Deployables.InitializeDeployableUIElements(avatar)) + sessionData.updateDeployableUIElements(Deployables.InitializeDeployableUIElements(avatar)) sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 75, 0)) sendResponse(SetCurrentAvatarMessage(guid, 0, 0)) sendResponse(ChatMsg(ChatMessageType.CMT_EXPANSIONS, wideContents=true, "", "1 on", None)) //CC on //TODO once per respawn? @@ -2717,7 +2717,7 @@ class ZoningOperations( ) //TODO will not always be "on" like this sendResponse(AvatarDeadStateMessage(DeadState.Alive, 0, 0, tplayer.Position, player.Faction, unk5 = true)) //looking for squad (members) - if (tplayer.avatar.lookingForSquad || sessionData.squad.lfsm) { + if (tplayer.avatar.lookingForSquad) { sendResponse(PlanetsideAttributeMessage(guid, 53, 1)) continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.PlanetsideAttribute(guid, 53, 1)) } @@ -3010,8 +3010,8 @@ class ZoningOperations( case (Some(vehicle: Vehicle), Some(vguid), Some(seat)) => //sit down sendResponse(ObjectAttachMessage(vguid, pguid, seat)) - sessionData.AccessContainer(vehicle) - sessionData.keepAliveFunc = sessionData.KeepAlivePersistence + sessionData.accessContainer(vehicle) + sessionData.keepAliveFunc = sessionData.keepAlivePersistence case _ => ; //we can't find a vehicle? and we're still here? that's bad player.VehicleSeated = None diff --git a/src/main/scala/net/psforever/objects/avatar/Avatar.scala b/src/main/scala/net/psforever/objects/avatar/Avatar.scala index b9d35746..7283da25 100644 --- a/src/main/scala/net/psforever/objects/avatar/Avatar.scala +++ b/src/main/scala/net/psforever/objects/avatar/Avatar.scala @@ -1,6 +1,7 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.avatar +import net.psforever.actors.session.AvatarActor import net.psforever.objects.definition.{AvatarDefinition, BasicDefinition} import net.psforever.objects.equipment.{EquipmentSize, EquipmentSlot} import net.psforever.objects.inventory.LocallyRegisteredInventory @@ -144,7 +145,8 @@ case class Avatar( cooldowns: Map[BasicDefinition, FiniteDuration], definition: BasicDefinition ): Option[Duration] = { - times.get(definition.Name) match { + val (_, resolvedName) = AvatarActor.resolvePurchaseTimeName(faction, definition) + times.get(resolvedName) match { case Some(purchaseTime) => val secondsSincePurchase = Seconds.secondsBetween(purchaseTime, LocalDateTime.now()) cooldowns.get(definition) match { diff --git a/src/main/scala/net/psforever/packet/game/AvatarVehicleTimerMessage.scala b/src/main/scala/net/psforever/packet/game/AvatarVehicleTimerMessage.scala index e1b50699..254f3a77 100644 --- a/src/main/scala/net/psforever/packet/game/AvatarVehicleTimerMessage.scala +++ b/src/main/scala/net/psforever/packet/game/AvatarVehicleTimerMessage.scala @@ -7,12 +7,12 @@ import scodec.Codec import scodec.codecs._ /** - * @param player_guid player guid ! - * @param text name of the item or vehicle name (ex : medkit, fury ...) - * @param time in seconds - * @param unk1 NA - Seems to be false when it's for medkit, true for vehicles + * @param player_guid player guid + * @param text internal name of the item or vehicle name, e.g., medkit, fury, trhev_antipersonnel + * @param time cooldown/delay in seconds + * @param unk `true` for vehicles and max exo-suits; `false` for other items */ -final case class AvatarVehicleTimerMessage(player_guid: PlanetSideGUID, text: String, time: Long, unk1: Boolean) +final case class AvatarVehicleTimerMessage(player_guid: PlanetSideGUID, text: String, time: Long, unk: Boolean) extends PlanetSideGamePacket { type Packet = AvatarVehicleTimerMessage def opcode = GamePacketOpcode.AvatarVehicleTimerMessage @@ -24,6 +24,6 @@ object AvatarVehicleTimerMessage extends Marshallable[AvatarVehicleTimerMessage] ("player_guid" | PlanetSideGUID.codec) :: ("text" | PacketHelpers.encodedString) :: ("time" | uint32L) :: - ("unk1" | bool) + ("unk" | bool) ).as[AvatarVehicleTimerMessage] } diff --git a/src/main/scala/net/psforever/packet/game/GenericActionMessage.scala b/src/main/scala/net/psforever/packet/game/GenericActionMessage.scala index 4f95473c..d1230be7 100644 --- a/src/main/scala/net/psforever/packet/game/GenericActionMessage.scala +++ b/src/main/scala/net/psforever/packet/game/GenericActionMessage.scala @@ -2,76 +2,69 @@ package net.psforever.packet.game import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket} +import enumeratum.values.{IntEnum, IntEnumEntry} import scodec.Codec import scodec.codecs._ +sealed abstract class GenericAction(val value: Int) extends IntEnumEntry + +object GenericAction extends IntEnum[GenericAction] { + val values: IndexedSeq[GenericAction] = findValues + + final case object ShowMosquitoRadar extends GenericAction(value = 3) + final case object HideMosquitoRadar extends GenericAction(value = 4) + final case object MissileLock extends GenericAction(value = 7) + final case object WaspMissileLock extends GenericAction(value = 8) + final case object TRekLock extends GenericAction(value = 9) + final case object DropSpecialItem extends GenericAction(value = 11) + final case object FacilityCaptureFanfare extends GenericAction(value = 12) + final case object NewCharacterBasicTrainingPrompt extends GenericAction(value = 14) + final case object MaxAnchorsExtend_RCV extends GenericAction(value = 15) + final case object MaxAnchorsRelease_RCV extends GenericAction(value = 16) + final case object MaxSpecialEffect_RCV extends GenericAction(value = 20) + final case object StopMaxSpecialEffect_RCV extends GenericAction(value = 21) + final case object CavernFacilityCapture extends GenericAction(value = 22) + final case object CavernFacilityKill extends GenericAction(value = 23) + final case object Imprinted extends GenericAction(value = 24) + final case object NoLongerImprinted extends GenericAction(value = 25) + final case object PurchaseTimersReset extends GenericAction(value = 27) + final case object LeaveWarpQueue_RCV extends GenericAction(value = 28) + final case object AwayFromKeyboard_RCV extends GenericAction(value = 29) + final case object BackInGame_RCV extends GenericAction(value = 30) + final case object FirstPersonViewWithEffect extends GenericAction(value = 31) + final case object FirstPersonViewFailToDeconstruct extends GenericAction(value = 32) + final case object FailToDeconstruct extends GenericAction(value = 33) + final case object LookingForSquad_RCV extends GenericAction(value = 36) + final case object NotLookingForSquad_RCV extends GenericAction(value = 37) + final case object Unknown45 extends GenericAction(value = 45) + + final case class Unknown(override val value: Int) extends GenericAction(value) +} + /** - * Reports that something has happened, or makes something happen.
- *
- * When sent from the server to a client, there are twenty-seven individual actions caused by this packet. - * They are only vaguely organized by behavior and some numbers may not be associated with an action. - * When sent by the client to the server, an unknown number of actions are available. - * The highest known action is a server-sent 45.
- *
- * Actions (when sent from server):
- * 03 - symbol: show Mosquito radar
- * 04 - symbol: hide Mosquito radar
- * 07 - warning: missile lock
- * 08 - warning: Wasp missile lock
- * 09 - warning: T-REK lock
- * 11 - Drop special item e.g. LLU
- * 12 - sound: base captured fanfare
- * 14 - prompt: new character basic training
- * 15 - MAX Deploy
- * 16 - MAX Undeploy
- * 22 - message: awarded a cavern capture (updates cavern capture status)
- * 23 - award a cavern kill
- * 24 - message: you have been imprinted (updates imprinted status)
- * 25 - message: you are no longer imprinted (updates imprinted status)
- * 27 - event: purchase timers reset (does it?)
- * 31 - forced into first person view; - * in third person view, player character sinks into the ground; green deconstruction particle effect under feet
- * 32 - forced into first person view, attempt to deconstruct but fail; - * event: fail to deconstruct due to having a "parent vehicle"
- * 33 - event: fail to deconstruct
- * 43 - prompt: friendly fire in virtual reality zone
- * 45 - ?
- *
- * Actions (when sent from client):
- * 15 - Max anchor - * 16 - Max unanchor - * 20 - Client requests MAX special effect (NC shield and TR overdrive. VS jump jets are handled by the jump_thrust boolean on PlayerStateMessageUpstream) - * 21 - Disable MAX special effect (NC shield) - * 28 - Cancel warp queue (see: `DroppodLaunchResponseMessage`)
- * 29 - AFK
- * 30 - back in game
- * 36 - turn on "Looking for Squad"
- * 37 - turn off "Looking for Squad" - * + * Reports that something has happened, or makes something happen. * @param action what this packet does */ -final case class GenericActionMessage(action: Int) extends PlanetSideGamePacket { +final case class GenericActionMessage(action: GenericAction) extends PlanetSideGamePacket { type Packet = GenericActionMessage def opcode = GamePacketOpcode.GenericActionMessage def encode = GenericActionMessage.encode(this) } object GenericActionMessage extends Marshallable[GenericActionMessage] { - def apply(action: GenericActionEnum.GenericActionEnum): GenericActionMessage = { - GenericActionMessage(action.id) + def apply(i: Int): GenericActionMessage = { + GenericActionMessage(GenericAction.values.find { _.value == i } match { + case Some(enum) => enum + case None => GenericAction.Unknown(i) + }) } - implicit val codec: Codec[GenericActionMessage] = ( - "action" | uint(6) - ).as[GenericActionMessage] -} - -object GenericActionEnum extends Enumeration { - type GenericActionEnum = Value - - /** Drop special item e.g. LLU */ - val DropSpecialItem = Value(11) - - /** Plays the base capture fanfare sound */ - val BaseCaptureFanfare = Value(12) + private val genericActionCodec = uint(bits = 6).xmap[GenericAction]({ + i => GenericAction.values.find { _.value == i } match { + case Some(enum) => enum + case None => GenericAction.Unknown(i) + } + }, enum => enum.value) + + implicit val codec: Codec[GenericActionMessage] = ("action" | genericActionCodec).as[GenericActionMessage] } diff --git a/src/main/scala/net/psforever/services/local/LocalServiceMessage.scala b/src/main/scala/net/psforever/services/local/LocalServiceMessage.scala index db5a7ab0..4f3d210b 100644 --- a/src/main/scala/net/psforever/services/local/LocalServiceMessage.scala +++ b/src/main/scala/net/psforever/services/local/LocalServiceMessage.scala @@ -11,10 +11,9 @@ import net.psforever.objects.vehicles.Utility import net.psforever.objects.zones.Zone import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle} import net.psforever.packet.PlanetSideGamePacket -import net.psforever.packet.game.GenericActionEnum.GenericActionEnum import net.psforever.packet.game.GenericObjectActionEnum.GenericObjectActionEnum import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum -import net.psforever.packet.game.{ChatMsg, DeployableInfo, DeploymentAction, TriggeredSound} +import net.psforever.packet.game.{ChatMsg, DeployableInfo, DeploymentAction, GenericAction, TriggeredSound} import net.psforever.services.hart.HartTimer.OrbitalShuttleEvent import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3} @@ -82,7 +81,7 @@ object LocalAction { final case class SendGenericActionMessage( player_guid: PlanetSideGUID, - action_number: GenericActionEnum + action_number: GenericAction ) extends Action final case class RouterTelepadMessage(msg: String) extends Action final case class RouterTelepadTransport( diff --git a/src/main/scala/net/psforever/services/local/LocalServiceResponse.scala b/src/main/scala/net/psforever/services/local/LocalServiceResponse.scala index 2aadb269..e40a93ec 100644 --- a/src/main/scala/net/psforever/services/local/LocalServiceResponse.scala +++ b/src/main/scala/net/psforever/services/local/LocalServiceResponse.scala @@ -6,7 +6,6 @@ import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle} import net.psforever.objects.ce.{Deployable, DeployedItem} import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal} import net.psforever.objects.vehicles.Utility -import net.psforever.packet.game.GenericActionEnum.GenericActionEnum import net.psforever.packet.game.GenericObjectActionEnum.GenericObjectActionEnum import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum import net.psforever.packet.PlanetSideGamePacket @@ -44,7 +43,7 @@ object LocalResponse { final case class SendGenericObjectActionMessage(target_guid: PlanetSideGUID, action_number: GenericObjectActionEnum) extends Response final case class SendChatMsg(msg: ChatMsg) extends Response - final case class SendGenericActionMessage(action_num: GenericActionEnum) extends Response + final case class SendGenericActionMessage(action_num: GenericAction) extends Response final case class LluSpawned(llu: CaptureFlag) extends Response final case class LluDespawned(llu: CaptureFlag) extends Response diff --git a/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala b/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala index a9e991a0..707aed6a 100644 --- a/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala +++ b/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala @@ -9,7 +9,7 @@ import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal import net.psforever.objects.zones.Zone import net.psforever.objects.{Default, GlobalDefinitions} -import net.psforever.packet.game.{GenericActionEnum, PlanetsideAttributeEnum} +import net.psforever.packet.game.{GenericAction, PlanetsideAttributeEnum} import net.psforever.services.local.{LocalAction, LocalServiceMessage} import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} @@ -212,7 +212,7 @@ class HackCaptureActor extends Actor { building.Actor! BuildingActor.SetFaction(hackedByFaction) // todo: This should probably only go to those within the captured SOI who belong to the capturing faction - building.Zone.LocalEvents ! LocalServiceMessage(building.Zone.id, LocalAction.SendGenericActionMessage(PlanetSideGUID(-1), GenericActionEnum.BaseCaptureFanfare)) + building.Zone.LocalEvents ! LocalServiceMessage(building.Zone.id, LocalAction.SendGenericActionMessage(PlanetSideGUID(-1), GenericAction.FacilityCaptureFanfare)) } else { log.info("Base hack completed, but base was out of NTU.") } diff --git a/src/test/scala/game/GenericActionMessageTest.scala b/src/test/scala/game/GenericActionMessageTest.scala index 658fe3e4..583e0435 100644 --- a/src/test/scala/game/GenericActionMessageTest.scala +++ b/src/test/scala/game/GenericActionMessageTest.scala @@ -12,14 +12,14 @@ class GenericActionMessageTest extends Specification { "decode" in { PacketCoding.decodePacket(string).require match { case GenericActionMessage(action) => - action mustEqual 37 + action mustEqual GenericAction.NotLookingForSquad_RCV case _ => ko } } "encode" in { - val msg = GenericActionMessage(37) + val msg = GenericActionMessage(GenericAction.NotLookingForSquad_RCV) val pkt = PacketCoding.encodePacket(msg).require.toByteVector pkt mustEqual string