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
+ }
+ }
+ }
+}