diff --git a/.codecov.yml b/.codecov.yml index 06eb9388a..80ad89238 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -9,6 +9,9 @@ coverage: threshold: 0.25% ignore: + - "src/main/java" + - "src/main/resources" + - "src/main/scala/akka" - "src/main/scala/net/psforever/objects/ObjectType.scala" - "src/main/scala/net/psforever/objects/avatar/Avatars.scala" - "src/main/scala/net/psforever/objects/ballistics/DamageResolution.scala" @@ -18,7 +21,12 @@ ignore: - "src/main/scala/net/psforever/objects/equipment/EquipmentSize.scala" - "src/main/scala/net/psforever/objects/equipment/Kits.scala" - "src/main/scala/net/psforever/objects/equipment/SItem.scala" + - "src/main/scala/net/psforever/objects/global" - "src/main/scala/net/psforever/objects/guid/AvailabilityPolicy.scala" + - "src/main/scala/net/psforever/objects/loadouts/EquipmentLoadout.scala" + - "src/main/scala/net/psforever/objects/loadouts/InfantryLoadout.scala" + - "src/main/scala/net/psforever/objects/loadouts/SquadLoadout.scala" + - "src/main/scala/net/psforever/objects/loadouts/VehicleLoadout.scala" - "src/main/scala/net/psforever/objects/serverobject/pad/AutoDriveControls.scala" - "src/main/scala/net/psforever/objects/serverobject/structures/StructureType.scala" - "src/main/scala/net/psforever/objects/serverobject/shuttle/ShuttleAmenity.scala" @@ -56,24 +64,42 @@ ignore: - "src/main/scala/net/psforever/packet/ControlPacketOpcode.scala" - "src/main/scala/net/psforever/packet/CryptoPacketOpcode.scala" - "src/main/scala/net/psforever/packet/GamePacketOpcode.scala" - - "src/main/scala/net/psforever/types/Angular.scala" - - "src/main/scala/net/psforever/types/CertificationType.scala" - - "src/main/scala/net/psforever/types/ChatMessageType.scala" - - "src/main/scala/net/psforever/types/DriveState.scala" - - "src/main/scala/net/psforever/types/EmoteType.scala" - - "src/main/scala/net/psforever/types/ExoSuitType.scala" - - "src/main/scala/net/psforever/types/GrenadeState.scala" - - "src/main/scala/net/psforever/types/ImplantType.scala" - - "src/main/scala/net/psforever/types/MeritCommendation.scala" - - "src/main/scala/net/psforever/types/PlanetSideEmpire.scala" - - "src/main/scala/net/psforever/types/TransactionType.scala" + - "src/main/scala/net/psforever/packet/ResetSequenceOpcode.scala" + - "src/main/scala/net/psforever/persistence" + - "src/main/scala/net/psforever/services/account/IPAddress.scala" + - "src/main/scala/net/psforever/services/account/ReceiveAccountData.scala" + - "src/main/scala/net/psforever/services/account/ReceiveIPAddress.scala" + - "src/main/scala/net/psforever/services/account/RetrieveAccountData.scala" + - "src/main/scala/net/psforever/services/account/RetrieveIPAddress.scala" + - "src/main/scala/net/psforever/services/account/StoreAccountData.scala" + - "src/main/scala/net/psforever/services/account/StoreIPAddress.scala" - "src/main/scala/net/psforever/services/avatar/AvatarAction.scala" - - "src/main/scala/net/psforever/services/galaxy/AvatarServiceMessage.scala" - - "src/main/scala/net/psforever/services/galaxy/AvatarServiceResponse.scala" - - "src/main/scala/net/psforever/services/galaxy/GalaxyAction.scala" + - "src/main/scala/net/psforever/services/avatar/AvatarService.scala" + - "src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala" + - "src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala" + - "src/main/scala/net/psforever/services/base/bus" + - "src/main/scala/net/psforever/services/base/envelope" + - "src/main/scala/net/psforever/services/chat/ChatChannel.scala" + - "src/main/scala/net/psforever/services/galaxy/GalaxyAction" + - "src/main/scala/net/psforever/services/galaxy/GalaxyService" - "src/main/scala/net/psforever/services/galaxy/GalaxyServiceMessage.scala" - "src/main/scala/net/psforever/services/galaxy/GalaxyServiceResponse.scala" - "src/main/scala/net/psforever/services/hart/HartEvent.scala" - "src/main/scala/net/psforever/services/hart/HartTimerActions.scala" - - "src/main/scala/net/psforever/services/local/LocalAction.scala" - - "src/main/scala/net/psforever/services/vehicle/VehicleAction.scala" + - "src/main/scala/net/psforever/services/local/LocalAction" + - "src/main/scala/net/psforever/services/local/LocalService" + - "src/main/scala/net/psforever/services/local/LocalServiceMessage.scala" + - "src/main/scala/net/psforever/services/local/LocalServiceResponse.scala" + - "src/main/scala/net/psforever/services/teamwork/SquadServiceMessage.scala" + - "src/main/scala/net/psforever/services/teamwork/SquadServiceResponse.scala" + - "src/main/scala/net/psforever/services/vehicle/VehicleAction" + - "src/main/scala/net/psforever/services/vehicle/VehicleService" + - "src/main/scala/net/psforever/services/vehicle/VehicleServiceMessage.scala" + - "src/main/scala/net/psforever/services/vehicle/VehicleServiceResponse.scala" + - "src/main/scala/net/psforever/services/Service.scala" + - "src/main/scala/net/psforever/types" + - "src/main/scala/net/psforever/util" + - "src/main/scala/net/psforever/zones" + - "src/main/scala/net/psforever/IFinalizable.scala" + - "src/main/scala/scodec" + - "src/test" diff --git a/server/src/test/scala/actor/service/AvatarServiceTest.scala b/server/src/test/scala/actor/service/AvatarServiceTest.scala index eb33c2ab7..520398554 100644 --- a/server/src/test/scala/actor/service/AvatarServiceTest.scala +++ b/server/src/test/scala/actor/service/AvatarServiceTest.scala @@ -16,6 +16,7 @@ import net.psforever.packet.game.{ObjectCreateMessage, PlayerStateMessageUpstrea import net.psforever.types._ import net.psforever.services.{RemoverActor, Service, ServiceManager} import net.psforever.services.avatar._ +import net.psforever.services.avatar.support.{CorpseEnvelope, DropItemEnvelope, PickupItemEnvelope, ReleaseEnvelope} import net.psforever.services.base.message.{ChangeAmmo, ChangeFireState_Start, ChangeFireState_Stop, ConcealPlayer, ObjectDelete, PlanetsideAttribute, ReloadTool, WeaponDryFire} class AvatarService1Test extends ActorTest { @@ -153,7 +154,7 @@ class DroptItemTest extends ActorTest { "AvatarService" should { "pass DropItem" in { service ! Service.Join("test") - service ! DropItemMessage("test", PlanetSideGUID(10), AvatarAction.DropItem(tool), Zone.Nowhere) + service ! DropItemEnvelope("test", PlanetSideGUID(10), AvatarAction.DropItem(tool), Zone.Nowhere) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarAction.DropCreatedItem(pkt))) } } @@ -324,7 +325,7 @@ class PickupItemTest extends ActorTest { ServiceManager.boot(system) val service = system.actorOf(Props(classOf[AvatarService]), AvatarServiceTest.TestName) service ! Service.Join("test") - service ! PickupItemMessage("test", AvatarAction.PickupItem(tool), Zone.Nowhere) + service ! PickupItemEnvelope("test", AvatarAction.PickupItem(tool), Zone.Nowhere) expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), ObjectDelete(tool.GUID, 0))) } } @@ -509,7 +510,7 @@ class AvatarReleaseTest extends FreedContextActorTest { assert(zone.Corpses.size == 1) assert(obj.HasGUID) val guid = obj.GUID - zone.AvatarEvents ! ReleaseMessage("test", AvatarAction.Release(obj, zone, Some(1 second))) //alive for one second + zone.AvatarEvents ! ReleaseEnvelope("test", AvatarAction.Release(obj, zone, Some(1 second))) //alive for one second val reply1 = subscriber.receiveOne(200 milliseconds) assert(reply1.isInstanceOf[AvatarServiceResponse]) @@ -560,7 +561,7 @@ class AvatarReleaseEarly1Test extends FreedContextActorTest { assert(zone.Corpses.size == 1) assert(obj.HasGUID) val guid = obj.GUID - zone.AvatarEvents ! ReleaseMessage("test", AvatarAction.Release(obj, zone)) //3+ minutes! + zone.AvatarEvents ! ReleaseEnvelope("test", AvatarAction.Release(obj, zone)) //3+ minutes! val reply1 = subscriber.receiveOne(200 milliseconds) assert(reply1.isInstanceOf[AvatarServiceResponse]) @@ -576,8 +577,8 @@ class AvatarReleaseEarly1Test extends FreedContextActorTest { val reply2msg = reply2.asInstanceOf[AvatarServiceResponse] assert(reply2msg.channel.equals("/test/Avatar")) assert(reply2msg.filter == Service.defaultPlayerGUID) - assert(reply2msg.reply.isInstanceOf[AvatarAction.ObjectDelete]) - assert(reply2msg.reply.asInstanceOf[AvatarAction.ObjectDelete].item_guid == guid) + assert(reply2msg.reply.isInstanceOf[ObjectDelete]) + assert(reply2msg.reply.asInstanceOf[ObjectDelete].obj_guid == guid) subscriber.expectNoMessage(1 seconds) assert(zone.Corpses.isEmpty) @@ -618,7 +619,7 @@ class AvatarReleaseEarly2Test extends FreedContextActorTest { assert(zone.Corpses.size == 1) assert(obj.HasGUID) val guid = obj.GUID - zone.AvatarEvents ! ReleaseMessage("test", AvatarAction.Release(obj, zone)) //3+ minutes! + zone.AvatarEvents ! ReleaseEnvelope("test", AvatarAction.Release(obj, zone)) //3+ minutes! val reply1 = subscriber.receiveOne(200 milliseconds) assert(reply1.isInstanceOf[AvatarServiceResponse]) diff --git a/src/main/scala/net/psforever/actors/session/csr/GeneralLogic.scala b/src/main/scala/net/psforever/actors/session/csr/GeneralLogic.scala index 5aa14d2f6..8d19e2ee2 100644 --- a/src/main/scala/net/psforever/actors/session/csr/GeneralLogic.scala +++ b/src/main/scala/net/psforever/actors/session/csr/GeneralLogic.scala @@ -34,7 +34,8 @@ import net.psforever.packet.PlanetSideGamePacket import net.psforever.packet.game.OutfitEventAction.{Initial, OutfitInfo, OutfitRankNames, Unk1} import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitEvent, OutfitMemberEvent, OutfitMembershipRequest, OutfitMembershipResponse, OutfitRequest, OutfitRequestAction, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage} import net.psforever.services.RemoverActor -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage, CorpseEnvelope} +import net.psforever.services.avatar.support.CorpseEnvelope +import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.services.base.message.PlanetsideAttribute import net.psforever.types.{CapacitorStateType, ChatMessageType, Cosmetic, ExoSuitType, PlanetSideEmpire, PlanetSideGUID, Vector3} diff --git a/src/main/scala/net/psforever/actors/session/csr/VehicleLogic.scala b/src/main/scala/net/psforever/actors/session/csr/VehicleLogic.scala index 78ba0e05f..e345d40d6 100644 --- a/src/main/scala/net/psforever/actors/session/csr/VehicleLogic.scala +++ b/src/main/scala/net/psforever/actors/session/csr/VehicleLogic.scala @@ -11,10 +11,8 @@ import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.vehicles.control.BfrFlight import net.psforever.objects.zones.Zone import net.psforever.objects.zones.interaction.InteractsWithZone -import net.psforever.packet.game.{ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, PlanetsideAttributeMessage, VehicleStateMessage, VehicleSubStateMessage} -import net.psforever.services.avatar.AvatarServiceMessage -import net.psforever.services.base.CachedMessage -import net.psforever.services.base.message.PlanetsideAttribute +import net.psforever.packet.game.{ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, VehicleStateMessage, VehicleSubStateMessage} +import net.psforever.services.base.CachedEnvelope import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.types.{DriveState, Vector3} @@ -245,7 +243,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex obj.Velocity = vel sessionLogic.updateBlockMap(obj, pos) obj.zoneInteractions() - continent.VehicleEvents ! CachedMessage( + continent.VehicleEvents ! CachedEnvelope( continent.id, player.GUID, VehicleAction.VehicleState(vehicle_guid, unk1, pos, ang, obj.Velocity, obj.Flying, 0, 0, 15, unk5 = false, obj.Cloaked) diff --git a/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala b/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala index ee784c3c6..e0495c652 100644 --- a/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala @@ -43,7 +43,7 @@ import net.psforever.packet.PlanetSideGamePacket import net.psforever.packet.game.{ActionCancelMessage, ActionResultMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestAction, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitMembershipRequest, OutfitMembershipRequestAction, OutfitMembershipResponse, OutfitRequest, OutfitRequestAction, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage} import net.psforever.services.account.{AccountPersistenceService, RetrieveAccountData} import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.base.CachedMessage +import net.psforever.services.base.CachedEnvelope import net.psforever.services.base.message.PlanetsideAttribute import net.psforever.services.local.support.CaptureFlagManager import net.psforever.types.{CapacitorStateType, ChatMessageType, Cosmetic, ExoSuitType, ImplantType, PlanetSideEmpire, PlanetSideGUID, Vector3} diff --git a/src/main/scala/net/psforever/actors/session/normal/VehicleLogic.scala b/src/main/scala/net/psforever/actors/session/normal/VehicleLogic.scala index 59fd325c9..21827dfbb 100644 --- a/src/main/scala/net/psforever/actors/session/normal/VehicleLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/VehicleLogic.scala @@ -12,7 +12,7 @@ import net.psforever.objects.vehicles.control.BfrFlight import net.psforever.objects.zones.Zone import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.packet.game.{ChatMsg, ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, VehicleStateMessage, VehicleSubStateMessage} -import net.psforever.services.base.CachedMessage +import net.psforever.services.base.CachedEnvelope import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.types.{ChatMessageType, DriveState, Vector3} @@ -76,7 +76,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex obj.Position = position obj.Orientation = angle // - continent.VehicleEvents ! CachedMessage( + continent.VehicleEvents ! CachedEnvelope( continent.id, player.GUID, VehicleAction.VehicleState( @@ -239,7 +239,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex obj.Velocity = vel sessionLogic.updateBlockMap(obj, pos) obj.zoneInteractions() - continent.VehicleEvents ! CachedMessage( + continent.VehicleEvents ! CachedEnvelope( continent.id, player.GUID, VehicleAction.VehicleState(vehicle_guid, unk1, pos, ang, obj.Velocity, obj.Flying, 0, 0, 15, unk5 = false, obj.Cloaked) diff --git a/src/main/scala/net/psforever/actors/session/spectator/GeneralLogic.scala b/src/main/scala/net/psforever/actors/session/spectator/GeneralLogic.scala index 0a89abf82..46c737541 100644 --- a/src/main/scala/net/psforever/actors/session/spectator/GeneralLogic.scala +++ b/src/main/scala/net/psforever/actors/session/spectator/GeneralLogic.scala @@ -17,7 +17,7 @@ import net.psforever.packet.PlanetSideGamePacket import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitMembershipRequest, OutfitMembershipResponse, OutfitRequest, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage} import net.psforever.services.account.AccountPersistenceService import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.base.CachedMessage +import net.psforever.services.base.CachedEnvelope import net.psforever.services.base.message.PlanetsideAttribute import net.psforever.types.{ExoSuitType, Vector3} @@ -76,7 +76,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex player.Crouching = isCrouching player.Jumping = isJumping player.Cloaked = player.ExoSuit == ExoSuitType.Infiltration && isCloaking - continent.AvatarEvents ! CachedMessage( + continent.AvatarEvents ! CachedEnvelope( "spectator", avatarGuid, AvatarAction.PlayerState( diff --git a/src/main/scala/net/psforever/actors/session/spectator/VehicleLogic.scala b/src/main/scala/net/psforever/actors/session/spectator/VehicleLogic.scala index af51a9628..d56536feb 100644 --- a/src/main/scala/net/psforever/actors/session/spectator/VehicleLogic.scala +++ b/src/main/scala/net/psforever/actors/session/spectator/VehicleLogic.scala @@ -7,7 +7,7 @@ import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.Vehicle import net.psforever.objects.serverobject.deploy.Deployment import net.psforever.packet.game.{ChildObjectStateMessage, DeployRequestMessage, FrameVehicleStateMessage, VehicleStateMessage, VehicleSubStateMessage} -import net.psforever.services.base.CachedMessage +import net.psforever.services.base.CachedEnvelope import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.types.{DriveState, Vector3} @@ -40,7 +40,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex obj.Velocity = vel sessionLogic.updateBlockMap(obj, pos) obj.zoneInteractions() - continent.VehicleEvents ! CachedMessage( + continent.VehicleEvents ! CachedEnvelope( continent.id, player.GUID, VehicleAction.VehicleState(vehicle_guid, unk1, pos, ang, obj.Velocity, obj.Flying, 0, 0, 15, unk5 = false, obj.Cloaked) diff --git a/src/main/scala/net/psforever/actors/session/support/ChatOperations.scala b/src/main/scala/net/psforever/actors/session/support/ChatOperations.scala index d53a0685f..10320bf40 100644 --- a/src/main/scala/net/psforever/actors/session/support/ChatOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/ChatOperations.scala @@ -20,8 +20,7 @@ import net.psforever.packet.game.{SetChatFilterMessage, TimeOfDayMessage} import net.psforever.services.Service import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.services.chat.{DefaultChannel, OutfitChannel, SquadChannel} -import net.psforever.services.local.support.HackCaptureActor -import net.psforever.services.local.CaptureMessage +import net.psforever.services.local.support.{CaptureEnvelope, HackCaptureActor} import net.psforever.services.teamwork.{SquadResponse, SquadService, SquadServiceResponse} import net.psforever.types.ChatMessageType.CMT_QUIT import org.log4s.Logger @@ -406,7 +405,7 @@ class ChatOperations( } else { if (building.CaptureTerminalIsHacked) { - zone.LocalEvents ! CaptureMessage(HackCaptureActor.ResecureCaptureTerminal(terminal, zone, PlayerSource.Nobody)) + zone.LocalEvents ! CaptureEnvelope(HackCaptureActor.ResecureCaptureTerminal(terminal, zone, PlayerSource.Nobody)) } building.Actor ! BuildingActor.SetFaction(faction) building.Actor ! BuildingActor.AmenityStateChange(terminal, Some(false)) diff --git a/src/main/scala/net/psforever/actors/session/support/GeneralOperations.scala b/src/main/scala/net/psforever/actors/session/support/GeneralOperations.scala index 483b03306..2b19df496 100644 --- a/src/main/scala/net/psforever/actors/session/support/GeneralOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/GeneralOperations.scala @@ -18,10 +18,10 @@ import net.psforever.objects.vehicles.Utility.InternalTelepad import net.psforever.objects.zones.blockmap.BlockMapEntity import net.psforever.objects.zones.exp.ToDatabase import net.psforever.services.RemoverActor -import net.psforever.services.avatar.GroundEnvelope +import net.psforever.services.avatar.support.GroundEnvelope import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} -import net.psforever.services.local.support.HackCaptureActor -import net.psforever.services.local.{CaptureMessage, LocalAction, LocalServiceMessage} +import net.psforever.services.local.support.{CaptureEnvelope, HackCaptureActor} +import net.psforever.services.local.{LocalAction, LocalServiceMessage} import scala.collection.mutable import scala.concurrent.ExecutionContext.Implicits.global @@ -1292,7 +1292,7 @@ class GeneralOperations( continent.GUID(specialItemSlotGuid) match { case Some(llu: CaptureFlag) => if (llu.Target.GUID == captureTerminal.Owner.GUID) { - continent.LocalEvents ! CaptureMessage(HackCaptureActor.FlagCaptured(llu)) + continent.LocalEvents ! CaptureEnvelope(HackCaptureActor.FlagCaptured(llu)) } else { log.info( s"LLU target is not this base. Target GUID: ${llu.Target.GUID} This base: ${captureTerminal.Owner.GUID}" 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 c7adf3df8..346a7f6bf 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionMountHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionMountHandlers.scala @@ -8,7 +8,7 @@ import net.psforever.objects.{PlanetSideGameObject, Tool, Vehicle} import net.psforever.objects.vehicles.{CargoBehavior, MountableWeapons} import net.psforever.objects.vital.InGameHistory import net.psforever.packet.game.{DismountVehicleCargoMsg, GenericObjectActionMessage, InventoryStateMessage, MountVehicleCargoMsg, MountVehicleMsg, ObjectAttachMessage, ObjectDetachMessage, PlanetsideAttributeMessage} -import net.psforever.services.base.CachedMessage +import net.psforever.services.base.CachedEnvelope import net.psforever.services.base.message.SendResponse import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.types.{BailType, PlanetSideGUID, Vector3} @@ -225,7 +225,7 @@ class SessionMountHandlers( sessionLogic.vehicles.ServerVehicleOverrideStop(v) }*/ v.Velocity = Vector3.Zero - continent.VehicleEvents ! CachedMessage( + continent.VehicleEvents ! CachedEnvelope( continent.id, tplayer.GUID, VehicleAction.VehicleState(v.GUID, unk1 = 0, tplayer.Position, v.Orientation, v.Velocity, v.Flying, unk3 = 0, unk4 = 0, wheel_direction = 15, unk5 = false, unk6 = v.Cloaked) 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 a895ab087..15197b0f0 100644 --- a/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala @@ -28,7 +28,7 @@ import net.psforever.objects.vital.projectile.ProjectileReason import net.psforever.objects.zones.exp.ToDatabase import net.psforever.packet.game.UplinkRequest import net.psforever.services.Service -import net.psforever.services.base.CachedMessage +import net.psforever.services.base.CachedEnvelope import net.psforever.services.base.message.{ChangeAmmo, ChangeFireState_Start, ChangeFireState_Stop, ReloadTool, SendResponse, WeaponDryFire} import net.psforever.services.local.LocalServiceMessage import net.psforever.types.{ChatMessageType, PlanetSideEmpire, ValidPlanetSideGUID, Vector3} @@ -494,7 +494,7 @@ class WeaponAndProjectileOperations( projectile.Position = shot_pos projectile.Orientation = shot_orient projectile.Velocity = shot_vel - continent.AvatarEvents ! CachedMessage( + continent.AvatarEvents ! CachedEnvelope( continent.id, player.GUID, AvatarAction.ProjectileState( 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 68d3f3a58..478704782 100644 --- a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala @@ -22,7 +22,7 @@ import net.psforever.objects.vital.{InGameHistory, IncarnationActivity, Reconstr import net.psforever.objects.zones.blockmap.BlockMapEntity import net.psforever.packet.game.GenericAction.FirstPersonViewWithEffect import net.psforever.packet.game.{CampaignStatistic, ChangeFireStateMessage_Start, CloudInfo, GenericActionMessage, GenericObjectActionEnum, HackState7, MailMessage, ObjectDetectedMessage, SessionStatistic, StormInfo, TriggeredSound, TrainingZoneMessage, WeatherMessage} -import net.psforever.services.avatar.{CorpseEnvelope, ReleaseMessage} +import net.psforever.services.avatar.support.{CorpseEnvelope, ReleaseEnvelope} import net.psforever.services.base.message.{GenericObjectAction, ObjectDelete, PlanetsideAttribute, SendResponse} import net.psforever.services.chat.DefaultChannel @@ -2946,7 +2946,7 @@ class ZoningOperations( tplayer.Release DepictPlayerAsCorpse(tplayer) zone.Population ! Zone.Corpse.Add(tplayer) - zone.AvatarEvents ! ReleaseMessage(zone.id, AvatarAction.Release(tplayer, zone)) + zone.AvatarEvents ! ReleaseEnvelope(zone.id, AvatarAction.Release(tplayer, zone)) } /** diff --git a/src/main/scala/net/psforever/actors/zone/building/CavernFacilityLogic.scala b/src/main/scala/net/psforever/actors/zone/building/CavernFacilityLogic.scala index 55ef7efd4..c6a0d0e1e 100644 --- a/src/main/scala/net/psforever/actors/zone/building/CavernFacilityLogic.scala +++ b/src/main/scala/net/psforever/actors/zone/building/CavernFacilityLogic.scala @@ -8,8 +8,7 @@ import net.psforever.actors.zone.{BuildingActor, BuildingControlDetails, ZoneAct import net.psforever.objects.serverobject.structures.{Amenity, Building, StructureType} import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAware, CaptureTerminalAwareBehavior} import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage} -import net.psforever.services.local.support.HackClearActor -import net.psforever.services.local.HackClearMessage +import net.psforever.services.local.support.{HackClearActor, HackClearEnvelope} import net.psforever.types.PlanetSideEmpire /** @@ -53,7 +52,7 @@ case object CavernFacilityLogic // When a CC is hacked (or resecured) all currently hacked amenities for the base should return to their default unhacked state building.HackableAmenities.foreach(amenity => { if (amenity.HackedBy.isDefined) { - building.Zone.LocalEvents ! HackClearMessage(HackClearActor.ObjectIsResecured(amenity)) + building.Zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(amenity)) } }) // No map update needed - will be sent by `HackCaptureActor` when required diff --git a/src/main/scala/net/psforever/actors/zone/building/FacilityLogic.scala b/src/main/scala/net/psforever/actors/zone/building/FacilityLogic.scala index de48e4bc7..2998750a9 100644 --- a/src/main/scala/net/psforever/actors/zone/building/FacilityLogic.scala +++ b/src/main/scala/net/psforever/actors/zone/building/FacilityLogic.scala @@ -8,8 +8,7 @@ import net.psforever.actors.zone.{BuildingActor, BuildingControlDetails} import net.psforever.objects.serverobject.structures.{Amenity, Building} import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAware, CaptureTerminalAwareBehavior} import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage} -import net.psforever.services.local.support.HackClearActor -import net.psforever.services.local.HackClearMessage +import net.psforever.services.local.support.{HackClearActor, HackClearEnvelope} import net.psforever.types.PlanetSideEmpire /** @@ -53,7 +52,7 @@ case object FacilityLogic // When a CC is hacked (or resecured) all currently hacked amenities for the base should return to their default unhacked state building.HackableAmenities.foreach(amenity => { if (amenity.HackedBy.isDefined) { - building.Zone.LocalEvents ! HackClearMessage(HackClearActor.ObjectIsResecured(amenity)) + building.Zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(amenity)) } }) // No map update needed - will be sent by `HackCaptureActor` when required diff --git a/src/main/scala/net/psforever/actors/zone/building/MajorFacilityLogic.scala b/src/main/scala/net/psforever/actors/zone/building/MajorFacilityLogic.scala index dbef18ddc..b12bf2ab9 100644 --- a/src/main/scala/net/psforever/actors/zone/building/MajorFacilityLogic.scala +++ b/src/main/scala/net/psforever/actors/zone/building/MajorFacilityLogic.scala @@ -18,8 +18,8 @@ import net.psforever.services.InterstellarClusterService import net.psforever.services.avatar.AvatarServiceMessage import net.psforever.services.base.message.{GenericObjectAction, PlanetsideAttribute, SendResponse} import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage} -import net.psforever.services.local.{CaptureMessage, HackClearMessage, LocalServiceMessage} -import net.psforever.services.local.support.{HackCaptureActor, HackClearActor} +import net.psforever.services.local.LocalServiceMessage +import net.psforever.services.local.support.{CaptureEnvelope, HackCaptureActor, HackClearActor, HackClearEnvelope} import net.psforever.types.PlanetSideEmpire /** @@ -103,7 +103,7 @@ case object MajorFacilityLogic hackedAmenities } amenitiesToClear.foreach { amenity => - building.Zone.LocalEvents ! HackClearMessage(HackClearActor.ObjectIsResecured(amenity)) + building.Zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(amenity)) } // No map update needed - will be sent by `HackCaptureActor` when required case dome: ForceDomePhysics => @@ -282,7 +282,7 @@ case object MajorFacilityLogic (bldg.GetFlag, bldg.CaptureTerminal) match { case (Some(flag), Some(terminal)) if (flag.Target eq building) && flag.Faction != building.Faction => //our hack destination may have been compromised and the hack needs to be cancelled - bldg.Zone.LocalEvents ! CaptureMessage(HackCaptureActor.ResecureCaptureTerminal(terminal, terminal.Zone, PlayerSource.Nobody)) + bldg.Zone.LocalEvents ! CaptureEnvelope(HackCaptureActor.ResecureCaptureTerminal(terminal, terminal.Zone, PlayerSource.Nobody)) case _ => () } Behaviors.same diff --git a/src/main/scala/net/psforever/objects/inventory/GridInventory.scala b/src/main/scala/net/psforever/objects/inventory/GridInventory.scala index eafc880bb..d77f30dd1 100644 --- a/src/main/scala/net/psforever/objects/inventory/GridInventory.scala +++ b/src/main/scala/net/psforever/objects/inventory/GridInventory.scala @@ -702,15 +702,18 @@ object GridInventory { */ private def sortKnapsack(list: List[InventoryItem], width: Int, height: Int): Unit = { val root = new KnapsackNode(0, 0, width, height) - list.foreach(item => { - findKnapsackSpace(root, item.obj.Tile.Width, item.obj.Tile.Height) match { + list.foreach { item => + val tile = item.obj.Tile + val twidth = tile.Width + val theight = tile.Height + findKnapsackSpace(root, twidth, theight) match { case Some(node) => - splitKnapsackSpace(node, item.obj.Tile.Width, item.obj.Tile.Height) + splitKnapsackSpace(node, twidth, theight) item.start = node.y * width + node.x - case _ => ; + case _ => item.start = -1 } - }) + } } /** @@ -720,42 +723,32 @@ object GridInventory { * Horizontal space for the `down` child is emphasized over vertical space for the `right` child. * By dividing and reducing a defined space like this, it can be tightly packed with a given number of elements.
*
- * Due to the nature of the knapsack problem and the naivette of the algorithm, small holes in the solution are bound to crop-up. + * Due to the nature of the knapsack problem and the naivete of the algorithm, small holes in the solution are bound to crop-up. * @param x the x-coordinate, upper left corner * @param y the y-coordinate, upper left corner * @param width the width * @param height the height */ - private class KnapsackNode(var x: Int, var y: Int, var width: Int, var height: Int) { - private var used: Boolean = false - var down: Option[KnapsackNode] = None - var right: Option[KnapsackNode] = None + private class KnapsackNode(val x: Int, val y: Int, val width: Int, val height: Int) { + private var used: Boolean = false + private var down: Option[KnapsackNode] = None + private var right: Option[KnapsackNode] = None def Used: Boolean = used - /** - * Initialize the `down` and `right` children of this node. - */ - def Split(): Unit = { - used = true - down = Some(new KnapsackNode(0, 0, 0, 0)) - right = Some(new KnapsackNode(0, 0, 0, 0)) - } + def Down: Option[KnapsackNode] = down + + def Right: Option[KnapsackNode] = right /** - * Change the dimensions of the node.
- *
- * Use: `{node}(nx, ny, nw, nh)` - * @param nx the new x-coordinate, upper left corner - * @param ny the new y-coordinate, upper left corner - * @param nw the new width - * @param nh the new height - */ - def apply(nx: Int, ny: Int, nw: Int, nh: Int): Unit = { - x = nx - y = ny - width = nw - height = nh + * Initialize the `down` and `right` children of this node. + * @param insertDown new "down" knapsack division + * @param insertRight new "right" knapsack division + */ + def Split(insertDown: KnapsackNode, insertRight: KnapsackNode): Unit = { + used = true + down = Some(insertDown) + right = Some(insertRight) } } @@ -768,7 +761,7 @@ object GridInventory { */ private def findKnapsackSpace(node: KnapsackNode, width: Int, height: Int): Option[KnapsackNode] = { if (node.Used) { - findKnapsackSpace(node.right.get, width, height).orElse(findKnapsackSpace(node.down.get, width, height)) + findKnapsackSpace(node.Right.get, width, height).orElse(findKnapsackSpace(node.Down.get, width, height)) } else if (width <= node.width && height <= node.height) { Some(node) } else { @@ -787,13 +780,14 @@ object GridInventory { * @param height height of the element */ private def splitKnapsackSpace(node: KnapsackNode, width: Int, height: Int): Unit = { - node.Split() - node.down.get(node.x, node.y + height, node.width, node.height - height) - node.right.get(node.x + width, node.y, node.width - width, height) + node.Split( + new KnapsackNode(node.x, node.y + height, node.width, node.height - height), + new KnapsackNode(node.x + width, node.y, node.width - width, height) + ) } def toPrintedList(inv: GridInventory): String = { - val list = new StringBuilder + val list = new mutable.StringBuilder list.append("\n") inv.Items.zipWithIndex.foreach { case (InventoryItem(obj, start), index) => @@ -803,6 +797,6 @@ object GridInventory { } def toPrintedGrid(inv: GridInventory): String = { - new StringBuilder().append("\n").append(inv.grid.toSeq.grouped(inv.width).mkString("\n")).toString + new mutable.StringBuilder().append("\n").append(inv.grid.toSeq.grouped(inv.width).mkString("\n")).toString } } diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala index 2b10df5ac..1e172174f 100644 --- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala +++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala @@ -12,8 +12,8 @@ import net.psforever.types.Vector3 import net.psforever.services.avatar.AvatarServiceMessage import net.psforever.services.base.message.{ObjectDelete, PlanetsideAttribute, SendResponse} import net.psforever.services.base.support.SupportActor -import net.psforever.services.vehicle.support.TurretUpgrader -import net.psforever.services.vehicle.{TurretMessage, VehicleServiceMessage} +import net.psforever.services.vehicle.support.{TurretEnvelope, TurretUpgrader} +import net.psforever.services.vehicle.VehicleServiceMessage /** * The "control" `Actor` mixin for damage-handling code for `WeaponTurret` objects. @@ -138,8 +138,8 @@ object DamageableWeaponTurret { case turret: WeaponTurret => if (turret.Upgrade != TurretUpgrade.None) { val vehicleEvents = zone.VehicleEvents - vehicleEvents ! TurretMessage(SupportActor.ClearSpecific(List(turret), zone)) - vehicleEvents ! TurretMessage(TurretUpgrader.AddTask(turret, zone, TurretUpgrade.None)) + vehicleEvents ! TurretEnvelope(SupportActor.ClearSpecific(List(turret), zone)) + vehicleEvents ! TurretEnvelope(TurretUpgrader.AddTask(turret, zone, TurretUpgrade.None)) } case _ => } diff --git a/src/main/scala/net/psforever/objects/serverobject/doors/DoorControl.scala b/src/main/scala/net/psforever/objects/serverobject/doors/DoorControl.scala index e9997872e..cd8b6a599 100644 --- a/src/main/scala/net/psforever/objects/serverobject/doors/DoorControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/doors/DoorControl.scala @@ -9,8 +9,8 @@ import net.psforever.objects.serverobject.locks.IFFLock import net.psforever.objects.serverobject.structures.PoweredAmenityControl import net.psforever.services.Service import net.psforever.services.base.support.SupportActor -import net.psforever.services.local.support.DoorCloseActor -import net.psforever.services.local.{DoorMessage, LocalAction, LocalServiceResponse} +import net.psforever.services.local.support.{DoorCloseActor, DoorMessage} +import net.psforever.services.local.{LocalAction, LocalServiceResponse} /** * An `Actor` that handles messages being dispatched to a specific `Door`. diff --git a/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala b/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala index 86c8f6774..50ac0796a 100644 --- a/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala +++ b/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala @@ -13,8 +13,8 @@ import net.psforever.packet.game.{GenericObjectActionMessage, HackMessage, HackS import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} import net.psforever.services.avatar.AvatarServiceMessage import net.psforever.services.base.message.SendResponse -import net.psforever.services.local.support.HackClearActor -import net.psforever.services.local.{HackClearMessage, HackEntityMessage, LocalAction, LocalServiceMessage} +import net.psforever.services.local.support.{HackClearActor, HackClearEnvelope, HackEntityEnvelope} +import net.psforever.services.local.{LocalAction, LocalServiceMessage} import scala.annotation.unused import scala.util.{Failure, Success} @@ -177,7 +177,7 @@ object GenericHackables { LocalAction.TriggerSound(target.HackSound, tplayer.Position, 30, 0.49803925f) ) val duration = target.HackEffectDuration(user.avatar.hackingSkillLevel()) - zone.LocalEvents ! HackEntityMessage( + zone.LocalEvents ! HackEntityEnvelope( zoneId, pguid, LocalAction.HackObject(target.GUID, hackValue, HackState7.Unk8), @@ -206,7 +206,7 @@ object GenericHackables { val currVirus = building.virusId building.virusId = 8 building.virusInstalledBy = None - zone.LocalEvents ! HackClearMessage(HackClearActor.ObjectIsResecured(target)) + zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(target)) zone.LocalEvents ! LocalServiceMessage( zone.id, SendResponse(GenericObjectActionMessage(target.GUID, 60)) @@ -214,11 +214,11 @@ object GenericHackables { currVirus match { case 0L => building.HackableAmenities.filter(d => d.Definition == GlobalDefinitions.lock_external).foreach { iff => - zone.LocalEvents ! HackClearMessage(HackClearActor.ObjectIsResecured(iff)) + zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(iff)) } case 4L => building.HackableAmenities.filter(d => d.Definition == GlobalDefinitions.order_terminal).foreach { term => - zone.LocalEvents ! HackClearMessage(HackClearActor.ObjectIsResecured(term)) + zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(term)) } case _ => () } @@ -232,13 +232,13 @@ object GenericHackables { case 0L => if (virus != 0) { building.HackableAmenities.filter(d => d.Definition == GlobalDefinitions.lock_external).foreach { iff => - zone.LocalEvents ! HackClearMessage(HackClearActor.ObjectIsResecured(iff)) + zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(iff)) } } case 4L => if (virus != 4) { building.HackableAmenities.filter(d => d.Definition == GlobalDefinitions.order_terminal).foreach { term => - zone.LocalEvents ! HackClearMessage(HackClearActor.ObjectIsResecured(term)) + zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(term)) } } case _ => () @@ -266,7 +266,7 @@ object GenericHackables { pguid, LocalAction.TriggerSound(TriggeredSound.TREKSuccessful, tplayer.Position, 30, 0.49803925f) ) - zone.LocalEvents ! HackEntityMessage( + zone.LocalEvents ! HackEntityEnvelope( zoneId, pguid, LocalAction.HackObject(target.GUID, installedVirusDuration.toLong, hackState), @@ -285,7 +285,7 @@ object GenericHackables { case 0L => building.HackableAmenities.filter(d => d.Definition == GlobalDefinitions.lock_external).foreach{ iff => iff.HackedBy = tplayer - zone.LocalEvents ! HackEntityMessage( + zone.LocalEvents ! HackEntityEnvelope( zoneId, pguid, LocalAction.HackObject(target.GUID, hackValue.toLong, HackState7.Unk8), @@ -295,7 +295,7 @@ object GenericHackables { case 4L => building.HackableAmenities.filter(d => d.Definition == GlobalDefinitions.order_terminal).foreach{ term => term.HackedBy = tplayer - zone.LocalEvents ! HackEntityMessage( + zone.LocalEvents ! HackEntityEnvelope( zoneId, pguid, LocalAction.HackObject(term.GUID, hackValue.toLong, HackState7.Unk8), diff --git a/src/main/scala/net/psforever/objects/serverobject/locks/IFFLocks.scala b/src/main/scala/net/psforever/objects/serverobject/locks/IFFLocks.scala index 6404445f4..da3516190 100644 --- a/src/main/scala/net/psforever/objects/serverobject/locks/IFFLocks.scala +++ b/src/main/scala/net/psforever/objects/serverobject/locks/IFFLocks.scala @@ -1,8 +1,7 @@ // Copyright (c) 2020 PSForever package net.psforever.objects.serverobject.locks -import net.psforever.services.local.support.HackClearActor -import net.psforever.services.local.HackClearMessage +import net.psforever.services.local.support.{HackClearActor, HackClearEnvelope} object IFFLocks { @@ -14,6 +13,6 @@ object IFFLocks { */ def FinishResecuringIFFLock(lock: IFFLock)(): Unit = { val zone = lock.Zone - lock.Zone.LocalEvents ! HackClearMessage(HackClearActor.ObjectIsResecured(lock)) + lock.Zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(lock)) } } diff --git a/src/main/scala/net/psforever/objects/serverobject/shuttle/OrbitalShuttlePadControl.scala b/src/main/scala/net/psforever/objects/serverobject/shuttle/OrbitalShuttlePadControl.scala index 1814f3ec4..9f2002da2 100644 --- a/src/main/scala/net/psforever/objects/serverobject/shuttle/OrbitalShuttlePadControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/shuttle/OrbitalShuttlePadControl.scala @@ -11,8 +11,9 @@ import net.psforever.packet.game.ChatMsg import net.psforever.services.avatar.AvatarServiceMessage import net.psforever.services.base.message.SendResponse import net.psforever.services.base.support.SupportActor -import net.psforever.services.local.{DoorMessage, LocalAction} +import net.psforever.services.local.LocalAction import net.psforever.services.hart.{HartTimer, HartTimerActions} +import net.psforever.services.local.support.DoorMessage import net.psforever.services.{Service, ServiceManager} import net.psforever.types.ChatMessageType diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala index 2628d5d2f..f1b95da07 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala @@ -7,8 +7,7 @@ import net.psforever.objects.sourcing.AmenitySource import net.psforever.objects.vital.interaction.DamageResult import net.psforever.packet.game.HackState1 import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} -import net.psforever.services.local.HackClearMessage -import net.psforever.services.local.support.HackClearActor +import net.psforever.services.local.support.{HackClearActor, HackClearEnvelope} import org.log4s.Logger import scala.annotation.unused @@ -149,7 +148,7 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit) tryAutoRepair() if (term.HackedBy.nonEmpty) { val zone = term.Zone - zone.LocalEvents ! HackClearMessage(HackClearActor.ObjectIsResecured(term)) + zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(term)) } super.DestructionAwareness(target, cause) } @@ -226,7 +225,7 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit) //clear hack state if (term.HackedBy.nonEmpty) { val zone = term.Zone - zone.LocalEvents ! HackClearMessage(HackClearActor.ObjectIsResecured(term)) + zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(term)) } } diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala index 46d124b81..9b289caad 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala @@ -12,8 +12,7 @@ import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableA import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl} import net.psforever.objects.vital.interaction.DamageResult import net.psforever.packet.game.HackState1 -import net.psforever.services.local.support.HackClearActor -import net.psforever.services.local.HackClearMessage +import net.psforever.services.local.support.{HackClearActor, HackClearEnvelope} /** * An `Actor` that handles messages being dispatched to a specific `Terminal`. @@ -100,7 +99,7 @@ class TerminalControl(term: Terminal) tryAutoRepair() if (term.HackedBy.nonEmpty) { val zone = term.Zone - zone.LocalEvents ! HackClearMessage(HackClearActor.ObjectIsResecured(term)) + zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(term)) } super.DestructionAwareness(target, cause) } @@ -122,7 +121,7 @@ class TerminalControl(term: Terminal) //clear hack state if (term.HackedBy.nonEmpty) { val zone = term.Zone - zone.LocalEvents ! HackClearMessage(HackClearActor.ObjectIsResecured(term)) + zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(term)) } } diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/capture/CaptureTerminals.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/capture/CaptureTerminals.scala index b1a233b72..2fcb492d5 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/capture/CaptureTerminals.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/capture/CaptureTerminals.scala @@ -6,9 +6,10 @@ import net.psforever.objects.serverobject.hackable.GenericHackables import net.psforever.objects.serverobject.structures.{Building, StructureType, WarpGate} import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} import net.psforever.objects.sourcing.PlayerSource -import net.psforever.services.local.{CaptureMessage, LocalAction, LocalServiceMessage} -import net.psforever.types.PlanetSideEmpire +import net.psforever.services.local.{LocalAction, LocalServiceMessage} import net.psforever.services.local.support.HackCaptureActor +import net.psforever.services.local.support.CaptureEnvelope +import net.psforever.types.PlanetSideEmpire import scala.concurrent.duration._ import scala.util.{Failure, Success} @@ -47,10 +48,10 @@ object CaptureTerminals { ) if (isResecured) { // Resecure the CC - events ! CaptureMessage(HackCaptureActor.ResecureCaptureTerminal(target, zone, PlayerSource(hackingPlayer))) + events ! CaptureEnvelope(HackCaptureActor.ResecureCaptureTerminal(target, zone, PlayerSource(hackingPlayer))) } else { // Start the CC hack timer - events ! CaptureMessage(HackCaptureActor.StartCaptureTerminalHack(target, zone, 0, 8L)) + events ! CaptureEnvelope(HackCaptureActor.StartCaptureTerminalHack(target, zone, 0, 8L)) } case Failure(_) => log.warn(s"Hack message failed on target guid: ${target.GUID}") diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/WeaponTurrets.scala b/src/main/scala/net/psforever/objects/serverobject/turret/WeaponTurrets.scala index 75c13a4d2..c0aedc2e1 100644 --- a/src/main/scala/net/psforever/objects/serverobject/turret/WeaponTurrets.scala +++ b/src/main/scala/net/psforever/objects/serverobject/turret/WeaponTurrets.scala @@ -8,8 +8,8 @@ import net.psforever.packet.game.{HackMessage, HackState, HackState1, HackState7 import net.psforever.services.avatar.AvatarServiceMessage import net.psforever.services.base.message.{SendResponse, SetEmpire} import net.psforever.services.local.{LocalAction, LocalServiceMessage} -import net.psforever.services.vehicle.support.TurretUpgrader -import net.psforever.services.vehicle.{TurretMessage, VehicleAction, VehicleServiceMessage} +import net.psforever.services.vehicle.support.{TurretEnvelope, TurretUpgrader} +import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.types.PlanetSideGUID object WeaponTurrets { @@ -53,8 +53,8 @@ object WeaponTurrets { log.info(s"Manned wall turret weapon being converted to $upgrade") val zone = target.Zone val events = zone.VehicleEvents - events ! TurretMessage(TurretUpgrader.ClearSpecific(List(target), zone)) - events ! TurretMessage(TurretUpgrader.AddTask(target, zone, upgrade)) + events ! TurretEnvelope(TurretUpgrader.ClearSpecific(List(target), zone)) + events ! TurretEnvelope(TurretUpgrader.AddTask(target, zone, upgrade)) } /** diff --git a/src/main/scala/net/psforever/objects/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala index 06a03638d..1c1336090 100644 --- a/src/main/scala/net/psforever/objects/zones/Zone.scala +++ b/src/main/scala/net/psforever/objects/zones/Zone.scala @@ -1571,6 +1571,17 @@ object Zone { } } + def AmsSpawnPoints(zone: Zone): List[SpawnTube] = { + import net.psforever.objects.vehicles.UtilityType + import net.psforever.objects.GlobalDefinitions + zone.Vehicles + .filter(veh => + veh.Health > 0 && veh.Definition == GlobalDefinitions.ams && veh.DeploymentState == DriveState.Deployed + ) + .flatMap(veh => veh.Utilities.values.filter(util => util.UtilType == UtilityType.ams_respawn_tube)) + .map(util => util().asInstanceOf[SpawnTube]) + } + object Setup { /* zone setup code */ diff --git a/src/main/scala/net/psforever/objects/zones/ZoneGroundActor.scala b/src/main/scala/net/psforever/objects/zones/ZoneGroundActor.scala index 0d362a661..84f44a3d0 100644 --- a/src/main/scala/net/psforever/objects/zones/ZoneGroundActor.scala +++ b/src/main/scala/net/psforever/objects/zones/ZoneGroundActor.scala @@ -4,8 +4,9 @@ package net.psforever.objects.zones import akka.actor.Actor import net.psforever.actors.zone.ZoneActor import net.psforever.objects.equipment.Equipment +import net.psforever.services.avatar.support.{DropItemEnvelope, PickupItemEnvelope} import net.psforever.types.PlanetSideGUID -import net.psforever.services.avatar.{AvatarAction, DropItemMessage, PickupItemMessage} +import net.psforever.services.avatar.AvatarAction import scala.annotation.tailrec import scala.collection.mutable.ListBuffer @@ -30,7 +31,7 @@ class ZoneGroundActor(zone: Zone, equipmentOnGround: ListBuffer[Equipment]) exte equipmentOnGround += item item.Position = pos item.Orientation = orient - zone.AvatarEvents ! DropItemMessage(zone.id, AvatarAction.DropItem(item), zone) + zone.AvatarEvents ! DropItemEnvelope(zone.id, AvatarAction.DropItem(item), zone) zone.actor ! ZoneActor.AddToBlockMap(item, pos) Zone.Ground.ItemOnGround(item, pos, orient) }) @@ -38,7 +39,7 @@ class ZoneGroundActor(zone: Zone, equipmentOnGround: ListBuffer[Equipment]) exte case Zone.Ground.PickupItem(item_guid) => sender() ! (FindItemOnGround(item_guid) match { case Some(item) => - zone.AvatarEvents ! PickupItemMessage(zone.id, AvatarAction.PickupItem(item, 0), zone) + zone.AvatarEvents ! PickupItemEnvelope(zone.id, AvatarAction.PickupItem(item, 0), zone) zone.actor ! ZoneActor.RemoveFromBlockMap(item) Zone.Ground.ItemInHand(item) case None => @@ -50,7 +51,7 @@ class ZoneGroundActor(zone: Zone, equipmentOnGround: ListBuffer[Equipment]) exte FindItemOnGround(item_guid) match { case Some(item) => zone.actor ! ZoneActor.RemoveFromBlockMap(item) - zone.AvatarEvents ! PickupItemMessage(zone.id, AvatarAction.PickupItem(item, 0), zone) + zone.AvatarEvents ! PickupItemEnvelope(zone.id, AvatarAction.PickupItem(item, 0), zone) case None => ; } diff --git a/src/main/scala/net/psforever/services/Service.scala b/src/main/scala/net/psforever/services/Service.scala index 19ae064aa..a8b9c9bbe 100644 --- a/src/main/scala/net/psforever/services/Service.scala +++ b/src/main/scala/net/psforever/services/Service.scala @@ -1,6 +1,7 @@ // Copyright (c) 2017 PSForever package net.psforever.services +import akka.actor.ActorRef import net.psforever.types.PlanetSideGUID object Service { @@ -8,7 +9,15 @@ object Service { final case class Startup() - final case class Join(channel: String) + final case class Join(channel: String, sendJoinConfirmation: Boolean) + + object Join { + def apply(channel: String): Join = Join(channel, sendJoinConfirmation = false) + } + + final case class JoinConfirmation(eventSystem: ActorRef, channel: String) + final case class Leave(channel: Option[String] = None) + final case class LeaveAll() } diff --git a/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala b/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala index a8e7d530c..d7415c59a 100644 --- a/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala +++ b/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala @@ -1,12 +1,9 @@ // Copyright (c) 2017-2026 PSForever package net.psforever.services.avatar -import net.psforever.objects.zones.Zone -import net.psforever.services.{RemoverActor, Service} -import net.psforever.services.avatar.AvatarAction.{DropItem, PickupItem, Release} +import net.psforever.services.Service import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.services.base.message.EventMessage -import net.psforever.services.base.{GenericSupportEnvelope, GenericSupportEnvelopeOnly} import net.psforever.types.PlanetSideGUID object AvatarServiceMessage { @@ -16,69 +13,3 @@ object AvatarServiceMessage { def apply(channel: String, filter: PlanetSideGUID, msg: EventMessage): MessageEnvelope = MessageEnvelope(channel, filter, msg) } - -final case class ReleaseMessage( - originalChannel: String, - filter: PlanetSideGUID, - msg: Release - ) - extends GenericSupportEnvelope { - def supportLabel: String = "undertaker" - def supportMessage: Any = { - val Release(player, zone, time) = msg - RemoverActor.AddTask(player, zone, time) - } -} - -object ReleaseMessage { - def apply(channel: String, actionMessage: Release): ReleaseMessage = - ReleaseMessage(channel, Service.defaultPlayerGUID, actionMessage) -} - -final case class PickupItemMessage( - originalChannel: String, - filter: PlanetSideGUID, - msg: PickupItem, - zone: Zone - ) - extends GenericSupportEnvelope { - def supportLabel: String = "janitor" - def supportMessage: Any = { - val PickupItem(item, _) = msg - RemoverActor.ClearSpecific(List(item), zone) - } -} - -object PickupItemMessage { - def apply(channel: String, actionMessage: PickupItem, zone: Zone): PickupItemMessage = - PickupItemMessage(channel, Service.defaultPlayerGUID, actionMessage, zone) -} - -final case class DropItemMessage( - originalChannel: String, - filter: PlanetSideGUID, - msg: DropItem, - zone: Zone - ) - extends GenericSupportEnvelope { - def supportLabel: String = "janitor" - def supportMessage: Any = { - val DropItem(item) = msg - RemoverActor.AddTask(item, zone) - } -} - -object DropItemMessage { - def apply(channel: String, actionMessage: DropItem, zone: Zone): DropItemMessage = - DropItemMessage(channel, Service.defaultPlayerGUID, actionMessage, zone) -} - -final case class CorpseEnvelope(supportMessage: Any) - extends GenericSupportEnvelopeOnly { - def supportLabel: String = "undertaker" -} - -final case class GroundEnvelope(supportMessage: Any) - extends GenericSupportEnvelopeOnly { - def supportLabel: String = "janitor" -} diff --git a/src/main/scala/net/psforever/services/avatar/support/CorpseRemovalActor.scala b/src/main/scala/net/psforever/services/avatar/support/CorpseRemovalActor.scala index e4d53dcb1..7a1f16e00 100644 --- a/src/main/scala/net/psforever/services/avatar/support/CorpseRemovalActor.scala +++ b/src/main/scala/net/psforever/services/avatar/support/CorpseRemovalActor.scala @@ -3,13 +3,38 @@ package net.psforever.services.avatar.support import net.psforever.objects.guid.{GUIDTask, TaskBundle} import net.psforever.objects.Player -import net.psforever.types.ExoSuitType -import net.psforever.services.RemoverActor +import net.psforever.types.{ExoSuitType, PlanetSideGUID} +import net.psforever.services.{RemoverActor, Service} +import net.psforever.services.avatar.AvatarAction.Release import net.psforever.services.avatar.AvatarServiceMessage +import net.psforever.services.base.{GenericSupportEnvelope, GenericSupportEnvelopeOnly} import net.psforever.services.base.message.ObjectDelete import scala.concurrent.duration._ +final case class ReleaseEnvelope( + originalChannel: String, + filter: PlanetSideGUID, + msg: Release + ) + extends GenericSupportEnvelope { + def supportLabel: String = "undertaker" + def supportMessage: Any = { + val Release(player, zone, time) = msg + RemoverActor.AddTask(player, zone, time) + } +} + +object ReleaseEnvelope { + def apply(channel: String, actionMessage: Release): ReleaseEnvelope = + ReleaseEnvelope(channel, Service.defaultPlayerGUID, actionMessage) +} + +final case class CorpseEnvelope(supportMessage: Any) + extends GenericSupportEnvelopeOnly { + def supportLabel: String = "undertaker" +} + class CorpseRemovalActor extends RemoverActor() { final val FirstStandardDuration: FiniteDuration = 1 minute diff --git a/src/main/scala/net/psforever/services/avatar/support/DroppedItemRemover.scala b/src/main/scala/net/psforever/services/avatar/support/DroppedItemRemover.scala index dbdf5d252..8224c53af 100644 --- a/src/main/scala/net/psforever/services/avatar/support/DroppedItemRemover.scala +++ b/src/main/scala/net/psforever/services/avatar/support/DroppedItemRemover.scala @@ -3,12 +3,59 @@ package net.psforever.services.avatar.support import net.psforever.objects.equipment.Equipment import net.psforever.objects.guid.{GUIDTask, TaskBundle} -import net.psforever.services.RemoverActor +import net.psforever.objects.zones.Zone +import net.psforever.services.avatar.AvatarAction.{DropItem, PickupItem} +import net.psforever.services.{RemoverActor, Service} import net.psforever.services.avatar.AvatarServiceMessage +import net.psforever.services.base.{GenericSupportEnvelope, GenericSupportEnvelopeOnly} import net.psforever.services.base.message.ObjectDelete +import net.psforever.types.PlanetSideGUID import scala.concurrent.duration._ +final case class PickupItemEnvelope( + originalChannel: String, + filter: PlanetSideGUID, + msg: PickupItem, + zone: Zone + ) + extends GenericSupportEnvelope { + def supportLabel: String = "janitor" + def supportMessage: Any = { + val PickupItem(item, _) = msg + RemoverActor.ClearSpecific(List(item), zone) + } +} + +object PickupItemEnvelope { + def apply(channel: String, actionMessage: PickupItem, zone: Zone): PickupItemEnvelope = + PickupItemEnvelope(channel, Service.defaultPlayerGUID, actionMessage, zone) +} + +final case class DropItemEnvelope( + originalChannel: String, + filter: PlanetSideGUID, + msg: DropItem, + zone: Zone + ) + extends GenericSupportEnvelope { + def supportLabel: String = "janitor" + def supportMessage: Any = { + val DropItem(item) = msg + RemoverActor.AddTask(item, zone) + } +} + +object DropItemEnvelope { + def apply(channel: String, actionMessage: DropItem, zone: Zone): DropItemEnvelope = + DropItemEnvelope(channel, Service.defaultPlayerGUID, actionMessage, zone) +} + +final case class GroundEnvelope(supportMessage: Any) + extends GenericSupportEnvelopeOnly { + def supportLabel: String = "janitor" +} + class DroppedItemRemover extends RemoverActor() { final val FirstStandardDuration: FiniteDuration = 3 minutes diff --git a/src/main/scala/net/psforever/services/base/GenericEventService.scala b/src/main/scala/net/psforever/services/base/GenericEventService.scala index d294539ed..202f08d7c 100644 --- a/src/main/scala/net/psforever/services/base/GenericEventService.scala +++ b/src/main/scala/net/psforever/services/base/GenericEventService.scala @@ -7,8 +7,6 @@ import net.psforever.services.base.bus.GenericEventBus import net.psforever.services.base.envelope.{GenericMessageEnvelope, GenericResponseEnvelope} import org.log4s.Logger -import scala.annotation.unused - trait EventSystemStamp abstract class GenericEventService(stamp: EventSystemStamp) @@ -18,7 +16,13 @@ abstract class GenericEventService(stamp: EventSystemStamp) protected val eventBus: GenericEventBus = new GenericEventBus private def commonJoinBehavior: Receive = { - case Service.Join(channel) => + case Service.Join(channel, true) => + val path = formatChannel(channel) + val who = sender() + eventBus.subscribe(who, path) + who ! Service.JoinConfirmation(self, channel) + + case Service.Join(channel, _) => val path = formatChannel(channel) val who = sender() eventBus.subscribe(who, path) @@ -53,7 +57,7 @@ abstract class GenericEventService(stamp: EventSystemStamp) eventBus.publish(composeResponseEnvelope(msg)) } - protected def composeResponseEnvelope(@unused msg: GenericMessageEnvelope): GenericResponseEnvelope = { + protected def composeResponseEnvelope(msg: GenericMessageEnvelope): GenericResponseEnvelope = { msg.response(stamp, formatChannel) } diff --git a/src/main/scala/net/psforever/services/base/GenericEventServiceWithCacheAndSupport.scala b/src/main/scala/net/psforever/services/base/GenericEventServiceWithCacheAndSupport.scala index abdb1b9f4..ce73b3d00 100644 --- a/src/main/scala/net/psforever/services/base/GenericEventServiceWithCacheAndSupport.scala +++ b/src/main/scala/net/psforever/services/base/GenericEventServiceWithCacheAndSupport.scala @@ -1,17 +1,21 @@ // Copyright (c) 2026 PSForever package net.psforever.services.base +import akka.actor.Cancellable +import net.psforever.objects.Default import net.psforever.services.Service -import net.psforever.services.base.envelope.{GenericMessageEnvelope, MessageEnvelope, MessageTransformationBehavior} +import net.psforever.services.base.envelope.{GenericMessageEnvelope, GenericResponseEnvelope, MessageEnvelope, MessageTransformationBehavior} import net.psforever.services.base.message.EventMessage import net.psforever.types.PlanetSideGUID import scala.collection.concurrent.{Map => CMap} import scala.jdk.CollectionConverters._ import java.util.concurrent.ConcurrentHashMap +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.duration.DurationInt /* -Adapted from the rating limiting code in https://github.com/Pinapse/giant with permission +Adapted from the rating limiting code in PSForever fork https://github.com/Pinapse/giant with permission */ trait CachedGenericEventMessageEnvelope @@ -19,26 +23,42 @@ trait CachedGenericEventMessageEnvelope def guid: PlanetSideGUID } -final case class CachedMessage( - guid: PlanetSideGUID, - originalChannel: String, - override val filter: PlanetSideGUID, - override val msg: EventMessage - ) extends CachedGenericEventMessageEnvelope { +final case class CachedEnvelope( + guid: PlanetSideGUID, + originalChannel: String, + override val filter: PlanetSideGUID, + override val msg: EventMessage + ) extends CachedGenericEventMessageEnvelope { assert(guid != Service.defaultPlayerGUID, "can not cache message under default GUID") } -object CachedMessage { +object CachedEnvelope { def apply(channel: String, filter: PlanetSideGUID, msg: EventMessage): GenericMessageEnvelope = { if (filter == Service.defaultPlayerGUID) { + org.log4s.getLogger("CachedEnvelope").warn("(1) cached message envelope downgraded to normal message envelope") MessageEnvelope(channel, filter, msg) } else { - CachedMessage(filter, channel, filter, msg) + CachedEnvelope(filter, channel, filter, msg) + } + } + + def apply(guid: PlanetSideGUID, channel: String, msg: EventMessage): GenericMessageEnvelope = { + if (guid == Service.defaultPlayerGUID) { + org.log4s.getLogger("CachedEnvelope").warn("(2) cached message envelope downgraded to normal message envelope") + MessageEnvelope(channel, guid, msg) + } else { + CachedEnvelope(guid, channel, guid, msg) } } } -private case object FlushCachedMessages +private case object FlushCachedMessages extends GenericMessageEnvelope { + def originalChannel: String = "" + def msg: EventMessage = NoMessage + def response(stamp: EventSystemStamp, sendToChannel: String => String): GenericResponseEnvelope = NoResponseEnvelope + def channel: String = "" + def filter: PlanetSideGUID = Service.defaultPlayerGUID +} abstract class GenericEventServiceWithCacheAndSupport ( @@ -48,6 +68,8 @@ abstract class GenericEventServiceWithCacheAndSupport private val flushCacheWait: Long = 50 //milliseconds private var hasCachedMessages: Boolean = false private var nextTimeToFlushCache: Long = 0L + private var emergencyFlush: Cancellable = Default.Cancellable + private val cache: CMap[String, CMap[String, CMap[PlanetSideGUID, GenericMessageEnvelope]]] = new ConcurrentHashMap[String, CMap[String, CMap[PlanetSideGUID, GenericMessageEnvelope]]]().asScala @@ -60,6 +82,7 @@ abstract class GenericEventServiceWithCacheAndSupport if (!hasCachedMessages) { hasCachedMessages = true nextTimeToFlushCache = System.currentTimeMillis() + flushCacheWait + emergencyFlush = context.system.scheduler.scheduleOnce(55 milliseconds, self, FlushCachedMessages) } } @@ -88,12 +111,16 @@ abstract class GenericEventServiceWithCacheAndSupport } } hasCachedMessages = false + emergencyFlush.cancel() + emergencyFlush = Default.Cancellable } override protected def handleMessage(event: GenericMessageEnvelope): Unit = { event match { case envelope: CachedGenericEventMessageEnvelope => pushToCache(envelope) + case FlushCachedMessages => + tryFlushCache() case _ => super.handleMessage(event) } diff --git a/src/main/scala/net/psforever/services/base/GenericEventServiceWithSupport.scala b/src/main/scala/net/psforever/services/base/GenericEventServiceWithSupport.scala index 0138898f5..c875f1d82 100644 --- a/src/main/scala/net/psforever/services/base/GenericEventServiceWithSupport.scala +++ b/src/main/scala/net/psforever/services/base/GenericEventServiceWithSupport.scala @@ -23,7 +23,7 @@ trait EventServiceSupport { def constructor(@unused context: ActorContext): ActorRef } -sealed trait GenericMessageToSupport { +trait GenericMessageToSupport { def supportLabel: String def supportMessage: Any } diff --git a/src/main/scala/net/psforever/services/local/LocalAction.scala b/src/main/scala/net/psforever/services/local/LocalAction.scala index 092b71776..f10d33ea6 100644 --- a/src/main/scala/net/psforever/services/local/LocalAction.scala +++ b/src/main/scala/net/psforever/services/local/LocalAction.scala @@ -9,6 +9,7 @@ import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal} import net.psforever.objects.vehicles.Utility import net.psforever.objects.zones.Zone import net.psforever.packet.game.{DeployableInfo, DeploymentAction, GenericAction, HackState7, ObjectCreateMessage, TriggeredEffect, TriggeredEffectLocation, TriggeredSound} +import net.psforever.services.Service import net.psforever.services.base.message.{EventMessage, EventResponse, SelfRespondingEvent, SendResponse} import net.psforever.services.hart.HartTimer.OrbitalShuttleEvent import net.psforever.types.{PlanetSideGUID, Vector3} @@ -126,7 +127,7 @@ object LocalAction { orient: Vector3 ) extends EventMessage { def response(): EventResponse = { - TriggerEffectAtLocation(PlanetSideGUID(0), effect, None, Some(TriggeredEffectLocation(pos, orient))) + TriggerEffectAtLocation(Service.defaultPlayerGUID, effect, None, Some(TriggeredEffectLocation(pos, orient))) } } diff --git a/src/main/scala/net/psforever/services/local/LocalServiceMessage.scala b/src/main/scala/net/psforever/services/local/LocalServiceMessage.scala index ed9d3a094..005610369 100644 --- a/src/main/scala/net/psforever/services/local/LocalServiceMessage.scala +++ b/src/main/scala/net/psforever/services/local/LocalServiceMessage.scala @@ -4,9 +4,6 @@ package net.psforever.services.local import net.psforever.services.Service import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.services.base.message.EventMessage -import net.psforever.services.base.{GenericSupportEnvelope, GenericSupportEnvelopeOnly} -import net.psforever.services.local.LocalAction.{IsADoorMessage, IsAHackMessage} -import net.psforever.services.local.support.CaptureFlagManager import net.psforever.types.PlanetSideGUID object LocalServiceMessage { @@ -16,36 +13,3 @@ object LocalServiceMessage { def apply(channel: String, filter: PlanetSideGUID, msg: EventMessage): MessageEnvelope = MessageEnvelope(channel, filter, msg) } - -final case class DoorMessage( - originalChannel: String, - msg: IsADoorMessage, - supportMessage: Any - ) extends GenericSupportEnvelope { - def filter: PlanetSideGUID = Service.defaultPlayerGUID - def supportLabel: String = "doorCloser" -} - -final case class HackEntityMessage( - originalChannel: String, - filter: PlanetSideGUID, - msg: IsAHackMessage, - supportMessage: Any - ) extends GenericSupportEnvelope { - def supportLabel: String = "hackClearer" -} - -final case class HackClearMessage(supportMessage: Any) - extends GenericSupportEnvelopeOnly { - def supportLabel: String = "hackClearer" -} - -final case class CaptureMessage(supportMessage: Any) - extends GenericSupportEnvelopeOnly { - def supportLabel: String = "hackCapturer" -} - -final case class FlagMessage(supportMessage: CaptureFlagManager.Command) - extends GenericSupportEnvelopeOnly { - def supportLabel: String = "captureFlagManager" -} diff --git a/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala b/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala index 4a3ea889b..d08d1e207 100644 --- a/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala +++ b/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala @@ -15,13 +15,19 @@ import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.packet.game._ import net.psforever.services.ServiceManager import net.psforever.services.ServiceManager.{Lookup, LookupResult} +import net.psforever.services.base.GenericSupportEnvelopeOnly import net.psforever.services.base.message.{GenericObjectAction, SendResponse} import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage} -import net.psforever.services.local.{CaptureMessage, LocalAction, LocalServiceMessage} +import net.psforever.services.local.{LocalAction, LocalServiceMessage} import net.psforever.types.{ChatMessageType, PlanetSideEmpire, PlanetSideGUID, Vector3} import scala.concurrent.duration.DurationInt +final case class FlagEnvelope(supportMessage: CaptureFlagManager.Command) + extends GenericSupportEnvelopeOnly { + def supportLabel: String = "captureFlagManager" +} + /** * Responsible for handling capture flag related lifecycles */ @@ -303,7 +309,7 @@ object CaptureFlagManager { if LoseFlagViolentlyToEnvironment(target, Set(EnvironmentAttribute.Water, EnvironmentAttribute.Lava, EnvironmentAttribute.Death)) /*|| LoseFlagViolentlyToWarpGateEnvelope(zone, target)*/ => flag.Destroyed = true - zone.LocalEvents ! CaptureMessage(HackCaptureActor.FlagLost(flag)) + zone.LocalEvents ! CaptureEnvelope(HackCaptureActor.FlagLost(flag)) true } .getOrElse(false) diff --git a/src/main/scala/net/psforever/services/local/support/DoorCloseActor.scala b/src/main/scala/net/psforever/services/local/support/DoorCloseActor.scala index ef959035e..a8ad7e9ae 100644 --- a/src/main/scala/net/psforever/services/local/support/DoorCloseActor.scala +++ b/src/main/scala/net/psforever/services/local/support/DoorCloseActor.scala @@ -5,12 +5,24 @@ import akka.actor.{Actor, Cancellable} import net.psforever.objects.{Default, Doors} import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.zones.Zone +import net.psforever.services.Service +import net.psforever.services.base.GenericSupportEnvelope +import net.psforever.services.local.LocalAction.IsADoorMessage import net.psforever.services.local.{LocalAction, LocalServiceMessage} import net.psforever.types.PlanetSideGUID import scala.annotation.tailrec import scala.concurrent.duration._ +final case class DoorMessage( + originalChannel: String, + msg: IsADoorMessage, + supportMessage: Any + ) extends GenericSupportEnvelope { + def filter: PlanetSideGUID = Service.defaultPlayerGUID + def supportLabel: String = "doorCloser" +} + /** * Close an opened door after a certain amount of time has passed. * This `Actor` is intended to sit on top of the event system that handles broadcast messaging regarding doors opening. 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 762eba15e..b6f825950 100644 --- a/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala +++ b/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala @@ -13,9 +13,10 @@ import net.psforever.objects.Default import net.psforever.objects.serverobject.structures.participation.MajorFacilityHackParticipation import net.psforever.packet.game.{ChatMsg, GenericAction, HackState7, PlanetsideAttributeEnum} import net.psforever.objects.sourcing.PlayerSource +import net.psforever.services.base.GenericSupportEnvelopeOnly import net.psforever.services.base.message.PlanetsideAttribute import net.psforever.services.local.support.HackCaptureActor.GetHackingFaction -import net.psforever.services.local.{FlagMessage, LocalAction, LocalServiceMessage} +import net.psforever.services.local.{LocalAction, LocalServiceMessage} import net.psforever.types.{ChatMessageType, PlanetSideEmpire, PlanetSideGUID} import java.util.concurrent.{Executors, TimeUnit} @@ -23,6 +24,11 @@ import scala.collection.Seq import scala.concurrent.duration.{FiniteDuration, _} import scala.util.Random +final case class CaptureEnvelope(supportMessage: Any) + extends GenericSupportEnvelopeOnly { + def supportLabel: String = "hackCapturer" +} + /** * Responsible for handling the aspects related to hacking control consoles and capturing bases. */ @@ -168,7 +174,7 @@ class HackCaptureActor extends Actor { if (found.isEmpty) { log.warn(s"FlagLost: flag data does not match to an entry in the hacked objects list") } - owner.Zone.LocalEvents ! FlagMessage(CaptureFlagManager.Lost(flag, CaptureFlagLostReasonEnum.FlagLost)) + owner.Zone.LocalEvents ! FlagEnvelope(CaptureFlagManager.Lost(flag, CaptureFlagLostReasonEnum.FlagLost)) case _ => () } @@ -190,7 +196,7 @@ class HackCaptureActor extends Actor { true case Some((owner, Some(flag), Some(neighbours))) if neighbours.nonEmpty && hackingFaction != flag.Faction => log.info(s"$hackingFaction is overriding the ongoing LLU hack of facility ${owner.Name} by ${flag.Faction}") - terminal.Zone.LocalEvents ! FlagMessage(CaptureFlagManager.Lost(flag, CaptureFlagLostReasonEnum.Ended)) + terminal.Zone.LocalEvents ! FlagEnvelope(CaptureFlagManager.Lost(flag, CaptureFlagLostReasonEnum.Ended)) NotifyHackStateChange(terminal, isResecured = false) RestartTimer() spawnCaptureFlag(neighbours, terminal, hackingFaction) @@ -204,7 +210,7 @@ class HackCaptureActor extends Actor { case Some((owner, Some(flag), _)) => log.warn(s"TrySpawnCaptureFlag: couldn't find any neighbouring $hackingFaction facilities of ${owner.Name} for LLU hack") owner.GetFlagSocket.foreach { _.clearOldFlagData() } - terminal.Zone.LocalEvents ! FlagMessage(CaptureFlagManager.Lost(flag, CaptureFlagLostReasonEnum.Ended)) + terminal.Zone.LocalEvents ! FlagEnvelope(CaptureFlagManager.Lost(flag, CaptureFlagLostReasonEnum.Ended)) false case _ => log.error(s"TrySpawnCaptureFlag: expecting a terminal ${terminal.GUID.guid} with the ctf owning facility") @@ -220,7 +226,7 @@ class HackCaptureActor extends Actor { // Find a random neighbouring base matching the hacking faction val targetBase = neighbours.toVector((new Random).nextInt(neighbours.size)) // Request LLU is created by CaptureFlagActor via LocalService - terminal.Zone.LocalEvents ! FlagMessage(CaptureFlagManager.SpawnCaptureFlag(terminal, targetBase, hackingFaction)) + terminal.Zone.LocalEvents ! FlagEnvelope(CaptureFlagManager.SpawnCaptureFlag(terminal, targetBase, hackingFaction)) } private def NotifyHackStateChange( diff --git a/src/main/scala/net/psforever/services/local/support/HackClearActor.scala b/src/main/scala/net/psforever/services/local/support/HackClearActor.scala index 396280233..5f6d964f6 100644 --- a/src/main/scala/net/psforever/services/local/support/HackClearActor.scala +++ b/src/main/scala/net/psforever/services/local/support/HackClearActor.scala @@ -8,13 +8,29 @@ import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} import net.psforever.objects.zones.Zone import net.psforever.packet.game.HackState7 +import net.psforever.services.base.{GenericSupportEnvelope, GenericSupportEnvelopeOnly} import net.psforever.services.base.message.GenericObjectAction +import net.psforever.services.local.LocalAction.IsAHackMessage import net.psforever.services.local.{LocalAction, LocalServiceMessage} import net.psforever.types.PlanetSideGUID import scala.annotation.tailrec import scala.concurrent.duration._ +final case class HackEntityEnvelope( + originalChannel: String, + filter: PlanetSideGUID, + msg: IsAHackMessage, + supportMessage: Any + ) extends GenericSupportEnvelope { + def supportLabel: String = "hackClearer" +} + +final case class HackClearEnvelope(supportMessage: Any) + extends GenericSupportEnvelopeOnly { + def supportLabel: String = "hackClearer" +} + /** * Restore original functionality to an object that has been hacked after a certain amount of time has passed. * This `Actor` is intended to sit on top of the event system that handles broadcast messaging regarding hacking events. diff --git a/src/main/scala/net/psforever/services/vehicle/VehicleAction.scala b/src/main/scala/net/psforever/services/vehicle/VehicleAction.scala index 6364e2cf5..dad025f86 100644 --- a/src/main/scala/net/psforever/services/vehicle/VehicleAction.scala +++ b/src/main/scala/net/psforever/services/vehicle/VehicleAction.scala @@ -114,13 +114,13 @@ object VehicleAction { final case class UpdateAmsSpawnPoint(zone: Zone) extends EventMessage { override def response(): EventResponse = { - VehicleAction.UpdateAmsSpawnList(AmsSpawnPoints(zone)) + VehicleAction.UpdateAmsSpawnList(Zone.AmsSpawnPoints(zone)) } } final case class AMSDeploymentChange(zone: Zone) extends EventMessage { override def response(): EventResponse = { - VehicleAction.UpdateAmsSpawnList(AmsSpawnPoints(zone)) + VehicleAction.UpdateAmsSpawnList(Zone.AmsSpawnPoints(zone)) } } @@ -140,16 +140,4 @@ object VehicleAction { old_inventory: List[(Equipment, PlanetSideGUID)], new_inventory: List[InventoryItem] ) extends SelfRespondingEvent - - import net.psforever.objects.serverobject.tube.SpawnTube - private def AmsSpawnPoints(zone: Zone): List[SpawnTube] = { - import net.psforever.objects.vehicles.UtilityType - import net.psforever.objects.GlobalDefinitions - zone.Vehicles - .filter(veh => - veh.Health > 0 && veh.Definition == GlobalDefinitions.ams && veh.DeploymentState == DriveState.Deployed - ) - .flatMap(veh => veh.Utilities.values.filter(util => util.UtilType == UtilityType.ams_respawn_tube)) - .map(util => util().asInstanceOf[SpawnTube]) - } } diff --git a/src/main/scala/net/psforever/services/vehicle/VehicleServiceMessage.scala b/src/main/scala/net/psforever/services/vehicle/VehicleServiceMessage.scala index 212e44638..f6aae9a32 100644 --- a/src/main/scala/net/psforever/services/vehicle/VehicleServiceMessage.scala +++ b/src/main/scala/net/psforever/services/vehicle/VehicleServiceMessage.scala @@ -3,7 +3,6 @@ package net.psforever.services.vehicle import net.psforever.services.Service import net.psforever.services.base.message.EventMessage -import net.psforever.services.base.GenericSupportEnvelopeOnly import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.types.PlanetSideGUID @@ -14,8 +13,3 @@ object VehicleServiceMessage { def apply(channel: String, filter: PlanetSideGUID, msg: EventMessage): MessageEnvelope = MessageEnvelope(channel, filter, msg) } - -final case class TurretMessage(supportMessage: Any) - extends GenericSupportEnvelopeOnly { - def supportLabel: String = "turretUpgrader" -} diff --git a/src/main/scala/net/psforever/services/vehicle/support/TurretUpgrader.scala b/src/main/scala/net/psforever/services/vehicle/support/TurretUpgrader.scala index ac08220e4..1b81938c7 100644 --- a/src/main/scala/net/psforever/services/vehicle/support/TurretUpgrader.scala +++ b/src/main/scala/net/psforever/services/vehicle/support/TurretUpgrader.scala @@ -9,6 +9,7 @@ import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.turret.{FacilityTurret, TurretUpgrade, WeaponTurret} import net.psforever.objects.vehicles.MountedWeapons import net.psforever.objects.zones.Zone +import net.psforever.services.base.GenericSupportEnvelopeOnly import net.psforever.types.PlanetSideGUID import net.psforever.services.base.support.{SimilarityComparator, SupportActor, SupportActorCaseConversions} import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} @@ -17,6 +18,11 @@ import scala.concurrent.Future import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits.global +final case class TurretEnvelope(supportMessage: Any) + extends GenericSupportEnvelopeOnly { + def supportLabel: String = "turretUpgrader" +} + class TurretUpgrader extends SupportActor[TurretUpgrader.Entry] { var task: Cancellable = Default.Cancellable diff --git a/src/test/scala/objects/DamageableTest.scala b/src/test/scala/objects/DamageableTest.scala index fbf3b9f3a..8c3357fe4 100644 --- a/src/test/scala/objects/DamageableTest.scala +++ b/src/test/scala/objects/DamageableTest.scala @@ -24,7 +24,7 @@ import net.psforever.packet.game.DamageWithPositionMessage import net.psforever.types._ import net.psforever.services.avatar.AvatarAction import net.psforever.services.base.support.SupportActor -import net.psforever.services.vehicle.TurretMessage +import net.psforever.services.vehicle.support.TurretEnvelope import org.specs2.mutable.Specification import scala.concurrent.duration._ @@ -1095,12 +1095,12 @@ class DamageableWeaponTurretDestructionTest extends ActorTest { assert(false, s"DamageableWeaponTurretDestructionTest-4: ${msg12_4(2)}") } msg56.head match { - case TurretMessage(SupportActor.ClearSpecific(List(t), _)) if turret eq t => ; + case TurretEnvelope(SupportActor.ClearSpecific(List(t), _)) if turret eq t => ; case _ => assert(false, s"DamageableWeaponTurretDestructionTest-5: ${msg56.head}") } msg56(1) match { - case TurretMessage(TurretUpgrader.AddTask(t, _, TurretUpgrade.None, _)) if t eq turret => () + case TurretEnvelope(TurretUpgrader.AddTask(t, _, TurretUpgrade.None, _)) if t eq turret => () case _ => assert(false, s"DamageableWeaponTurretDestructionTest-6: ${msg56(1)}") } assert(turret.Health <= turret.Definition.DamageDestroysAt) diff --git a/src/test/scala/objects/DeploymentTest.scala b/src/test/scala/objects/DeploymentTest.scala index c0d4702ec..685479768 100644 --- a/src/test/scala/objects/DeploymentTest.scala +++ b/src/test/scala/objects/DeploymentTest.scala @@ -76,7 +76,7 @@ class DeploymentBehavior2Test extends ActorTest { case MessageEnvelope( "test", _, - VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deploying, 0, false, Vector3.Zero) + VehicleAction.DeployRequest(PlanetSideGUID(1), DriveState.Deploying, 0, false, Vector3.Zero) ) => () case _ => assert(false, "") } @@ -89,7 +89,7 @@ class DeploymentBehavior2Test extends ActorTest { case MessageEnvelope( "test", _, - VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deployed, 0, false, Vector3.Zero) + VehicleAction.DeployRequest(PlanetSideGUID(1), DriveState.Deployed, 0, false, Vector3.Zero) ) => () case _ => assert(false, "") } @@ -106,7 +106,7 @@ class DeploymentBehavior2Test extends ActorTest { case MessageEnvelope( "test", _, - VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Undeploying, 0, false, Vector3.Zero) + VehicleAction.DeployRequest(PlanetSideGUID(1), DriveState.Undeploying, 0, false, Vector3.Zero) ) => () case _ => assert(false, "") } @@ -119,7 +119,7 @@ class DeploymentBehavior2Test extends ActorTest { case MessageEnvelope( "test", _, - VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Mobile, 0, false, Vector3.Zero) + VehicleAction.DeployRequest(PlanetSideGUID(1), DriveState.Mobile, 0, false, Vector3.Zero) ) => () case _ => assert(false, "") } @@ -148,7 +148,7 @@ class DeploymentBehavior3Test extends ActorTest { case MessageEnvelope( "test", _, - VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deploying, 0, false, Vector3.Zero) + VehicleAction.DeployRequest(PlanetSideGUID(1), DriveState.Deploying, 0, false, Vector3.Zero) ) => () case _ => assert(false, "") } @@ -161,7 +161,7 @@ class DeploymentBehavior3Test extends ActorTest { case MessageEnvelope( "test", _, - VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deployed, 0, false, Vector3.Zero) + VehicleAction.DeployRequest(PlanetSideGUID(1), DriveState.Deployed, 0, false, Vector3.Zero) ) => () case _ => assert(false, "") } @@ -178,7 +178,7 @@ class DeploymentBehavior3Test extends ActorTest { case MessageEnvelope( "test", _, - VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Undeploying, 0, false, Vector3.Zero) + VehicleAction.DeployRequest(PlanetSideGUID(1), DriveState.Undeploying, 0, false, Vector3.Zero) ) => () case _ => assert(false, "") } @@ -191,7 +191,7 @@ class DeploymentBehavior3Test extends ActorTest { case MessageEnvelope( "test", _, - VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Mobile, 0, false, Vector3.Zero) + VehicleAction.DeployRequest(PlanetSideGUID(1), DriveState.Mobile, 0, false, Vector3.Zero) ) => () case _ => assert(false, "") } diff --git a/src/test/scala/objects/DoorTest.scala b/src/test/scala/objects/DoorTest.scala index 25010c2a7..2404e44ca 100644 --- a/src/test/scala/objects/DoorTest.scala +++ b/src/test/scala/objects/DoorTest.scala @@ -12,7 +12,8 @@ import net.psforever.objects.{Default, GlobalDefinitions, Player} import net.psforever.objects.serverobject.doors.{Door, DoorControl} import net.psforever.objects.serverobject.structures.{Building, StructureType} import net.psforever.objects.zones.{Zone, ZoneMap} -import net.psforever.services.local.{DoorMessage, LocalAction, LocalServiceResponse} +import net.psforever.services.local.support.DoorMessage +import net.psforever.services.local.{LocalAction, LocalServiceResponse} import net.psforever.types._ import org.specs2.mutable.Specification diff --git a/src/test/scala/objects/PlayerControlTest.scala b/src/test/scala/objects/PlayerControlTest.scala index 514b35846..eeee7f3b5 100644 --- a/src/test/scala/objects/PlayerControlTest.scala +++ b/src/test/scala/objects/PlayerControlTest.scala @@ -563,7 +563,7 @@ class PlayerControlDeathStandingTest extends ActorTest { ) assert( msg_avatar(1) match { - case MessageEnvelope("TestCharacter2", _, AvatarAction.Killed(PlanetSideGUID(2), _, None)) => true + case MessageEnvelope("TestCharacter2", _, AvatarAction.Killed(_, None)) => true case _ => false } ) diff --git a/src/test/scala/objects/RepairableTest.scala b/src/test/scala/objects/RepairableTest.scala index 95c28f5ce..a558b6487 100644 --- a/src/test/scala/objects/RepairableTest.scala +++ b/src/test/scala/objects/RepairableTest.scala @@ -318,7 +318,7 @@ class RepairableTurretWeapon extends ActorTest { ) assert( msg4 match { - case MessageEnvelope("test", _, VehicleAction.EquipmentInSlot(_, PlanetSideGUID(2), 1, t)) + case MessageEnvelope("test", _, VehicleAction.EquipmentInSlot(PlanetSideGUID(2), 1, t)) if t eq turretWeapon => true case _ => false diff --git a/src/test/scala/objects/TelepadRouterTest.scala b/src/test/scala/objects/TelepadRouterTest.scala index a1c25b0cd..bc43ef10d 100644 --- a/src/test/scala/objects/TelepadRouterTest.scala +++ b/src/test/scala/objects/TelepadRouterTest.scala @@ -59,7 +59,6 @@ class TelepadDeployableNoRouterTest extends ActorTest { "NEUTRAL", _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Build, DeployableInfo(PlanetSideGUID(1), DeployableIcon.RouterTelepad, Vector3.Zero, PlanetSideGUID(0)) ) @@ -75,7 +74,6 @@ class TelepadDeployableNoRouterTest extends ActorTest { "NEUTRAL", _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Dismiss, DeployableInfo(PlanetSideGUID(1), DeployableIcon.RouterTelepad, Vector3.Zero, PlanetSideGUID(0)) ) @@ -130,7 +128,6 @@ class TelepadDeployableNoActivationTest extends ActorTest { "NEUTRAL", _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Build, DeployableInfo(PlanetSideGUID(1), DeployableIcon.RouterTelepad, Vector3.Zero, PlanetSideGUID(0)) ) @@ -147,7 +144,6 @@ class TelepadDeployableNoActivationTest extends ActorTest { "NEUTRAL", _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Dismiss, DeployableInfo(PlanetSideGUID(1), DeployableIcon.RouterTelepad, Vector3.Zero, PlanetSideGUID(0)) ) @@ -205,7 +201,6 @@ class TelepadDeployableAttemptTest extends ActorTest { "NEUTRAL", _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Build, DeployableInfo(PlanetSideGUID(1), DeployableIcon.RouterTelepad, Vector3.Zero, PlanetSideGUID(0)) ) @@ -279,7 +274,6 @@ class TelepadDeployableResponseFromRouterTest extends FreedContextActorTest { "NEUTRAL", _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Build, DeployableInfo(PlanetSideGUID(1), DeployableIcon.RouterTelepad, Vector3.Zero, PlanetSideGUID(0)) ) diff --git a/src/test/scala/objects/VehicleControlTest.scala b/src/test/scala/objects/VehicleControlTest.scala index 14bf44040..54306b8ae 100644 --- a/src/test/scala/objects/VehicleControlTest.scala +++ b/src/test/scala/objects/VehicleControlTest.scala @@ -27,7 +27,7 @@ import net.psforever.packet.game._ import net.psforever.services.ServiceManager import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} -import net.psforever.services.vehicle.VehicleAction +import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.types._ import scala.concurrent.duration._ @@ -74,7 +74,7 @@ class VehicleControlPrepareForDeletionPassengerTest extends ActorTest { val vehicle_msg = vehicleProbe.receiveN(1, 500 milliseconds) vehicle_msg.head match { - case MessageEnvelope("test", _, VehicleAction.KickPassenger(PlanetSideGUID(2), 4, true, PlanetSideGUID(1))) => ; + case MessageEnvelope("test", _, VehicleAction.KickPassenger(4, true, PlanetSideGUID(1))) => ; case _ => assert(false, s"VehicleControlPrepareForDeletionPassengerTest: ${vehicle_msg.head}") } @@ -226,7 +226,7 @@ class VehicleControlPrepareForDeletionMountedCargoTest extends FreedContextActor val vehicleMsgs = eventsProbe.receiveN(6, 10.seconds) val cargoMsgs = cargoProbe.receiveN(1, 1.seconds) vehicleMsgs.head match { - case MessageEnvelope("test", _, VehicleAction.KickPassenger(PlanetSideGUID(4), 4, true, PlanetSideGUID(2))) => () + case MessageEnvelope("test", _, VehicleAction.KickPassenger(4, true, PlanetSideGUID(2))) => () case _ => assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-1: ${vehicleMsgs.head}") } @@ -478,8 +478,8 @@ class VehicleControlShieldsChargingTest extends ActorTest { vehicle.Actor ! CommonMessages.ChargeShields(15, None) val msg = probe.receiveOne(500 milliseconds) assert(msg match { - case VehicleServiceMessage(_, _, PlanetsideAttribute(PlanetSideGUID(10), 68, 15)) => true - case _ => false + case MessageEnvelope(_, _, PlanetsideAttribute(PlanetSideGUID(10), 68, 15)) => true + case _ => false }) assert(vehicle.Shields == 15) assert(vehicle.History.exists({ p => p.isInstanceOf[ShieldCharge] })) diff --git a/src/test/scala/service/LocalServiceTest.scala b/src/test/scala/service/LocalServiceTest.scala deleted file mode 100644 index 8519962c1..000000000 --- a/src/test/scala/service/LocalServiceTest.scala +++ /dev/null @@ -1,376 +0,0 @@ -// Copyright (c) 2017 PSForever -package service - -import akka.actor.Props -import akka.testkit.TestProbe -import base.{ActorTest, FreedContextActorTest} -import net.psforever.objects.{GlobalDefinitions, SensorDeployable, Vehicle} -import net.psforever.objects.serverobject.PlanetSideServerObject -import net.psforever.objects.serverobject.terminals.{ProximityTerminal, Terminal} -import net.psforever.objects.vehicles.control.VehicleControl -import net.psforever.objects.zones.{Zone, ZoneMap} -import net.psforever.packet.game._ -import net.psforever.services.base.message.{SendResponse, SetEmpire} -import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3} -import net.psforever.services.{Service, ServiceManager} -import net.psforever.services.local._ - -import scala.concurrent.duration._ - -class LocalService1Test extends ActorTest { - ServiceManager.boot(system) - - "LocalService" should { - "construct" in { - system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") - assert(true) - } - } -} - -class LocalService2Test extends ActorTest { - ServiceManager.boot(system) - - "LocalService" should { - "subscribe" in { - val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") - service ! Service.Join("test") - assert(true) - } - } -} - -class LocalService3Test extends ActorTest { - ServiceManager.boot(system) - - "LocalService" should { - "subscribe to a specific channel" in { - val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") - service ! Service.Join("test") - service ! Service.Leave() - assert(true) - } - } -} - -class LocalService4Test extends ActorTest { - ServiceManager.boot(system) - - "LocalService" should { - "subscribe" in { - val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") - service ! Service.Join("test") - service ! Service.LeaveAll() - assert(true) - } - } -} - -class LocalService5Test extends ActorTest { - ServiceManager.boot(system) - - "LocalService" should { - "pass an unhandled message" in { - val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") - service ! Service.Join("test") - service ! "hello" - expectNoMessage() - } - } -} - - - -class DeployItemTest extends ActorTest { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "deploy-item-test-service") - val objDef = GlobalDefinitions.motionalarmsensor - val obj = new SensorDeployable(objDef) - obj.Position = Vector3(1, 2, 3) - obj.Orientation = Vector3(4, 5, 6) - obj.GUID = PlanetSideGUID(40) - val pkt = ObjectCreateMessage( - objDef.ObjectId, - obj.GUID, - objDef.Packet.ConstructorData(obj).get - ) - - "AvatarService" should { - "pass DeployItem" in { - service ! Service.Join("test") - service ! LocalServiceMessage("test", LocalAction.DeployItem(obj)) - expectMsg(LocalServiceResponse("/test/Local", PlanetSideGUID(0), SendResponse(Seq(pkt)))) - } - } -} - -class DeployableMapIconTest extends ActorTest { - ServiceManager.boot(system) - - "LocalService" should { - "pass DeployableMapIcon" in { - val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") - service ! Service.Join("test") - service ! LocalServiceMessage( - "test", - PlanetSideGUID(10), - LocalAction.DeployableMapIcon(DeploymentAction.Build, DeployableInfo(PlanetSideGUID(40), DeployableIcon.Boomer, Vector3(1, 2, 3), PlanetSideGUID(11))) - ) - expectMsg( - LocalServiceResponse( - "/test/Local", - PlanetSideGUID(10), - LocalAction.DeployableMapIcon( - DeploymentAction.Build, - DeployableInfo(PlanetSideGUID(40), DeployableIcon.Boomer, Vector3(1, 2, 3), PlanetSideGUID(11)) - ) - ) - ) - } - } -} - -class DoorClosesTest extends FreedContextActorTest { - val probe = new TestProbe(system) - val zone = new Zone("test", new ZoneMap("test-map"), 0) { - override def SetupNumberPools() : Unit = { } - } - zone.init(context) - expectNoMessage(500 milliseconds) - - "LocalService" should { - "pass DoorCloses" in { - zone.LocalEvents.tell(Service.Join("test"), probe.ref) - zone.LocalEvents ! LocalServiceMessage("test", PlanetSideGUID(10), LocalAction.DoorCloses(PlanetSideGUID(40))) - probe.expectMsg(LocalServiceResponse("/test/Local", PlanetSideGUID(10), LocalAction.DoorCloses(PlanetSideGUID(40)))) - } - } -} - -class HackClearTest extends ActorTest { - ServiceManager.boot(system) - val obj = new PlanetSideServerObject() { - def Faction = PlanetSideEmpire.NEUTRAL - def Definition = null - GUID = PlanetSideGUID(40) - } - - "LocalService" should { - "pass HackClear" in { - val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") - service ! Service.Join("test") - service ! LocalServiceMessage("test", PlanetSideGUID(10), LocalAction.HackClear(obj.GUID, 0L, HackState7.Unk8)) - expectMsg( - LocalServiceResponse("/test/Local", PlanetSideGUID(10), LocalAction.HackClear(PlanetSideGUID(40), 0L, HackState7.Unk8)) - ) - } - } -} - -class ProximityTerminalEffectOnTest extends ActorTest { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") - val terminal = new ProximityTerminal(GlobalDefinitions.medical_terminal) - terminal.GUID = PlanetSideGUID(1) - - "LocalService" should { - "pass ProximityTerminalEffect (true)" in { - service ! Service.Join("nowhere") - service ! LocalServiceMessage("nowhere", LocalAction.ProximityTerminalEffect(PlanetSideGUID(1), effectState = true)) - expectMsg( - LocalServiceResponse( - "/nowhere/Local", - PlanetSideGUID(0), - LocalAction.ProximityTerminalEffect(PlanetSideGUID(1), true) - ) - ) - } - } -} - -class ProximityTerminalEffectOffTest extends ActorTest { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") - val terminal = new ProximityTerminal(GlobalDefinitions.medical_terminal) - terminal.GUID = PlanetSideGUID(1) - - "LocalService" should { - "pass ProximityTerminalEffect (false)" in { - service ! Service.Join("nowhere") - service ! LocalServiceMessage("nowhere", LocalAction.ProximityTerminalEffect(PlanetSideGUID(1), effectState = false)) - expectMsg( - LocalServiceResponse( - "/nowhere/Local", - PlanetSideGUID(0), - LocalAction.ProximityTerminalEffect(PlanetSideGUID(1), false) - ) - ) - } - } -} - -class RouterTelepadTransportTest extends ActorTest { - ServiceManager.boot(system) - - "LocalService" should { - "pass RouterTelepadTransport" in { - val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") - service ! Service.Join("test") - service ! LocalServiceMessage( - "test", - PlanetSideGUID(10), - LocalAction.RouterTelepadTransport( - PlanetSideGUID(11), - PlanetSideGUID(12), - PlanetSideGUID(13) - ) - ) - expectMsg( - LocalServiceResponse( - "/test/Local", - PlanetSideGUID(10), - LocalAction.RouterTelepadTransport(PlanetSideGUID(11), PlanetSideGUID(12), PlanetSideGUID(13)) - ) - ) - } - } -} - -class SetEmpireTest extends ActorTest { - ServiceManager.boot(system) - val obj = new SensorDeployable(GlobalDefinitions.motionalarmsensor) - - "LocalService" should { - "pass SetEmpire" in { - val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") - service ! Service.Join("test") - service ! LocalServiceMessage("test", SetEmpire(PlanetSideGUID(10), PlanetSideEmpire.TR)) - expectMsg( - LocalServiceResponse( - "/test/Local", - PlanetSideGUID(0), - SetEmpire(PlanetSideGUID(10), PlanetSideEmpire.TR) - ) - ) - } - } -} - -class ToggleTeleportSystemTest extends ActorTest { - ServiceManager.boot(system) - - "LocalService" should { - "pass ToggleTeleportSystem" in { - val router = Vehicle(GlobalDefinitions.router) - router.Actor = system.actorOf(Props(classOf[VehicleControl], router), "test-router") - val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") - service ! Service.Join("test") - service ! LocalServiceMessage("test", PlanetSideGUID(10), LocalAction.ToggleTeleportSystem(router, None)) - expectMsg( - LocalServiceResponse("/test/Local", PlanetSideGUID(10), LocalAction.ToggleTeleportSystem(router, None)) - ) - } - } -} - -class TriggerEffectTest extends ActorTest { - ServiceManager.boot(system) - - "LocalService" should { - "pass TriggerEffect (1)" in { - val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") - service ! Service.Join("test") - service ! LocalServiceMessage("test", LocalAction.TriggerEffect("on", PlanetSideGUID(40))) - expectMsg( - LocalServiceResponse( - "/test/Local", - PlanetSideGUID(10), - LocalAction.TriggerEffectAtLocation(PlanetSideGUID(40), "on", None, None) - ) - ) - } - } -} - -class TriggerEffectInfoTest extends ActorTest { - ServiceManager.boot(system) - - "LocalService" should { - "pass TriggerEffect (2)" in { - val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") - service ! Service.Join("test") - service ! LocalServiceMessage( - "test", - PlanetSideGUID(10), - LocalAction.TriggerEffectInfo(PlanetSideGUID(40), "on", true, 1000) - ) - expectMsg( - LocalServiceResponse( - "/test/Local", - PlanetSideGUID(10), - LocalAction.TriggerEffectAtLocation(PlanetSideGUID(40), "on", Some(TriggeredEffect(true, 1000)), None) - ) - ) - } - } -} - -class TriggerEffectLocationTest extends ActorTest { - ServiceManager.boot(system) - - "LocalService" should { - "pass TriggerEffect (3)" in { - val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") - service ! Service.Join("test") - service ! LocalServiceMessage( - "test", - PlanetSideGUID(10), - LocalAction.TriggerEffectLocation( - "spawn_object_failed_effect", - Vector3(1.1f, 2.2f, 3.3f), - Vector3(4.4f, 5.5f, 6.6f) - ) - ) - expectMsg( - LocalServiceResponse( - "/test/Local", - PlanetSideGUID(10), - LocalAction.TriggerEffectAtLocation( - PlanetSideGUID(0), - "spawn_object_failed_effect", - None, - Some(TriggeredEffectLocation(Vector3(1.1f, 2.2f, 3.3f), Vector3(4.4f, 5.5f, 6.6f))) - ) - ) - ) - } - } -} - -class TriggerSoundTest extends ActorTest { - import net.psforever.packet.game.TriggeredSound - ServiceManager.boot(system) - - "LocalService" should { - "pass TriggerSound" in { - val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service") - service ! Service.Join("test") - service ! LocalServiceMessage( - "test", - PlanetSideGUID(10), - LocalAction.TriggerSound(TriggeredSound.LockedOut, Vector3(1.1f, 2.2f, 3.3f), 0, 0.75f) - ) - expectMsg( - LocalServiceResponse( - "/test/Local", - PlanetSideGUID(10), - LocalAction.TriggerSound(TriggeredSound.LockedOut, Vector3(1.1f, 2.2f, 3.3f), 0, 0.75f) - ) - ) - } - } -} - -object LocalServiceTest { - //decoy -} diff --git a/src/test/scala/service/VehicleServiceTest.scala b/src/test/scala/service/VehicleServiceTest.scala deleted file mode 100644 index 8bd56c628..000000000 --- a/src/test/scala/service/VehicleServiceTest.scala +++ /dev/null @@ -1,410 +0,0 @@ -// Copyright (c) 2017 PSForever -package service - -import akka.actor.Props -import base.ActorTest -import net.psforever.objects._ -import net.psforever.objects.vehicles.control.VehicleControl -import net.psforever.objects.zones.Zone -import net.psforever.types.{PlanetSideGUID, _} -import net.psforever.services.{Service, ServiceManager} -import net.psforever.services.vehicle._ - -class VehicleService1Test extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "construct" in { - system.actorOf(Props(classOf[VehicleService]), "v-service") - assert(true) - } - } -} - -class VehicleService2Test extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "subscribe" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - assert(true) - } - } -} - -class VehicleService3Test extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "subscribe to a specific channel" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - service ! Service.Leave() - assert(true) - } - } -} - -class VehicleService4Test extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "subscribe" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - service ! Service.LeaveAll() - assert(true) - } - } -} - -class VehicleService5Test extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "pass an unhandled message" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - service ! "hello" - expectNoMessage() - } - } -} - -class OwnershipTest extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "pass Awareness" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage("test", PlanetSideGUID(10), VehicleAction.Ownership(PlanetSideGUID(11))) - expectMsg( - VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleAction.Ownership(PlanetSideGUID(11))) - ) - } - } -} - -class ChildObjectStateTest extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "pass ChildObjectState" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - PlanetSideGUID(10), - VehicleAction.ChildObjectState(PlanetSideGUID(11), 1.2f, 3.4f) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleAction.ChildObjectState(PlanetSideGUID(11), 1.2f, 3.4f) - ) - ) - } - } -} - -class DeployRequestTest extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "pass DeployRequest" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - PlanetSideGUID(10), - VehicleAction.DeployRequest(PlanetSideGUID(11), DriveState.Mobile, 0, false, Vector3(1.2f, 3.4f, 5.6f)) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleAction.DeployRequest(PlanetSideGUID(11), DriveState.Mobile, 0, false, Vector3(1.2f, 3.4f, 5.6f)) - ) - ) - } - } -} - -class DismountVehicleTest extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "pass DismountVehicle" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage("test", PlanetSideGUID(10), VehicleAction.DismountVehicle(BailType.Normal, false)) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleAction.DismountVehicle(BailType.Normal, false) - ) - ) - } - } -} - -class InventoryStateTest extends ActorTest { - ServiceManager.boot(system) - val tool = Tool(GlobalDefinitions.beamer) - tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(13) - val cdata = tool.Definition.Packet.ConstructorData(tool).get - - "VehicleService" should { - "pass InventoryState" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - PlanetSideGUID(10), - VehicleAction.InventoryState(tool, PlanetSideGUID(11), 0, cdata) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleAction.InventoryState(tool, PlanetSideGUID(11), 0, cdata) - ) - ) - } - } -} - -class InventoryState2Test extends ActorTest { - ServiceManager.boot(system) - val tool = Tool(GlobalDefinitions.beamer) - tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(13) - val cdata = tool.Definition.Packet.ConstructorData(tool).get - - "VehicleService" should { - "pass InventoryState2" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - PlanetSideGUID(10), - VehicleAction.InventoryState2(PlanetSideGUID(11), PlanetSideGUID(12), 13) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleAction.InventoryState2(PlanetSideGUID(11), PlanetSideGUID(12), 13) - ) - ) - } - } -} - -class KickPassengerTest extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "pass KickPassenger" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - PlanetSideGUID(10), - VehicleAction.KickPassenger(0, false, PlanetSideGUID(11)) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleAction.KickPassenger(0, false, PlanetSideGUID(11)) - ) - ) - } - } -} - -class LoadVehicleTest extends ActorTest { - ServiceManager.boot(system) - val vehicle = Vehicle(GlobalDefinitions.quadstealth) - vehicle.Actor = system.actorOf(Props(classOf[VehicleControl], vehicle), "test-vehicle") - val cdata = vehicle.Definition.Packet.ConstructorData(vehicle).get - - "VehicleService" should { - "pass LoadVehicle" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - PlanetSideGUID(10), - VehicleAction.LoadVehicle(vehicle, 12, PlanetSideGUID(11), cdata) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleAction.LoadVehicle(vehicle, 12, PlanetSideGUID(11), cdata) - ) - ) - } - } -} - -class MountVehicleTest extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "pass MountVehicle" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage("test", VehicleAction.MountVehicle(PlanetSideGUID(11), 0)) - expectMsg( - VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleAction.MountVehicle(PlanetSideGUID(11), 0)) - ) - } - } -} - -class SeatPermissionsTest extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "pass SeatPermissions" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - VehicleAction.SeatPermissions(PlanetSideGUID(11), 0, 12L) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleAction.SeatPermissions(PlanetSideGUID(11), 0, 12L) - ) - ) - } - } -} - -class StowEquipmentTest extends ActorTest { - ServiceManager.boot(system) - val tool = Tool(GlobalDefinitions.beamer) - tool.GUID = PlanetSideGUID(12) - tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(13) - val toolDef = tool.Definition - val cdata = tool.Definition.Packet.DetailedConstructorData(tool).get - - "StowEquipment" should { - "pass StowEquipment" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - PlanetSideGUID(10), - VehicleAction.StowEquipment(PlanetSideGUID(11), 0, tool) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleAction.StowEquipment(PlanetSideGUID(11), 0, toolDef.ObjectId, tool.GUID, cdata) - ) - ) - } - } -} - -class UnstowEquipmentTest extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "pass UnstowEquipment" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage("test", PlanetSideGUID(10), VehicleAction.UnstowEquipment(PlanetSideGUID(11))) - expectMsg( - VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleAction.UnstowEquipment(PlanetSideGUID(11))) - ) - } - } -} - -class VehicleStateTest extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "pass VehicleState" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - VehicleAction.VehicleState(PlanetSideGUID(11), 0, Vector3(1.2f, 3.4f, 5.6f), Vector3(7.8f, 9.1f, 2.3f), Some(Vector3(4.5f, 6.7f, 8.9f)), Option(1), 2, 3, 4, false, true) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleAction.VehicleState( - PlanetSideGUID(11), - 0, - Vector3(1.2f, 3.4f, 5.6f), - Vector3(7.8f, 9.1f, 2.3f), - Some(Vector3(4.5f, 6.7f, 8.9f)), - Option(1), - 2, - 3, - 4, - false, - true - ) - ) - ) - } - } -} - -class TransferPassengerChannelTest extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "pass TransferPassengerChannel" in { - val service = system.actorOf(Props(classOf[VehicleService]), "v-service") - val fury = Vehicle(GlobalDefinitions.fury) - fury.Actor = system.actorOf(Props(classOf[VehicleControl], fury), "test-fury") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - PlanetSideGUID(10), - VehicleAction.TransferPassengerChannel("old_channel", "new_channel", fury, PlanetSideGUID(11)) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleAction.TransferPassengerChannel("old_channel", "new_channel", fury, PlanetSideGUID(11)) - ) - ) - } - } -} - -//class TransferPassengerTest extends ActorTest { -// ServiceManager.boot(system) -// "VehicleService" should { -// "pass TransferPassenger" in { -// val fury = Vehicle(GlobalDefinitions.fury) -// val service = system.actorOf(Props[VehicleService], "v-service") -// service ! Service.Join("test") -// service ! VehicleServiceMessage("test", VehicleAction.TransferPassenger(PlanetSideGUID(10), "temp_channel", fury, PlanetSideGUID(11))) -// expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleAction.TransferPassenger("temp_channel", fury, PlanetSideGUID(11)))) -// } -// } -//} - -object VehicleServiceTest { - //decoy -} diff --git a/src/test/scala/service/avatar/AvatarActionTest.scala b/src/test/scala/service/avatar/AvatarActionTest.scala new file mode 100644 index 000000000..f83658d50 --- /dev/null +++ b/src/test/scala/service/avatar/AvatarActionTest.scala @@ -0,0 +1,215 @@ +// Copyright (c) 2026 PSForever +package service.avatar + +import net.psforever.objects.avatar.Avatar +import net.psforever.objects.ballistics.Projectile +import net.psforever.objects.sourcing.SourceEntry +import net.psforever.objects.zones.{Zone, ZoneMap} +import net.psforever.objects.{GlobalDefinitions, Player, Tool} +import net.psforever.packet.game.ObjectCreateMessage +import net.psforever.packet.game.objectcreate.{BasicCharacterData, DroppedItemData, ObjectCreateMessageParent, PlacementData} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.message.ObjectDelete +import net.psforever.types._ +import org.specs2.mutable.Specification + +object AvatarActionTest { + assert(GlobalDefinitions.suppressor != null, "missing definition - suppressor does not exist") + assert(GlobalDefinitions.avatar != null, "missing definition - avatar does not exist") + + private var i: Int = 1 + val testSuppressor: Tool = { + val obj = new Tool(GlobalDefinitions.suppressor) + obj.Position = Vector3(1, 2, 3) + obj.Orientation = Vector3(4, 5, 6) + obj.GUID = PlanetSideGUID(i) + i += 1 + obj.AmmoSlots.map(_.Box).foreach { box => + box.GUID = PlanetSideGUID(i) + i += 1 + } + obj + } + + val testPlayer: Player = { + val avatar = new Avatar(id = 1, BasicCharacterData("testPlayer", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) + avatar.locker.GUID = PlanetSideGUID(i) + i += 1 + val obj = new Player(avatar) + obj.Position = Vector3(1, 2, 3) + obj.Orientation = Vector3(4, 5, 6) + obj.GUID = PlanetSideGUID(i) + obj.Spawn() + i += 1 + obj + } +} + +class AvatarActionTest extends Specification { + import AvatarActionTest._ + + "DropItem" should { + "respond" in { + val obj = testSuppressor + val msg = AvatarAction.DropItem(obj) + val definition = obj.Definition + val objectData = DroppedItemData( + PlacementData(obj.Position, obj.Orientation), + definition.Packet.ConstructorData(obj).get + ) + msg.response() match { + case AvatarAction.DropCreatedItem(pkt) => + pkt match { + case ObjectCreateMessage(_, oid, guid, _, data) => + oid mustEqual definition.ObjectId + guid mustEqual obj.GUID + data mustEqual objectData + case _ => + ko + } + case _ => + ko + } + } + } + + "EquipmentInHand" should { + "respond" in { + val obj = testSuppressor + val msg = AvatarAction.EquipmentInHand(PlanetSideGUID(100), 2, obj) + val definition = obj.Definition + val objectData = definition.Packet.ConstructorData(obj).get + msg.response() match { + case AvatarAction.EquipmentCreatedInHand(pkt) => + pkt match { + case ObjectCreateMessage(_, oid, guid, pdata, data) => + oid mustEqual definition.ObjectId + guid mustEqual obj.GUID + pdata.contains(ObjectCreateMessageParent(PlanetSideGUID(100), 2)) mustEqual true + data mustEqual objectData + case _ => + ko + } + case _ => + ko + } + } + } + + "LoadPlayer (1)" should { + "respond" in { + val obj = testPlayer + val definition = obj.Definition + val id = definition.ObjectId + val guid = obj.GUID + val objectData = definition.Packet.ConstructorData(obj).get + val msg = AvatarAction.LoadPlayer(id, guid, objectData, None) + msg.response() match { + case AvatarAction.LoadCreatedPlayer(pkt) => + pkt match { + case ObjectCreateMessage(_, oid, guid, _, data) => + oid mustEqual id + guid mustEqual guid + data mustEqual objectData + case _ => + ko + } + case _ => + ko + } + } + } + + "LoadPlayer (2)" should { + "respond" in { + val obj = testPlayer + val definition = obj.Definition + val id = definition.ObjectId + val guid = obj.GUID + val parentData = ObjectCreateMessageParent(PlanetSideGUID(100), 2) + val objectData = definition.Packet.ConstructorData(obj).get + val msg = AvatarAction.LoadPlayer(id, guid, objectData, Some(parentData)) + msg.response() match { + case AvatarAction.LoadCreatedPlayer(pkt) => + pkt match { + case ObjectCreateMessage(_, oid, guid, pdata, data) => + oid mustEqual id + guid mustEqual guid + pdata.contains(parentData) mustEqual true + data mustEqual objectData + case _ => + ko + } + case _ => + ko + } + } + } + + "LoadProjectile" should { + assert(GlobalDefinitions.wasp_rocket_projectile != null, "missing definition - wasp_rocket_projectile does not exist") + assert(GlobalDefinitions.wasp_weapon_system != null, "missing definition - wasp_weapon_system does not exist") + + "respond" in { + val obj = new Projectile( + GlobalDefinitions.wasp_rocket_projectile, + GlobalDefinitions.wasp_weapon_system, + GlobalDefinitions.wasp_weapon_system.FireModes.head, + Some((1, SourceEntry.None)), + SourceEntry.None, + GlobalDefinitions.wasp_weapon_system.ObjectId, + Vector3(1, 2, 3), + Vector3(4, 5, 6), + Some(Vector3(7, 8, 9)) + ) + obj.GUID = PlanetSideGUID(1) + val definition = obj.Definition + val id = definition.ObjectId + val guid = obj.GUID + val objectData = definition.Packet.ConstructorData(obj).get + val msg = AvatarAction.LoadProjectile(id, guid, objectData) + msg.response() match { + case AvatarAction.LoadCreatedProjectile(pkt) => + pkt match { + case ObjectCreateMessage(_, oid, guid, _, data) => + oid mustEqual id + guid mustEqual guid + data mustEqual objectData + case _ => + ko + } + case _ => + ko + } + } + } + + "PickupItem" should { + "respond" in { + val obj = testSuppressor + val msg = AvatarAction.PickupItem(obj, 2) + msg.response() match { + case ObjectDelete(guid, slot) => + guid mustEqual obj.GUID + slot mustEqual 2 + case _ => + ko + } + } + } + + "Release" should { + val testZone: Zone = new Zone(id = "test", new ZoneMap( name = "test"), zoneNumber = 1) + + "respond" in { + val obj = testPlayer + val msg = AvatarAction.Release(obj, testZone) + msg.response() match { + case AvatarAction.ReleasePlayer(player) => + (player == obj) mustEqual true + case _ => + ko + } + } + } +} diff --git a/src/test/scala/service/base/EnvelopeTest.scala b/src/test/scala/service/base/EnvelopeTest.scala new file mode 100644 index 000000000..85c1e7b3c --- /dev/null +++ b/src/test/scala/service/base/EnvelopeTest.scala @@ -0,0 +1,89 @@ +// Copyright (c) 2026 PSForever +package service.base + +import net.psforever.services.base.EventSystemStamp +import net.psforever.services.base.envelope._ +import net.psforever.services.base.message.{EventMessage, EventResponse, SelfRespondingEvent} +import net.psforever.types.PlanetSideGUID +import org.specs2.mutable.Specification + +object EnvelopeTest { + case object TestStamp extends EventSystemStamp + + val TestFilter: PlanetSideGUID = PlanetSideGUID(1) + + final def StringWithSlashes(str: String): String = s"/$str/" + + final case class TestMessage(value: Int) extends SelfRespondingEvent + + final case class TestOutputEvent(value: Int) extends EventResponse + + final case class TestInputMessage(value: Int) extends EventMessage { + def response(): EventResponse = TestOutputEvent(value + 2) + } +} + +class EnvelopeTest extends Specification { + import EnvelopeTest._ + + "MessageEnvelope" should { + "construct" in { + MessageEnvelope("test", TestFilter, TestMessage(5)) + ok + } + + "message match" in { + val input = MessageEnvelope("test", TestFilter, TestMessage(5)) + input match { + case GenericMessageEnvelope("test", TestFilter, TestMessage(5)) => + ok + case _ => + ko + } + } + + "response (no stamp)" in { + val input = MessageEnvelope("test", TestFilter, TestMessage(5)) + input match { + case reply @ GenericResponseEnvelope("test", TestFilter, NoReply) => + reply.stamp mustEqual Undelivered + case _ => + ko + } + } + + "response" in { + val input = MessageEnvelope("test", TestFilter, TestMessage(5)) + val output = input.response(TestStamp, StringWithSlashes) + output match { + case reply @ GenericResponseEnvelope("/test/", TestFilter, TestMessage(5)) => + reply.stamp mustEqual TestStamp + case _ => + ko + } + } + + "response (different from input)" in { + val input = MessageEnvelope("test", TestFilter, TestInputMessage(5)) + val output = input.response(TestStamp, StringWithSlashes) + output match { + case reply @ GenericResponseEnvelope("/test/", TestFilter, TestOutputEvent(7)) => + reply.stamp mustEqual TestStamp + case _ => + ko + } + } + } + + "GenericResponseEnvelope" should { + "construct (quick)" in { + val input = GenericResponseEnvelope(TestStamp, "test", TestFilter, TestMessage(5)) + input match { + case reply @ GenericResponseEnvelope("test", TestFilter, TestMessage(5)) => + reply.stamp mustEqual TestStamp + case _ => + ko + } + } + } +} diff --git a/src/test/scala/service/base/EventMessageTest.scala b/src/test/scala/service/base/EventMessageTest.scala new file mode 100644 index 000000000..8b9b1bf50 --- /dev/null +++ b/src/test/scala/service/base/EventMessageTest.scala @@ -0,0 +1,42 @@ +// Copyright (c) 2026 PSForever +package service.base + +import net.psforever.services.base.message.{EventMessage, EventResponse, SelfRespondingEvent} +import org.specs2.mutable.Specification + +object EventMessageTest { + final case class TestOutputEvent(value: Int) extends EventResponse + + final case class TestInputMessage(value: Int) extends EventMessage { + def response(): EventResponse = TestOutputEvent(value + 2) + } + + final case class TestMessage(value: Int) extends SelfRespondingEvent +} + +class EventMessageTest extends Specification { + import EventMessageTest._ + + "EventMessage" should { + "construct" in { + TestInputMessage(5) + ok + } + + "response" in { + TestInputMessage(5).response() mustEqual TestOutputEvent(7) + } + } + + "SelfResponseMessage" should { + "construct" in { + TestMessage(5) + ok + } + + "response (is same as self)" in { + val msg = TestMessage(5) + msg.response() mustEqual msg + } + } +} diff --git a/src/test/scala/service/base/EventServiceCacheSupportTest.scala b/src/test/scala/service/base/EventServiceCacheSupportTest.scala new file mode 100644 index 000000000..2e7fc7610 --- /dev/null +++ b/src/test/scala/service/base/EventServiceCacheSupportTest.scala @@ -0,0 +1,221 @@ +// Copyright (c) 2026 PSForever +package service.base + +import akka.actor.Props +import akka.testkit.TestProbe +import base.ActorTest +import net.psforever.services.Service +import net.psforever.services.base.message.EventMessage +import net.psforever.services.base.{CachedGenericEventMessageEnvelope, EventServiceSupport, GenericEventServiceWithCacheAndSupport, GenericSupportEnvelope} +import net.psforever.types.PlanetSideGUID + +import scala.concurrent.duration._ + +object EventServiceCacheSupportTest { + class TestCacheService(eventSupportServices: List[EventServiceSupport]) + extends GenericEventServiceWithCacheAndSupport(EventServiceTestBase.TestStamp, eventSupportServices) + + final case class CachedSupportTestEnvelope( + guid: PlanetSideGUID, + originalChannel: String, + override val msg: EventMessage, + supportMessage: Any + ) extends CachedGenericEventMessageEnvelope with GenericSupportEnvelope { + assert(guid != Service.defaultPlayerGUID, "can not cache message under default GUID") + def filter: PlanetSideGUID = Service.defaultPlayerGUID + def supportLabel: String = "supportActor" + } +} + +class EventServiceCacheSupportTestDefault extends ActorTest { + import EventServiceCacheSupportTest._ + "GenericEventServiceWithCacheAndSupport" should { + "construct" in { + system.actorOf(Props(classOf[TestCacheService], List()), name = "EventServiceCacheSupportTest.0") + } + } +} + +class EventServiceCacheSupportTestSupportNormally extends ActorTest { + import EventServiceCacheSupportTest._ + import EventServiceTestBase._ + "GenericEventServiceWithCacheAndSupport" should { + "send a valid message to both subscribed channel and support class, like normal GenericEventServiceWithSupport" in { + val mainProbe = TestProbe("MainProbe") + val supportProbe = TestProbe("SupportProbe") + val events = system.actorOf(Props(classOf[TestCacheService], List(TestSupportActorLoader)), name = "EventServiceCacheSupportTest.1") + events.tell(Service.Join("test"), mainProbe.ref) + val originalMessage = TestSupportEnvelope("test", SupportActorRepliesWith("hello world", supportProbe.ref)) + events ! originalMessage + //main reply + val mainReply = mainProbe.receiveOne(250 milliseconds) + mainReply match { + case msg if msg == originalMessage => () + case badmsg => + assert(false, s"(1) expected delivery of test envelope, but received $badmsg instead") + } + //support reply + val supportReply = supportProbe.receiveOne(250 milliseconds) + supportReply match { + case msg: String if msg.equals("hello world") => () + case badmsg => + assert(false, s"(2) expected delivery of test envelope payload 'hello world', but received '$badmsg' instead") + } + } + } +} + +class EventServiceCacheSupportTestCachedMessages extends ActorTest { + import EventServiceCacheSupportTest._ + import EventServiceTestBase._ + "GenericEventServiceWithCacheAndSupport" should { + "wait on sending designated cache-able messages after a few milliseconds" in { + val mainProbe = TestProbe("MainProbe") + val events = system.actorOf(Props(classOf[TestCacheService], List()), name = "EventServiceCacheSupportTest.2") + events.tell(Service.Join("test"), mainProbe.ref) + val firstMessage = CachedEnvelope(PlanetSideGUID(1), "test", TestMessage(1)) + events ! firstMessage + mainProbe.expectNoMessage(50 milliseconds) + val reply = mainProbe.receiveOne(125 milliseconds) + reply match { + case msg if msg == firstMessage => () + case badmsg => + assert(false, s"(3) expected delivery of test envelope, but received $badmsg instead") + } + } + } +} + +class EventServiceCacheSupportTestMultipleCachedMessagesAtOnce extends ActorTest { + import EventServiceCacheSupportTest._ + import EventServiceTestBase._ + "GenericEventServiceWithCacheAndSupport" should { + "send cache-able messages within a time span in bulk" in { + val mainProbe = TestProbe("MainProbe") + val events = system.actorOf(Props(classOf[TestCacheService], List()), name = "EventServiceCacheSupportTest.3") + events.tell(Service.Join("test"), mainProbe.ref) + val firstMessage = CachedEnvelope(PlanetSideGUID(1), "test", TestMessage(1)) + val secondMessage = CachedEnvelope(PlanetSideGUID(2), "test", TestMessage(2)) + val thirdMessage = CachedEnvelope(PlanetSideGUID(3), "test", TestMessage(3)) + events ! firstMessage + events ! secondMessage + events ! thirdMessage + mainProbe.expectNoMessage(50 milliseconds) + val reply = mainProbe.receiveN(3, 125 milliseconds) + reply.head match { + case msg if msg == firstMessage => () + case badmsg => + assert(false, s"(4) expected delivery of test envelope, but received $badmsg instead") + } + reply(1) match { + case msg if msg == secondMessage => () + case badmsg => + assert(false, s"(5) expected delivery of test envelope, but received $badmsg instead") + } + reply(2) match { + case msg if msg == thirdMessage => () + case badmsg => + assert(false, s"(6) expected delivery of test envelope, but received $badmsg instead") + } + mainProbe.expectNoMessage(65 milliseconds) + } + } +} + +class EventServiceCacheSupportTestMultipleCachedSameMessages extends ActorTest { + import EventServiceCacheSupportTest._ + import EventServiceTestBase._ + "GenericEventServiceWithCacheAndSupport" should { + "only caches and dispatches the last message with a given filtering token" in { + val mainProbe = TestProbe("MainProbe") + val events = system.actorOf(Props(classOf[TestCacheService], List()), name = "EventServiceCacheSupportTest.4") + events.tell(Service.Join("test"), mainProbe.ref) + val firstMessage = CachedEnvelope(PlanetSideGUID(1), "test", TestMessage(1)) + val secondMessage = CachedEnvelope(PlanetSideGUID(1), "test", TestMessage(2)) + val thirdMessage = CachedEnvelope(PlanetSideGUID(1), "test", TestMessage(3)) //this one! + events ! firstMessage + events ! secondMessage + events ! thirdMessage + mainProbe.expectNoMessage(50 milliseconds) + val reply = mainProbe.receiveOne(125 milliseconds) + reply match { + case badmsg if badmsg == firstMessage => + assert(false, s"(1) received wrong message - $badmsg") + case badmsg if badmsg == secondMessage => + assert(false, s"(2) received wrong message - $badmsg") + case msg if msg == thirdMessage => () + case badmsg => + assert(false, s"(7) expected delivery of test envelope, but received $badmsg instead") + } + } + } +} + +class EventServiceCacheSupportTestMultipleCachedMessages extends ActorTest { + import EventServiceCacheSupportTest._ + import EventServiceTestBase._ + "GenericEventServiceWithCacheAndSupport" should { + "send cache-able messages within a time span, separated if they are part of different caches" in { + val mainProbe = TestProbe("MainProbe") + val events = system.actorOf(Props(classOf[TestCacheService], List()), name = "EventServiceCacheSupportTest.5") + events.tell(Service.Join("test"), mainProbe.ref) + val firstMessage = CachedEnvelope(PlanetSideGUID(1), "test", TestMessage(1)) + //first cache flush + events ! firstMessage + mainProbe.expectNoMessage(50 milliseconds) + val firstReply = mainProbe.receiveOne(125 milliseconds) + firstReply match { + case msg if msg == firstMessage => () + case badmsg => + assert(false, s"(8) expected delivery of test envelope, but received $badmsg instead") + } + mainProbe.expectNoMessage(100 milliseconds) + //second cache flush + val secondMessage = CachedEnvelope(PlanetSideGUID(2), "test", TestMessage(2)) + events ! secondMessage + mainProbe.expectNoMessage(50 milliseconds) + val reply = mainProbe.receiveOne(125 milliseconds) + reply match { + case msg if msg == secondMessage => () + case badmsg => + assert(false, s"(9) expected delivery of test envelope, but received $badmsg instead") + } + } + } +} + +class EventServiceCacheSupportTestSupportDelayed extends ActorTest { + import EventServiceCacheSupportTest._ + import EventServiceTestBase._ + "GenericEventServiceWithCacheAndSupport" should { + "cache a message for a support actor, but only dispatch that message to the support actor when flushing the cache" in { + val mainProbe = TestProbe("MainProbe") + val supportProbe = TestProbe("SupportProbe") + val events = system.actorOf(Props(classOf[TestCacheService], List(TestSupportActorLoader)), name = "EventServiceCacheSupportTest.6") + events.tell(Service.Join("test"), mainProbe.ref) + val originalMessage = CachedSupportTestEnvelope( + PlanetSideGUID(1), + "test", + TestMessage(1), + SupportActorRepliesWith("hello world", supportProbe.ref) + ) + events ! originalMessage + //main reply + supportProbe.expectNoMessage(50 milliseconds) + mainProbe.expectNoMessage(10 milliseconds) + val mainReply = mainProbe.receiveOne(125 milliseconds) + mainReply match { + case msg if msg == originalMessage => () + case badmsg => + assert(false, s"(10) expected delivery of test envelope, but received $badmsg instead") + } + //support reply + val supportReply = supportProbe.receiveOne(10 milliseconds) + supportReply match { + case msg: String if msg.equals("hello world") => () + case badmsg => + assert(false, s"(11) expected delivery of test envelope payload 'hello world', but received '$badmsg' instead") + } + } + } +} diff --git a/src/test/scala/service/base/EventServiceSupportTest.scala b/src/test/scala/service/base/EventServiceSupportTest.scala new file mode 100644 index 000000000..1b61e6eaf --- /dev/null +++ b/src/test/scala/service/base/EventServiceSupportTest.scala @@ -0,0 +1,133 @@ +// Copyright (c) 2026 PSForever +package service.base + +import akka.actor.Props +import akka.testkit.TestProbe +import base.ActorTest +import net.psforever.services.Service +import net.psforever.services.base.{EventServiceSupport, GenericEventServiceWithSupport, GenericSupportEnvelopeOnly} + +import scala.concurrent.duration._ + +object EventServiceSupportTest { + class TestSupportService(eventSupportServices: List[EventServiceSupport]) + extends GenericEventServiceWithSupport(EventServiceTestBase.TestStamp, eventSupportServices) + + final case class TestSupportOnlyEnvelope(supportLabel: String, supportMessage: EventServiceTestBase.SupportActorRepliesWith) + extends GenericSupportEnvelopeOnly +} + +class EventServiceSupportTestDefault extends ActorTest { + import EventServiceSupportTest._ + "GenericEventServiceWithSupport" should { + "construct" in { + system.actorOf(Props(classOf[TestSupportService], List()), name = "EventServiceSupportTest.0") + } + } +} + +class EventServiceSupportTestLoadSupportClass extends ActorTest { + import EventServiceSupportTest._ + import EventServiceTestBase._ + "GenericEventServiceWithSupport" should { + "construct with setting up a test support class" in { + system.actorOf(Props(classOf[TestSupportService], List(TestSupportActorLoader)), name = "EventServiceSupportTest.1") + } + } +} + +class EventServiceSupportTestSendToSupportClass extends ActorTest { + import EventServiceSupportTest._ + import EventServiceTestBase._ + "GenericEventServiceWithSupport" should { + "send a valid message to both subscribed channel and support class" in { + val mainProbe = TestProbe("MainProbe") + val supportProbe = TestProbe("SupportProbe") + val events = system.actorOf(Props(classOf[TestSupportService], List(TestSupportActorLoader)), name = "EventServiceSupportTest.2") + events.tell(Service.Join("test"), mainProbe.ref) + val originalMessage = TestSupportEnvelope("test", SupportActorRepliesWith("hello world", supportProbe.ref)) + events ! originalMessage + //main reply + val mainReply = mainProbe.receiveOne(250 milliseconds) + mainReply match { + case msg if msg == originalMessage => () + case badmsg => + assert(false, s"(1) expected delivery of test envelope, but received $badmsg instead") + } + //support reply + val supportReply = supportProbe.receiveOne(250 milliseconds) + supportReply match { + case msg: String if msg.equals("hello world") => () + case badmsg => + assert(false, s"(2) expected delivery of test envelope payload 'hello world', but received '$badmsg' instead") + } + } + } +} + +class EventServiceSupportTestSendToSupportClassOnly1 extends ActorTest { + import EventServiceSupportTest._ + import EventServiceTestBase._ + "GenericEventServiceWithSupport" should { + "send a valid message to support class (but not to subscribed channel)" in { + val mainProbe = TestProbe("MainProbe") + val supportProbe = TestProbe("SupportProbe") + val events = system.actorOf(Props(classOf[TestSupportService], List(TestSupportActorLoader)), name = "EventServiceSupportTest.3") + events.tell(Service.Join("test"), mainProbe.ref) + val originalMessage = TestSupportEnvelope("notatest", SupportActorRepliesWith("hello world", supportProbe.ref)) + events ! originalMessage + //main reply (no reply) + mainProbe.expectNoMessage(500 milliseconds) + //support reply + val supportReply = supportProbe.receiveOne(250 milliseconds) + supportReply match { + case msg: String if msg.equals("hello world") => () + case badmsg => + assert(false, s"(3) expected delivery of test envelope payload 'hello world', but received '$badmsg' instead") + } + } + } +} + +class EventServiceSupportTestSendToSupportClassOnly2 extends ActorTest { + import EventServiceSupportTest._ + import EventServiceTestBase._ + "GenericEventServiceWithSupport" should { + "send a valid message to support class (skip subscribed channel)" in { + val mainProbe = TestProbe("MainProbe") + val supportProbe = TestProbe("SupportProbe") + val events = system.actorOf(Props(classOf[TestSupportService], List(TestSupportActorLoader)), name = "EventServiceSupportTest.4") + events.tell(Service.Join("test"), mainProbe.ref) + val originalMessage = TestSupportOnlyEnvelope("supportActor", SupportActorRepliesWith("hello world", supportProbe.ref)) + events ! originalMessage + //main reply (no reply) + mainProbe.expectNoMessage(500 milliseconds) + //support reply + val supportReply = supportProbe.receiveOne(250 milliseconds) + supportReply match { + case msg: String if msg.equals("hello world") => () + case badmsg => + assert(false, s"(4) expected delivery of test envelope payload 'hello world', but received '$badmsg' instead") + } + } + } +} + +class EventServiceSupportTestSendToNoOne extends ActorTest { + import EventServiceSupportTest._ + import EventServiceTestBase._ + "GenericEventServiceWithSupport" should { + "send a valid message that neither support class nor subscribed channel handle" in { + val mainProbe = TestProbe("MainProbe") + val supportProbe = TestProbe("SupportProbe") + val events = system.actorOf(Props(classOf[TestSupportService], List(TestSupportActorLoader)), name = "EventServiceSupportTest.5") + events.tell(Service.Join("test"), mainProbe.ref) + val originalMessage = TestSupportOnlyEnvelope("notASupportActor", SupportActorRepliesWith("hello world", supportProbe.ref)) + events ! originalMessage + //main reply (no reply) + mainProbe.expectNoMessage(500 milliseconds) + //support reply (no reply) + supportProbe.expectNoMessage(500 milliseconds) + } + } +} diff --git a/src/test/scala/service/base/EventServiceTest.scala b/src/test/scala/service/base/EventServiceTest.scala new file mode 100644 index 000000000..91c60031d --- /dev/null +++ b/src/test/scala/service/base/EventServiceTest.scala @@ -0,0 +1,199 @@ +// Copyright (c) 2026 PSForever +package service.base + +import akka.actor.Props +import akka.testkit.TestProbe +import base.ActorTest +import net.psforever.services.Service +import net.psforever.services.base.GenericEventService +import net.psforever.services.base.envelope.GenericResponseEnvelope + +import scala.concurrent.duration._ + +object EventServiceTest { + class TestService() extends GenericEventService(EventServiceTestBase.TestStamp) +} + +class EventServiceTestDefault extends ActorTest { + import EventServiceTest._ + "GenericEventSystem" should { + "construct" in { + system.actorOf(Props[TestService](), name = "EventServiceTest.0") + } + } +} + +class EventServiceTest1 extends ActorTest { + import EventServiceTest._ + "GenericEventSystem" should { + "be subscribed to by channel name" in { + val probe = TestProbe("testProbe") + val events = system.actorOf(Props[TestService](), name = "EventServiceTest.1") + events.tell(Service.Join("test"), probe.ref) + probe.expectNoMessage(100 milliseconds) + } + } +} + +class EventServiceTest2 extends ActorTest { + import EventServiceTest._ + import EventServiceTestBase._ + "GenericEventSystem" should { + "receive messages from a subscribed channel" in { + val probe = TestProbe("testProbe") + val events = system.actorOf(Props[TestService](), name = "EventServiceTest.2") + events.tell(Service.Join("test"), probe.ref) + events ! MessageEnvelope("test", Service.defaultPlayerGUID, TestMessage(5)) + probe.receiveN(1, 100 milliseconds) + } + } +} + +class EventServiceTest3 extends ActorTest { + import EventServiceTest._ + import EventServiceTestBase._ + "GenericEventSystem" should { + "receive messages that are responses to the original message from a subscribed channel" in { + val probe = TestProbe("testProbe") + val events = system.actorOf(Props[TestService](), name = "EventServiceTest.3") + val msg = MessageEnvelope("test", Service.defaultPlayerGUID, TestMessage(5)) + // s => "/$s" is the default channel manipulation of the event system + val formalReply = msg.response(EventServiceTestBase.TestStamp, s => "/$s") + events.tell(Service.Join("test"), probe.ref) + events ! MessageEnvelope("test", Service.defaultPlayerGUID, TestMessage(5)) + val reply = probe.receiveOne(100 milliseconds) + assert(reply == formalReply, "(1) message expected but not received format") + } + } +} + +class EventServiceTest4 extends ActorTest { + import EventServiceTest._ + import EventServiceTestBase._ + "GenericEventSystem" should { + "not receive messages from an unsubscribed channel" in { + val probe = TestProbe("testProbe") + val missedProbe = TestProbe("testProbe") + val events = system.actorOf(Props[TestService](), name = "EventServiceTest.4") + events.tell(Service.Join("test"), probe.ref) + events.tell(Service.Join("notATest"), missedProbe.ref) + events ! MessageEnvelope("test", Service.defaultPlayerGUID, TestMessage(5)) + val reply = probe.receiveOne(100 milliseconds) + reply match { + case GenericResponseEnvelope("/test", _, TestMessage(5)) => () + case _ => assert(false, "(2) message expected but not received") + } + missedProbe.expectNoMessage(100 milliseconds) + } + } +} + +class EventServiceTest5 extends ActorTest { + import EventServiceTest._ + import EventServiceTestBase._ + "GenericEventSystem" should { + "ignore unexpected messages" in { + val probe = TestProbe("testProbe") + val events = system.actorOf(Props[TestService](), name = "EventServiceTest.5") + events.tell(Service.Join("test"), probe.ref) + events ! TestMessage(5) + probe.expectNoMessage(250 milliseconds) + //the warn log should show something + } + } +} + +class EventServiceTest6 extends ActorTest { + import EventServiceTest._ + import EventServiceTestBase._ + "GenericEventSystem" should { + "leave single channels" in { + val probe = TestProbe("testProbe") + val events = system.actorOf(Props[TestService](), name = "EventServiceTest.6") + events.tell(Service.Join("test"), probe.ref) + events.tell(Service.Join("anotherTest"), probe.ref) + + events ! MessageEnvelope("test", Service.defaultPlayerGUID, TestMessage(5)) + events ! MessageEnvelope("anotherTest", Service.defaultPlayerGUID, TestMessage(5)) + val reply1 = probe.receiveN(2, 100 milliseconds) + reply1.head match { + case GenericResponseEnvelope("/test", _, _) => () + case _ => assert(false, "(3) message expected but not received") + } + reply1(1) match { + case GenericResponseEnvelope("/anotherTest", _, _) => () + case _ => assert(false, "(4) message expected but not received") + } + + events.tell(Service.Leave(Some("anotherTest")), probe.ref) + events ! MessageEnvelope("test", Service.defaultPlayerGUID, TestMessage(5)) + events ! MessageEnvelope("anotherTest", Service.defaultPlayerGUID, TestMessage(6)) + val reply2 = probe.receiveOne(100 milliseconds) + reply2 match { + case GenericResponseEnvelope("/test", _, TestMessage(5)) => () + case _ => assert(false, "(5) message expected but not received") + } + probe.expectNoMessage(250 milliseconds) + } + } +} + +class EventServiceTest7 extends ActorTest { + import EventServiceTest._ + import EventServiceTestBase._ + "GenericEventSystem" should { + "leave all channels (1)" in { + val probe = TestProbe("testProbe") + val events = system.actorOf(Props[TestService](), name = "EventServiceTest.7") + events.tell(Service.Join("test"), probe.ref) + events.tell(Service.Join("anotherTest"), probe.ref) + + events ! MessageEnvelope("test", Service.defaultPlayerGUID, TestMessage(5)) + events ! MessageEnvelope("anotherTest", Service.defaultPlayerGUID, TestMessage(6)) + val reply = probe.receiveN(2,100 milliseconds) + reply.head match { + case GenericResponseEnvelope("/test", _, TestMessage(5)) => () + case _ => assert(false, "(6) message expected but not received") + } + reply(1) match { + case GenericResponseEnvelope("/anotherTest", _, TestMessage(6)) => () + case _ => assert(false, "(7) message expected but not received") + } + + events.tell(Service.Leave(), probe.ref) + events ! MessageEnvelope("test", Service.defaultPlayerGUID, TestMessage(5)) + events ! MessageEnvelope("anotherTest", Service.defaultPlayerGUID, TestMessage(6)) + probe.expectNoMessage(250 milliseconds) + } + } +} + +class EventServiceTest8 extends ActorTest { + import EventServiceTest._ + import EventServiceTestBase._ + "GenericEventSystem" should { + "leave all channels" in { + val probe = TestProbe("testProbe") + val events = system.actorOf(Props[TestService](), name = "EventServiceTest.8") + events.tell(Service.Join("test"), probe.ref) + events.tell(Service.Join("anotherTest"), probe.ref) + + events ! MessageEnvelope("test", Service.defaultPlayerGUID, TestMessage(5)) + events ! MessageEnvelope("anotherTest", Service.defaultPlayerGUID, TestMessage(6)) + val reply = probe.receiveN(2,100 milliseconds) + reply.head match { + case GenericResponseEnvelope("/test", _, TestMessage(5)) => () + case _ => assert(false, "(6) message expected but not received") + } + reply(1) match { + case GenericResponseEnvelope("/anotherTest", _, TestMessage(6)) => () + case _ => assert(false, "(7) message expected but not received") + } + + events.tell(Service.LeaveAll(), probe.ref) + events ! MessageEnvelope("test", Service.defaultPlayerGUID, TestMessage(5)) + events ! MessageEnvelope("anotherTest", Service.defaultPlayerGUID, TestMessage(6)) + probe.expectNoMessage(250 milliseconds) + } + } +} diff --git a/src/test/scala/service/base/EventServiceTestBase.scala b/src/test/scala/service/base/EventServiceTestBase.scala new file mode 100644 index 000000000..f5ac013d7 --- /dev/null +++ b/src/test/scala/service/base/EventServiceTestBase.scala @@ -0,0 +1,39 @@ +package service.base + +import akka.actor.{Actor, ActorContext, ActorRef, Props} +import net.psforever.services.Service +import net.psforever.services.base.message.SelfRespondingEvent +import net.psforever.services.base.{EventServiceSupport, EventSystemStamp, GenericSupportEnvelope} +import net.psforever.types.PlanetSideGUID + +object EventServiceTestBase { + case object TestStamp extends EventSystemStamp + + final case class TestMessage(value: Int) extends SelfRespondingEvent + + final case class SupportActorRepliesWith(msg: String, sendTo: ActorRef) extends SelfRespondingEvent + + class TestSupportActor extends Actor { + def receive: Receive = { + case SupportActorRepliesWith(msg, sendTo) => + sendTo ! msg + case _ => () + } + } + + case object TestSupportActorLoader extends EventServiceSupport { + def label: String = "supportActor" + def constructor(context: ActorContext): ActorRef = { + context.actorOf(Props[TestSupportActor](), name = "supportActor") + } + } + + final case class TestSupportEnvelope( + originalChannel: String, + msg: SupportActorRepliesWith + ) extends GenericSupportEnvelope { + def filter: PlanetSideGUID = Service.defaultPlayerGUID + def supportLabel: String = "supportActor" + def supportMessage: Any = msg + } +} diff --git a/src/test/scala/service/local/LocalActionTest.scala b/src/test/scala/service/local/LocalActionTest.scala new file mode 100644 index 000000000..a929bd485 --- /dev/null +++ b/src/test/scala/service/local/LocalActionTest.scala @@ -0,0 +1,73 @@ +// Copyright (c) 2026 PSForever +package service.local + +import net.psforever.objects.{ExplosiveDeployable, GlobalDefinitions} +import net.psforever.packet.game.{ObjectCreateMessage, TriggeredEffect, TriggeredEffectLocation} +import net.psforever.services.Service +import net.psforever.services.base.message.SendResponse +import net.psforever.services.local.LocalAction +import net.psforever.types.{PlanetSideGUID, Vector3} +import org.specs2.mutable.Specification + +class LocalActionTest extends Specification { + "DeployItem" should { + assert(GlobalDefinitions.boomer != null, "missing definition - Boomer does not exist") + + "respond" in { + val obj = new ExplosiveDeployable(GlobalDefinitions.boomer) + obj.GUID = PlanetSideGUID(1) + val msg = LocalAction.DeployItem(obj) + val definition = obj.Definition + val objectData = definition.Packet.ConstructorData(obj).get + msg.response() match { + case SendResponse(packets) => + packets.head match { + case ObjectCreateMessage(_, oid, guid, _, data) => + oid mustEqual definition.ObjectId + guid mustEqual obj.GUID + data mustEqual objectData + case _ => + ko + } + case _ => + ko + } + } + } + + "TriggerEffect" should { + "respond" in { + val msg = LocalAction.TriggerEffect("on", PlanetSideGUID(1)) + msg.response() match { + case LocalAction.TriggerEffectAtLocation(PlanetSideGUID(1), "on", None, None) => + ok + case _ => + ko + } + } + } + + "TriggerEffectInfo" should { + "respond" in { + val msg = LocalAction.TriggerEffectInfo(PlanetSideGUID(1), "on", unk1 = true, unk2 = 10L) + msg.response() match { + case LocalAction.TriggerEffectAtLocation(PlanetSideGUID(1), "on", Some(TriggeredEffect(true, 10L)), None) => + ok + case _ => + ko + } + } + } + + "TriggerEffectLocation" should { + "respond" in { + val msg = LocalAction.TriggerEffectLocation("on", Vector3(1, 2, 3), Vector3(4, 5, 6)) + msg.response() match { + case LocalAction.TriggerEffectAtLocation(Service.defaultPlayerGUID, "on", None, Some(TriggeredEffectLocation(Vector3(1, 2, 3), Vector3(4, 5, 6)))) => + ok + case _ => + ko + } + } + } +} diff --git a/src/test/scala/service/vehicle/VehicleActionTest.scala b/src/test/scala/service/vehicle/VehicleActionTest.scala new file mode 100644 index 000000000..f028ab38a --- /dev/null +++ b/src/test/scala/service/vehicle/VehicleActionTest.scala @@ -0,0 +1,102 @@ +// Copyright (c) 2026 PSForever +package service.vehicle + +import net.psforever.objects.zones.{Zone, ZoneMap} +import net.psforever.objects.{GlobalDefinitions, Tool, Vehicle} +import net.psforever.packet.game.ObjectCreateMessage +import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent +import net.psforever.services.vehicle.VehicleAction +import net.psforever.types.{DriveState, PlanetSideGUID} +import org.specs2.mutable.Specification + +object VehicleActionTest { + assert(GlobalDefinitions.quadstealth != null, "missing definition - quadstealth does not exist") + assert(GlobalDefinitions.ams_respawn_tube != null, "missing definition - ams_respawn_tube does not exist") + assert(GlobalDefinitions.ams != null, "missing definition - AMS does not exist") + + final val testZone: Zone = { + var i = 1 + val notAms = new Vehicle(GlobalDefinitions.quadstealth) + notAms.GUID = PlanetSideGUID(i) + notAms.Health = 1 + i += 1 + val ams = new Vehicle(GlobalDefinitions.ams) + ams.Health = 1 + ams.GUID = PlanetSideGUID(i) + i += 1 + ams.Utilities.values.foreach { utility => + utility.apply().GUID = PlanetSideGUID(i) + i += 1 + } + ams.DeploymentState = DriveState.Deployed + new Zone(id = "test", new ZoneMap( name = "test"), zoneNumber = 1) { + override def Vehicles: List[Vehicle] = List(notAms, ams) + } + } +} + +class VehicleActionTest extends Specification { + import VehicleActionTest._ + + "EquipmentInSlot" should { + assert(GlobalDefinitions.suppressor != null, "missing definition - Suppressor does not exist") + + "respond" in { + var i = 1 + val obj = new Tool(GlobalDefinitions.suppressor) + obj.GUID = PlanetSideGUID(i) + i += 1 + obj.AmmoSlots.map(_.Box).foreach { box => + box.GUID = PlanetSideGUID(i) + i += 1 + } + val msg = VehicleAction.EquipmentInSlot(PlanetSideGUID(1), 2, obj) + val definition = obj.Definition + val objectData = definition.Packet.ConstructorData(obj).get + msg.response() match { + case VehicleAction.EquipmentCreatedInSlot(packets) => + packets match { + case ObjectCreateMessage(_, oid, guid, pdata, data) => + oid mustEqual definition.ObjectId + guid mustEqual obj.GUID + pdata.contains(ObjectCreateMessageParent(PlanetSideGUID(1), 2)) mustEqual true + data mustEqual objectData + case _ => + ko + } + case _ => + ko + } + } + } + + "UpdateAmsSpawnPoint" should { + "respond" in { + testZone.Vehicles.size mustEqual 2 + val msg = VehicleAction.UpdateAmsSpawnPoint(testZone) + msg.response() match { + case VehicleAction.UpdateAmsSpawnList(list) => + list.size mustEqual 1 + list.head.Definition mustEqual GlobalDefinitions.ams_respawn_tube + list.head.Owner.Definition mustEqual GlobalDefinitions.ams + case _ => + ko + } + } + } + + "AMSDeploymentChange" should { + "respond" in { + testZone.Vehicles.size mustEqual 2 + val msg = VehicleAction.AMSDeploymentChange(testZone) + msg.response() match { + case VehicleAction.UpdateAmsSpawnList(list) => + list.size mustEqual 1 + list.head.Definition mustEqual GlobalDefinitions.ams_respawn_tube + list.head.Owner.Definition mustEqual GlobalDefinitions.ams + case _ => + ko + } + } + } +}