diff --git a/.codecov.yml b/.codecov.yml index 26736d869..37159d819 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,34 @@ 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/avatar/AvatarResponse.scala" - - "src/main/scala/net/psforever/services/galaxy/GalaxyAction.scala" - - "src/main/scala/net/psforever/services/galaxy/GalaxyResponse.scala" + - "src/main/scala/net/psforever/services/avatar/AvatarService.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/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/local/LocalResponse.scala" - - "src/main/scala/net/psforever/services/vehicle/VehicleAction.scala" - - "src/main/scala/net/psforever/services/vehicle/VehicleResponse.scala" + - "src/main/scala/net/psforever/services/local/LocalAction" + - "src/main/scala/net/psforever/services/local/LocalService" + - "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/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/main/scala/net/psforever/server/Server.scala b/server/src/main/scala/net/psforever/server/Server.scala index 2ec30a503..686e39953 100644 --- a/server/src/main/scala/net/psforever/server/Server.scala +++ b/server/src/main/scala/net/psforever/server/Server.scala @@ -101,7 +101,7 @@ object Server { val zones = Zones.zones :+ Zone.Nowhere val serviceManager = ServiceManager.boot serviceManager ! ServiceManager.Register(classic.Props[AccountIntermediaryService](), "accountIntermediary") - serviceManager ! ServiceManager.Register(classic.Props[GalaxyService](), "galaxy") + serviceManager ! ServiceManager.Register(GalaxyService(), "galaxy") serviceManager ! ServiceManager.Register(classic.Props[SquadService](), "squad") serviceManager ! ServiceManager.Register(classic.Props[AccountPersistenceService](), "accountPersistence") serviceManager ! ServiceManager.Register(classic.Props[PropertyOverrideManager](), "propertyOverrideManager") diff --git a/server/src/test/scala/actor/objects/AutoRepairIntegrationTest.scala b/server/src/test/scala/actor/objects/AutoRepairIntegrationTest.scala index 7742079e0..b1b939c67 100644 --- a/server/src/test/scala/actor/objects/AutoRepairIntegrationTest.scala +++ b/server/src/test/scala/actor/objects/AutoRepairIntegrationTest.scala @@ -32,7 +32,7 @@ import scala.concurrent.duration._ class AutoRepairFacilityIntegrationTest extends FreedContextActorTest { import akka.actor.typed.scaladsl.adapter._ system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id) - ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy") + ServiceManager.boot(system) ! ServiceManager.Register(GalaxyService(), "galaxy") expectNoMessage(1000 milliseconds) val guid = new NumberPoolHub(new MaxNumberSource(max = 10)) val avatarProbe = new TestProbe(system) @@ -105,7 +105,7 @@ class AutoRepairFacilityIntegrationTest extends FreedContextActorTest { class AutoRepairFacilityIntegrationGiveNtuTest extends FreedContextActorTest { import akka.actor.typed.scaladsl.adapter._ system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id) - ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy") + ServiceManager.boot(system) ! ServiceManager.Register(GalaxyService(), "galaxy") expectNoMessage(1000 milliseconds) val guid = new NumberPoolHub(new MaxNumberSource(max = 10)) val avatarProbe = new TestProbe(system) @@ -160,7 +160,7 @@ class AutoRepairFacilityIntegrationGiveNtuTest extends FreedContextActorTest { class AutoRepairFacilityIntegrationAntGiveNtuTest extends FreedContextActorTest { import akka.actor.typed.scaladsl.adapter._ system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id) - ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy") + ServiceManager.boot(system) ! ServiceManager.Register(GalaxyService(), "galaxy") expectNoMessage(1000 milliseconds) var buildingMap = new TrieMap[Int, Building]() val guid = new NumberPoolHub(new MaxNumberSource(max = 10)) @@ -251,7 +251,7 @@ class AutoRepairFacilityIntegrationAntGiveNtuTest extends FreedContextActorTest class AutoRepairFacilityIntegrationTerminalDestroyedTerminalAntTest extends FreedContextActorTest { import akka.actor.typed.scaladsl.adapter._ system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id) - ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy") + ServiceManager.boot(system) ! ServiceManager.Register(GalaxyService(), "galaxy") expectNoMessage(1000 milliseconds) var buildingMap = new TrieMap[Int, Building]() val guid = new NumberPoolHub(new MaxNumberSource(max = 10)) @@ -353,7 +353,7 @@ class AutoRepairFacilityIntegrationTerminalDestroyedTerminalAntTest extends Free class AutoRepairFacilityIntegrationTerminalIncompleteRepairTest extends FreedContextActorTest { import akka.actor.typed.scaladsl.adapter._ system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id) - ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy") + ServiceManager.boot(system) ! ServiceManager.Register(GalaxyService(), "galaxy") expectNoMessage(1000 milliseconds) var buildingMap = new TrieMap[Int, Building]() val guid = new NumberPoolHub(new MaxNumberSource(max = 10)) @@ -469,7 +469,7 @@ class AutoRepairFacilityIntegrationTerminalIncompleteRepairTest extends FreedCon class AutoRepairTowerIntegrationTest extends FreedContextActorTest { import akka.actor.typed.scaladsl.adapter._ system.spawn(InterstellarClusterService(Nil), InterstellarClusterService.InterstellarClusterServiceKey.id) - ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy") + ServiceManager.boot(system) ! ServiceManager.Register(GalaxyService(), "galaxy") expectNoMessage(1000 milliseconds) val guid = new NumberPoolHub(new MaxNumberSource(max = 10)) val avatarProbe = new TestProbe(system) diff --git a/server/src/test/scala/actor/objects/VehicleSpawnPadTest.scala b/server/src/test/scala/actor/objects/VehicleSpawnPadTest.scala index 6ca49eca0..e847d5036 100644 --- a/server/src/test/scala/actor/objects/VehicleSpawnPadTest.scala +++ b/server/src/test/scala/actor/objects/VehicleSpawnPadTest.scala @@ -9,11 +9,12 @@ import net.psforever.objects.serverobject.structures.StructureType import net.psforever.objects.{GlobalDefinitions, Player, Vehicle} import net.psforever.objects.zones.Zone import net.psforever.types.{PlanetSideGUID, _} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.vehicle.VehicleAction import akka.actor.typed.scaladsl.adapter._ import net.psforever.actors.zone.ZoneActor import net.psforever.objects.avatar.Avatar import net.psforever.objects.serverobject.terminals.Terminal +import net.psforever.services.base.envelope.GenericMessageEnvelope import scala.concurrent.duration._ @@ -37,7 +38,7 @@ class VehicleSpawnControl2Test extends ActorTest { pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle, terminal) //order probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ConcealPlayer]) - probe.expectMsgClass(1 minute, classOf[VehicleServiceMessage]) + probe.expectMsgClass(1 minute, classOf[GenericMessageEnvelope]) probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.AttachToRails]) probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.StartPlayerSeatedInVehicle]) vehicle.Seats(0).mount(player) @@ -69,11 +70,11 @@ class VehicleSpawnControl3Test extends ActorTest { pad.Actor ! VehicleSpawnPad.VehicleOrder(player2, vehicle, terminal) //second order (vehicle shared) assert(probe.receiveOne(1 seconds) match { - case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Queue, _) => true - case _ => false + case VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Queue, _) => true + case _ => false }) probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ConcealPlayer]) - probe.expectMsgClass(1 minute, classOf[VehicleServiceMessage]) + probe.expectMsgClass(1 minute, classOf[GenericMessageEnvelope]) probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.AttachToRails]) probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.StartPlayerSeatedInVehicle]) vehicle.Seats(0).mount(player) @@ -82,11 +83,11 @@ class VehicleSpawnControl3Test extends ActorTest { probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ServerVehicleOverrideStart]) probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ServerVehicleOverrideEnd]) assert(probe.receiveOne(1 minute) match { - case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Blocked, _) => true + case VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, _) => true case _ => false }) assert(probe.receiveOne(1 minute) match { - case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Blocked, _) => true + case VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, _) => true case _ => false }) @@ -110,14 +111,6 @@ class VehicleSpawnControl4Test extends ActorTest { player.Continent = "problem" //problem pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle, terminal) //order - - val msg = probe.receiveOne(1 minute) -// assert( -// msg match { -// case VehicleServiceMessage.Decon(RemoverActor.AddTask(v, z, _)) => (v == vehicle) && (z == zone) -// case _ => false -// } -// ) probe.expectNoMessage(5 seconds) } } @@ -133,18 +126,18 @@ class VehicleSpawnControl5Test extends ActorTest() { pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle, terminal) //order probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ConcealPlayer]) - probe.expectMsgClass(1 minute, classOf[VehicleServiceMessage]) + probe.expectMsgClass(1 minute, classOf[GenericMessageEnvelope]) vehicle.Health = 0 //problem probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.AttachToRails]) probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.DetachFromRails]) probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.RevealPlayer]) assert(probe.receiveOne(1 minute) match { - case VehicleServiceMessage(_, VehicleAction.LoadVehicle(_, _, _, _, _)) => true - case _ => false + case GenericMessageEnvelope(_, _, VehicleAction.LoadVehicle(_, _, _, _)) => true + case _ => false }) assert(probe.receiveOne(1 minute) match { - case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Blocked, _) => true - case _ => false + case VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, _) => true + case _ => false }) } } @@ -160,18 +153,18 @@ class VehicleSpawnControl6Test extends ActorTest() { pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle, terminal) //order probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ConcealPlayer]) - probe.expectMsgClass(1 minute, classOf[VehicleServiceMessage]) + probe.expectMsgClass(1 minute, classOf[GenericMessageEnvelope]) player.Die probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.AttachToRails]) probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.DetachFromRails]) probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.RevealPlayer]) assert(probe.receiveOne(1 minute) match { - case VehicleServiceMessage(_, VehicleAction.LoadVehicle(_, _, _, _, _)) => true - case _ => false + case GenericMessageEnvelope(_, _, VehicleAction.LoadVehicle(_, _, _, _)) => true + case _ => false }) assert(probe.receiveOne(1 minute) match { - case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Blocked, _) => true - case _ => false + case VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, _) => true + case _ => false }) } } @@ -188,18 +181,18 @@ class VehicleSpawnControl7Test extends ActorTest { pad.Actor ! VehicleSpawnPad.VehicleOrder(player, vehicle, terminal) //order probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.ConcealPlayer]) - probe.expectMsgClass(1 minute, classOf[VehicleServiceMessage]) + probe.expectMsgClass(1 minute, classOf[GenericMessageEnvelope]) probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.AttachToRails]) probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.StartPlayerSeatedInVehicle]) probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.DetachFromRails]) probe.expectMsgClass(1 minute, classOf[VehicleSpawnPad.RevealPlayer]) assert(probe.receiveOne(1 minute) match { - case VehicleServiceMessage(_, VehicleAction.LoadVehicle(_, _, _, _, _)) => true - case _ => false + case GenericMessageEnvelope(_, _, VehicleAction.LoadVehicle(_, _, _, _)) => true + case _ => false }) assert(probe.receiveOne(1 minute) match { - case VehicleSpawnPad.PeriodicReminder(_, VehicleSpawnPad.Reminders.Blocked, _) => true - case _ => false + case VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, _) => true + case _ => false }) } } diff --git a/server/src/test/scala/actor/service/AvatarServiceTest.scala b/server/src/test/scala/actor/service/AvatarServiceTest.scala deleted file mode 100644 index 6e0eab30d..000000000 --- a/server/src/test/scala/actor/service/AvatarServiceTest.scala +++ /dev/null @@ -1,651 +0,0 @@ -// Copyright (c) 2017 PSForever -package actor.service - -import akka.actor.Props -import akka.testkit.TestProbe -import scala.concurrent.duration._ - -import actor.base.{ActorTest, FreedContextActorTest} -import net.psforever.objects._ -import net.psforever.objects.avatar.Avatar -import net.psforever.objects.guid.NumberPoolHub -import net.psforever.objects.guid.source.MaxNumberSource -import net.psforever.objects.zones.{Zone, ZoneMap} -import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectClass, ObjectCreateMessageParent, PlacementData} -import net.psforever.packet.game.{ObjectCreateMessage, PlayerStateMessageUpstream} -import net.psforever.types._ -import net.psforever.services.{RemoverActor, Service, ServiceManager} -import net.psforever.services.avatar._ - -class AvatarService1Test extends ActorTest { - "AvatarService" should { - "construct" in { - ServiceManager.boot(system) - system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - assert(true) - } - } -} - -class AvatarService2Test extends ActorTest { - "AvatarService" should { - "subscribe" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - assert(true) - } - } -} - -class AvatarService3Test extends ActorTest { - "AvatarService" should { - ServiceManager.boot(system) - "subscribe to a specific channel" in { - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! Service.Leave() - assert(true) - } - } -} - -class AvatarService4Test extends ActorTest { - "AvatarService" should { - "subscribe" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! Service.LeaveAll() - assert(true) - } - } -} - -class AvatarService5Test extends ActorTest { - "AvatarService" should { - "pass an unhandled message" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! "hello" - expectNoMessage() - } - } -} - -class ArmorChangedTest extends ActorTest { - "AvatarService" should { - "pass ArmorChanged" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! AvatarServiceMessage("test", AvatarAction.ArmorChanged(PlanetSideGUID(10), ExoSuitType.Reinforced, 0)) - expectMsg( - AvatarServiceResponse( - "/test/Avatar", - PlanetSideGUID(10), - AvatarResponse.ArmorChanged(ExoSuitType.Reinforced, 0) - ) - ) - } - } -} - -class ConcealPlayerTest extends ActorTest { - "AvatarService" should { - "pass ConcealPlayer" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! AvatarServiceMessage("test", AvatarAction.ConcealPlayer(PlanetSideGUID(10))) - expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ConcealPlayer())) - } - } -} - -class EquipmentInHandTest extends ActorTest { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), "release-test-service") - val toolDef = GlobalDefinitions.beamer - val tool = Tool(toolDef) - tool.GUID = PlanetSideGUID(40) - tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(41) - val pkt = ObjectCreateMessage( - toolDef.ObjectId, - tool.GUID, - ObjectCreateMessageParent(PlanetSideGUID(11), 2), - toolDef.Packet.ConstructorData(tool).get - ) - - "AvatarService" should { - "pass EquipmentInHand" in { - service ! Service.Join("test") - service ! AvatarServiceMessage( - "test", - AvatarAction.EquipmentInHand(PlanetSideGUID(10), PlanetSideGUID(11), 2, tool) - ) - expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentInHand(pkt))) - } - } -} - -class DroptItemTest extends ActorTest { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), "release-test-service") - val toolDef = GlobalDefinitions.beamer - val tool = Tool(toolDef) - tool.Position = Vector3(1, 2, 3) - tool.Orientation = Vector3(4, 5, 6) - tool.GUID = PlanetSideGUID(40) - tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(41) - val pkt = ObjectCreateMessage( - toolDef.ObjectId, - tool.GUID, - DroppedItemData( - PlacementData(tool.Position, tool.Orientation), - toolDef.Packet.ConstructorData(tool).get - ) - ) - - "AvatarService" should { - "pass DropItem" in { - service ! Service.Join("test") - service ! AvatarServiceMessage("test", AvatarAction.DropItem(PlanetSideGUID(10), tool)) - expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.DropItem(pkt))) - } - } -} - -class LoadPlayerTest extends ActorTest { - val obj = Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.VS, CharacterSex.Female, 1, CharacterVoice.Voice1)) - obj.GUID = PlanetSideGUID(10) - obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(11) - val c1data = obj.Definition.Packet.DetailedConstructorData(obj).get - val pkt1 = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(10), c1data) - val parent = ObjectCreateMessageParent(PlanetSideGUID(12), 0) - obj.VehicleSeated = PlanetSideGUID(12) - val c2data = obj.Definition.Packet.DetailedConstructorData(obj).get - val pkt2 = ObjectCreateMessage(ObjectClass.avatar, PlanetSideGUID(10), parent, c2data) - - "AvatarService" should { - "pass LoadPlayer" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - //no parent data - service ! AvatarServiceMessage( - "test", - AvatarAction.LoadPlayer(PlanetSideGUID(20), ObjectClass.avatar, PlanetSideGUID(10), c1data, None) - ) - expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(20), AvatarResponse.LoadPlayer(pkt1))) - //parent data - service ! AvatarServiceMessage( - "test", - AvatarAction.LoadPlayer(PlanetSideGUID(20), ObjectClass.avatar, PlanetSideGUID(10), c2data, Some(parent)) - ) - expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(20), AvatarResponse.LoadPlayer(pkt2))) - } - } -} - -class ObjectDeleteTest extends ActorTest { - "AvatarService" should { - "pass ObjectDelete" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! AvatarServiceMessage("test", AvatarAction.ObjectDelete(PlanetSideGUID(10), PlanetSideGUID(11))) - expectMsg( - AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ObjectDelete(PlanetSideGUID(11), 0)) - ) - - service ! AvatarServiceMessage("test", AvatarAction.ObjectDelete(PlanetSideGUID(10), PlanetSideGUID(11), 55)) - expectMsg( - AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ObjectDelete(PlanetSideGUID(11), 55)) - ) - } - } -} - -class ObjectHeldTest extends ActorTest { - "AvatarService" should { - "pass ObjectHeld" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! AvatarServiceMessage("test", AvatarAction.ObjectHeld(PlanetSideGUID(10), 1, 2)) - expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ObjectHeld(1, 2))) - } - } -} - -class PutDownFDUTest extends ActorTest { - "AvatarService" should { - "pass PutDownFDU" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! AvatarServiceMessage("test", AvatarAction.PutDownFDU(PlanetSideGUID(10))) - expectMsg( - AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.PutDownFDU(PlanetSideGUID(10))) - ) - } - } -} - -class PlanetsideAttributeTest extends ActorTest { - "AvatarService" should { - "pass PlanetsideAttribute" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! AvatarServiceMessage("test", AvatarAction.PlanetsideAttribute(PlanetSideGUID(10), 5, 1200L)) - expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.PlanetsideAttribute(5, 1200L))) - } - } -} - -class PlayerStateTest extends ActorTest { - val msg = PlayerStateMessageUpstream( - PlanetSideGUID(75), - Vector3(3694.1094f, 2735.4531f, 90.84375f), - Some(Vector3(4.375f, 2.59375f, 0.0f)), - 61.875f, - 351.5625f, - 0.0f, - 136, - 0, - false, - false, - false, - false, - 112, - 0 - ) - - "AvatarService" should { - "pass PlayerState" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! AvatarServiceMessage( - "test", - AvatarAction.PlayerState( - PlanetSideGUID(10), - Vector3(3694.1094f, 2735.4531f, 90.84375f), - Some(Vector3(4.375f, 2.59375f, 0.0f)), - 61.875f, - 351.5625f, - 0.0f, - 136, - false, - false, - false, - false, - false, - false - ) - ) - expectMsg( - AvatarServiceResponse( - "/test/Avatar", - PlanetSideGUID(10), - AvatarResponse.PlayerState( - Vector3(3694.1094f, 2735.4531f, 90.84375f), - Some(Vector3(4.375f, 2.59375f, 0.0f)), - 61.875f, - 351.5625f, - 0.0f, - 136, - false, - false, - false, - false, - false, - false - ) - ) - ) - } - } -} - -class PickupItemTest extends ActorTest { - val obj = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.VS, CharacterSex.Female, 1, CharacterVoice.Voice1)) - val tool = Tool(GlobalDefinitions.beamer) - tool.GUID = PlanetSideGUID(40) - - "pass PickUpItem" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! AvatarServiceMessage("test", AvatarAction.PickupItem(PlanetSideGUID(10), tool)) - expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ObjectDelete(tool.GUID, 0))) - } -} - -class ReloadTest extends ActorTest { - "AvatarService" should { - "pass Reload" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! AvatarServiceMessage("test", AvatarAction.Reload(PlanetSideGUID(10), PlanetSideGUID(40))) - expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.Reload(PlanetSideGUID(40)))) - } - } -} - -class ChangeAmmoTest extends ActorTest { - val ammoDef = GlobalDefinitions.energy_cell - val ammoBox = AmmoBox(ammoDef) - - "AvatarService" should { - "pass ChangeAmmo" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! AvatarServiceMessage( - "test", - AvatarAction.ChangeAmmo( - PlanetSideGUID(10), - PlanetSideGUID(40), - 0, - PlanetSideGUID(40), - ammoDef.ObjectId, - PlanetSideGUID(41), - ammoDef.Packet.ConstructorData(ammoBox).get - ) - ) - expectMsg( - AvatarServiceResponse( - "/test/Avatar", - PlanetSideGUID(10), - AvatarResponse.ChangeAmmo( - PlanetSideGUID(40), - 0, - PlanetSideGUID(40), - ammoDef.ObjectId, - PlanetSideGUID(41), - ammoDef.Packet.ConstructorData(ammoBox).get - ) - ) - ) - } - } -} - -class ChangeFireModeTest extends ActorTest { - val ammoDef = GlobalDefinitions.energy_cell - val ammoBox = AmmoBox(ammoDef) - - "AvatarService" should { - "pass ChangeFireMode" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! AvatarServiceMessage("test", AvatarAction.ChangeFireMode(PlanetSideGUID(10), PlanetSideGUID(40), 0)) - expectMsg( - AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ChangeFireMode(PlanetSideGUID(40), 0)) - ) - } - } -} - -class ChangeFireStateStartTest extends ActorTest { - "AvatarService" should { - "pass ChangeFireState_Start" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! AvatarServiceMessage("test", AvatarAction.ChangeFireState_Start(PlanetSideGUID(10), PlanetSideGUID(40))) - expectMsg( - AvatarServiceResponse( - "/test/Avatar", - PlanetSideGUID(10), - AvatarResponse.ChangeFireState_Start(PlanetSideGUID(40)) - ) - ) - } - } -} - -class ChangeFireStateStopTest extends ActorTest { - "AvatarService" should { - "pass ChangeFireState_Stop" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! AvatarServiceMessage("test", AvatarAction.ChangeFireState_Stop(PlanetSideGUID(10), PlanetSideGUID(40))) - expectMsg( - AvatarServiceResponse( - "/test/Avatar", - PlanetSideGUID(10), - AvatarResponse.ChangeFireState_Stop(PlanetSideGUID(40)) - ) - ) - } - } -} - -class WeaponDryFireTest extends ActorTest { - "AvatarService" should { - "pass WeaponDryFire" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! AvatarServiceMessage("test", AvatarAction.WeaponDryFire(PlanetSideGUID(10), PlanetSideGUID(40))) - expectMsg( - AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.WeaponDryFire(PlanetSideGUID(40))) - ) - } - } -} - -class AvatarStowEquipmentTest extends ActorTest { - val tool = Tool(GlobalDefinitions.beamer) - - "AvatarService" should { - "pass StowEquipment" in { - ServiceManager.boot(system) - val service = system.actorOf(Props(classOf[AvatarService], Zone.Nowhere), AvatarServiceTest.TestName) - service ! Service.Join("test") - service ! AvatarServiceMessage( - "test", - AvatarAction.StowEquipment(PlanetSideGUID(10), PlanetSideGUID(11), 2, tool) - ) - expectMsg( - AvatarServiceResponse( - "/test/Avatar", - PlanetSideGUID(10), - AvatarResponse.StowEquipment(PlanetSideGUID(11), 2, tool) - ) - ) - } - } -} - -/* -Preparation for these three Release tests is involved. -The ServiceManager must be set up correctly. -The Zone needs to be set up and initialized properly with a ZoneActor. -The ZoneActor builds the GUID Actor and the ZonePopulationActor. - -ALL of these Actors will talk to each other. -The lines of communication can short circuit if the next Actor does not have the correct information. -Putting Actor startup in the main class, outside of the body of the test, helps. -Frequent pauses to allow everything to sort their messages also helps. -Even with all this work, the tests have a high chance of failure just due to being asynchronous. - */ -class AvatarReleaseTest extends FreedContextActorTest { - val guid: NumberPoolHub = new NumberPoolHub(new MaxNumberSource(15)) - val zone = new Zone("test", new ZoneMap("test-map"), 0) { - override def SetupNumberPools() : Unit = { } - GUID(guid) - } - zone.init(context) - val obj = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.VS, CharacterSex.Female, 1, CharacterVoice.Voice1)) - guid.register(obj) - guid.register(obj.Slot(5).Equipment.get) - obj.Zone = zone - obj.Release - val subscriber = new TestProbe(system) - - "AvatarService" should { - "pass Release" in { - expectNoMessage(100 milliseconds) //spacer - - zone.AvatarEvents.tell(Service.Join("test"), subscriber.ref) - assert(zone.Corpses.isEmpty) - zone.Population ! Zone.Corpse.Add(obj) - subscriber.expectNoMessage(200 milliseconds) //spacer - - assert(zone.Corpses.size == 1) - assert(obj.HasGUID) - val guid = obj.GUID - zone.AvatarEvents ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone, Some(1 second))) //alive for one second - - val reply1 = subscriber.receiveOne(200 milliseconds) - assert(reply1.isInstanceOf[AvatarServiceResponse]) - val reply1msg = reply1.asInstanceOf[AvatarServiceResponse] - assert(reply1msg.channel == "/test/Avatar") - assert(reply1msg.avatar_guid == guid) - assert(reply1msg.replyMessage.isInstanceOf[AvatarResponse.Release]) - assert(reply1msg.replyMessage.asInstanceOf[AvatarResponse.Release].player == obj) - - val reply2 = subscriber.receiveOne(2 seconds) - assert(reply2.isInstanceOf[AvatarServiceResponse]) - val reply2msg = reply2.asInstanceOf[AvatarServiceResponse] - assert(reply2msg.channel.equals("/test/Avatar")) - assert(reply2msg.avatar_guid == Service.defaultPlayerGUID) - assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete]) - assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid) - - subscriber.expectNoMessage(1 seconds) - assert(zone.Corpses.isEmpty) - assert(!obj.HasGUID) - } - } -} - -class AvatarReleaseEarly1Test extends FreedContextActorTest { - val guid: NumberPoolHub = new NumberPoolHub(new MaxNumberSource(15)) - val zone = new Zone("test", new ZoneMap("test-map"), 0) { - override def SetupNumberPools() : Unit = { } - GUID(guid) - } - zone.init(context) - val obj = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.VS, CharacterSex.Female, 1, CharacterVoice.Voice1)) - guid.register(obj) - guid.register(obj.Slot(5).Equipment.get) - obj.Zone = zone - obj.Release - val subscriber = new TestProbe(system) - - "AvatarService" should { - "pass Release" in { - expectNoMessage(100 milliseconds) //spacer - - zone.AvatarEvents.tell(Service.Join("test"), subscriber.ref) - assert(zone.Corpses.isEmpty) - zone.Population ! Zone.Corpse.Add(obj) - subscriber.expectNoMessage(200 milliseconds) //spacer - - assert(zone.Corpses.size == 1) - assert(obj.HasGUID) - val guid = obj.GUID - zone.AvatarEvents ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone)) //3+ minutes! - - val reply1 = subscriber.receiveOne(200 milliseconds) - assert(reply1.isInstanceOf[AvatarServiceResponse]) - val reply1msg = reply1.asInstanceOf[AvatarServiceResponse] - assert(reply1msg.channel == "/test/Avatar") - assert(reply1msg.avatar_guid == guid) - assert(reply1msg.replyMessage.isInstanceOf[AvatarResponse.Release]) - assert(reply1msg.replyMessage.asInstanceOf[AvatarResponse.Release].player == obj) - - zone.AvatarEvents ! AvatarServiceMessage.Corpse(RemoverActor.HurrySpecific(List(obj), zone)) //IMPORTANT: ONE ENTRY - val reply2 = subscriber.receiveOne(200 milliseconds) - assert(reply2.isInstanceOf[AvatarServiceResponse]) - val reply2msg = reply2.asInstanceOf[AvatarServiceResponse] - assert(reply2msg.channel.equals("/test/Avatar")) - assert(reply2msg.avatar_guid == Service.defaultPlayerGUID) - assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete]) - assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid) - - subscriber.expectNoMessage(1 seconds) - assert(zone.Corpses.isEmpty) - assert(!obj.HasGUID) - } - } -} - -class AvatarReleaseEarly2Test extends FreedContextActorTest { - val guid: NumberPoolHub = new NumberPoolHub(new MaxNumberSource(15)) - val zone = new Zone("test", new ZoneMap("test-map"), 0) { - override def SetupNumberPools() : Unit = { } - GUID(guid) - } - zone.init(context) - val obj = Player(Avatar(0, "TestCharacter", PlanetSideEmpire.VS, CharacterSex.Female, 1, CharacterVoice.Voice1)) - guid.register(obj) - guid.register(obj.Slot(5).Equipment.get) - obj.Zone = zone - obj.Release - val objAlt = Player( - Avatar(0, "TestCharacter2", PlanetSideEmpire.NC, CharacterSex.Male, 1, CharacterVoice.Voice1) - ) //necessary clutter - objAlt.GUID = PlanetSideGUID(3) - objAlt.Slot(5).Equipment.get.GUID = PlanetSideGUID(4) - objAlt.Zone = zone - val subscriber = new TestProbe(system) - - "AvatarService" should { - "pass Release" in { - expectNoMessage(100 milliseconds) //spacer - - zone.AvatarEvents.tell(Service.Join("test"), subscriber.ref) - assert(zone.Corpses.isEmpty) - zone.Population ! Zone.Corpse.Add(obj) - subscriber.expectNoMessage(200 milliseconds) //spacer - - assert(zone.Corpses.size == 1) - assert(obj.HasGUID) - val guid = obj.GUID - zone.AvatarEvents ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone)) //3+ minutes! - - val reply1 = subscriber.receiveOne(200 milliseconds) - assert(reply1.isInstanceOf[AvatarServiceResponse]) - val reply1msg = reply1.asInstanceOf[AvatarServiceResponse] - assert(reply1msg.channel == "/test/Avatar") - assert(reply1msg.avatar_guid == guid) - assert(reply1msg.replyMessage.isInstanceOf[AvatarResponse.Release]) - assert(reply1msg.replyMessage.asInstanceOf[AvatarResponse.Release].player == obj) - - zone.AvatarEvents ! AvatarServiceMessage.Corpse( - RemoverActor.HurrySpecific(List(objAlt, obj), zone) - ) //IMPORTANT: TWO ENTRIES - val reply2 = subscriber.receiveOne(100 milliseconds) - assert(reply2.isInstanceOf[AvatarServiceResponse]) - val reply2msg = reply2.asInstanceOf[AvatarServiceResponse] - assert(reply2msg.channel.equals("/test/Avatar")) - assert(reply2msg.avatar_guid == Service.defaultPlayerGUID) - assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete]) - assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid) - - subscriber.expectNoMessage(1 seconds) - assert(zone.Corpses.isEmpty) - assert(!obj.HasGUID) - } - } -} - -object AvatarServiceTest { - import java.util.concurrent.atomic.AtomicInteger - private val number = new AtomicInteger(1) - - def TestName: String = { - s"service${number.getAndIncrement()}" - } -} diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 86ac54b7f..765c800fe 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -575,6 +575,17 @@ network { # before it is dropped. Can be used as a watchdog for hung server sessions. outbound-grace-time = 1 minute } + + event-caching { + # The minimum amount of time a cached message has to wait before being dispatched. (ms) + # Ideal circumstances are when message traffic is light. + flush-cache-delay = 15 + # The absolute maximum amount of time a cached message has to wait before being dispatched. (ms) + flush-cache-max-delay = 50 + # The numeric difference between light traffic and heavy traffic. + # In this case, the event system's traffic density and the message caching delay are numerically synonymous. + message-traffic-threshold = 12 + } } development { diff --git a/src/main/scala/net/psforever/actors/session/AvatarActor.scala b/src/main/scala/net/psforever/actors/session/AvatarActor.scala index 2b72d42ff..87c59aa42 100644 --- a/src/main/scala/net/psforever/actors/session/AvatarActor.scala +++ b/src/main/scala/net/psforever/actors/session/AvatarActor.scala @@ -12,7 +12,8 @@ import net.psforever.objects.avatar.ModePermissions import net.psforever.objects.avatar.scoring.{Assist, Death, EquipmentStat, KDAStat, Kill, Life, ScoreCard, SupportActivity} import net.psforever.objects.sourcing.{TurretSource, VehicleSource} import net.psforever.packet.game.ImplantAction -import net.psforever.services.avatar.AvatarServiceResponse +import net.psforever.services.avatar.AvatarStamp +import net.psforever.services.base.envelope.GenericResponseEnvelope import net.psforever.types.{ChatMessageType, StatisticalCategory, StatisticalElement} import net.psforever.zones.Zones import org.joda.time.{LocalDateTime, Seconds} @@ -49,8 +50,9 @@ import net.psforever.objects.vital.{DamagingActivity, HealFromImplant, HealingAc import net.psforever.packet.game.objectcreate.{BasicCharacterData, ObjectClass, RibbonBars} import net.psforever.packet.game.{Friend => GameFriend, _} import net.psforever.persistence -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{SendResponse, PlanetsideAttribute} +import net.psforever.services.avatar.AvatarAction import net.psforever.types.{ CharacterSex, CharacterVoice, @@ -585,9 +587,10 @@ object AvatarActor { def displayLookingForSquad(session: Session, state: Int): Unit = { val player = session.player - session.zone.AvatarEvents ! AvatarServiceMessage( + session.zone.AvatarEvents ! MessageEnvelope( player.Faction.toString, - AvatarAction.PlanetsideAttribute(player.GUID, 53, state) + player.GUID, + PlanetsideAttribute(player.GUID, 53, state) ) } @@ -1383,9 +1386,10 @@ class AvatarActor( case SetLookingForSquad(lfs) => avatarCopy(avatar.copy(lookingForSquad = lfs)) sessionActor ! SessionActor.SendResponse(PlanetsideAttributeMessage(session.get.player.GUID, 53, 0)) - session.get.zone.AvatarEvents ! AvatarServiceMessage( + session.get.zone.AvatarEvents ! MessageEnvelope( avatar.faction.toString, - AvatarAction.PlanetsideAttribute(session.get.player.GUID, 53, if (lfs) 1 else 0) + session.get.player.GUID, + PlanetsideAttribute(session.get.player.GUID, 53, if (lfs) 1 else 0) ) Behaviors.same @@ -1794,9 +1798,9 @@ class AvatarActor( ) val player = session.get.player val zone = player.Zone - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.SendResponse(Service.defaultPlayerGUID, DisplayedAwardMessage(player.GUID, ribbon, bar)) + SendResponse(DisplayedAwardMessage(player.GUID, ribbon, bar)) ) Behaviors.same @@ -2073,9 +2077,9 @@ class AvatarActor( case Success(_) => val zone = session.get.zone avatarCopy(avatar.copy(decoration = avatar.decoration.copy(cosmetics = Some(cosmetics)))) - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.PlanetsideAttributeToAll( + PlanetsideAttribute( session.get.player.GUID, 106, Cosmetic.valuesToAttributeValue(cosmetics) @@ -3000,13 +3004,13 @@ class AvatarActor( val next = BattleRank.withExperience(newBep).value val br24 = BattleRank.BR24.value sessionActor ! SessionActor.SendResponse(BattleExperienceMessage(pguid, newBep, localModifier)) - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(pguid, 17, newBep)) + events ! MessageEnvelope(zoneId, PlanetsideAttribute(pguid, 17, newBep)) if (current < br24 && next >= br24 || current >= br24 && next < br24) { setCosmetics(Set()).onComplete { _ => val evts = events val name = player.Name val guid = pguid - evts ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(guid, 106, 1)) //set to no helmet + evts ! MessageEnvelope(name, PlanetsideAttribute(guid, 106, 1)) //set to no helmet } } // when the level is reduced, take away any implants over the implant slot limit @@ -3051,10 +3055,7 @@ class AvatarActor( val sess = session.get val zone = sess.zone avatar = avatar.copy(cep = cep) - zone.AvatarEvents ! AvatarServiceMessage( - zone.id, - AvatarAction.PlanetsideAttributeToAll(sess.player.GUID, 18, cep) - ) + zone.AvatarEvents ! MessageEnvelope(zone.id, PlanetsideAttribute(sess.player.GUID, 18, cep)) case Failure(exception) => log.error(exception)("db failure") } @@ -3152,7 +3153,7 @@ class AvatarActor( if (exp > 0L) { setBep(exp, msg) zone.actor ! ZoneActor.RewardOurSupporters(playerSource, historyTranscript, killStat, exp) - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( player.Name, AvatarAction.ShareKillExperienceWithSquad(player, exp)) } } @@ -3163,12 +3164,9 @@ class AvatarActor( val _session = session.get val zone = _session.zone val player = _session.player - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( player.Name, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - AvatarStatisticsMessage(DeathStatistic(ScoreCard.deathCount(avatar.scorecard))) - ) + SendResponse(AvatarStatisticsMessage(DeathStatistic(ScoreCard.deathCount(avatar.scorecard)))) ) } @@ -3494,8 +3492,8 @@ class AvatarActor( value: Int ): Unit = { import akka.actor.typed.scaladsl.adapter.TypedActorRefOps - import net.psforever.services.avatar.{AvatarResponse => RESP} - sessionActor.toClassic ! AvatarServiceResponse("", guid, RESP.AvatarImplant(action, index, value)) + val resp = AvatarAction.AvatarImplant(action, index, value) + sessionActor.toClassic ! GenericResponseEnvelope(AvatarStamp, "", guid, resp) } private def buyImplantAction( @@ -3654,9 +3652,9 @@ class AvatarActor( // Start client-side initialization timer, visible on the character screen // Progress accumulates according to the client's knowledge of the implant initialization time // What is normally a 60s timer that is set to 120s on the server will still visually update as if 60s - session.get.zone.AvatarEvents ! AvatarServiceMessage( + session.get.zone.AvatarEvents ! MessageEnvelope( avatar.name, - AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, actionProgress)) + SendResponse(ActionProgressMessage(slot + 6, actionProgress)) ) implant.copy(initialized = false, active = false, timer = futureDelay) } else { @@ -3703,9 +3701,9 @@ class AvatarActor( def stopImplantInitializationTimer(implant: Implant, slot: Int): Implant = { cancelImplantInitializedTimer(slot) //can not formally stop the initialization time on the character information window; set it to 100 to make it look blank - session.get.zone.AvatarEvents ! AvatarServiceMessage( + session.get.zone.AvatarEvents ! MessageEnvelope( avatar.name, - AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, 100)) + SendResponse(ActionProgressMessage(slot + 6, 100)) ) implant.copy(initialized = false, active = false, timer = 0L) } @@ -3773,9 +3771,10 @@ class AvatarActor( private def deactivateImplant(implant: Implant, slot: Int): Implant = { cancelImplantInitializedTimer(slot) // Deactivation sound / effect - session.get.zone.AvatarEvents ! AvatarServiceMessage( + session.get.zone.AvatarEvents ! MessageEnvelope( session.get.zone.id, - AvatarAction.PlanetsideAttribute(session.get.player.GUID, 28, implant.definition.implantType.value * 2) + session.get.player.GUID, + PlanetsideAttribute(session.get.player.GUID, 28, implant.definition.implantType.value * 2) ) sendAvatarImplantMessageToSelf(session.get.player.GUID, ImplantAction.Activation, slot, value = 0) implant.copy(active = false) @@ -3852,10 +3851,7 @@ class AvatarActor( val newHealth = player.Health = originalHealth + 1 val events = zone.AvatarEvents player.LogActivity(HealFromImplant(implant.definition.implantType, 1)) - events ! AvatarServiceMessage( - zone.id, - AvatarAction.PlanetsideAttributeToAll(guid, 0, newHealth) - ) + events ! MessageEnvelope(zone.id, PlanetsideAttribute(guid, 0, newHealth)) false } else { !aliveAndWounded @@ -3867,9 +3863,10 @@ class AvatarActor( // Activation sound / effect val sess = session.get val zone = sess.zone - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.PlanetsideAttribute( + sess.player.GUID, + PlanetsideAttribute( sess.player.GUID, 28, implant.definition.implantType.value * 2 + 1 @@ -3892,9 +3889,9 @@ class AvatarActor( case (implantOpt @ Some(implant), slot) => //update ongoing progress val actionProgress = calculateImplantTimerStats(implant, AvatarActor.initializationTime(implant))._3 - session.get.zone.AvatarEvents ! AvatarServiceMessage( + session.get.zone.AvatarEvents ! MessageEnvelope( avatar.name, - AvatarAction.SendResponse(Service.defaultPlayerGUID, ActionProgressMessage(slot + 6, actionProgress)) + SendResponse(ActionProgressMessage(slot + 6, actionProgress)) ) implantOpt case (None, _) => diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala index aaec38df4..29bee0368 100644 --- a/src/main/scala/net/psforever/actors/session/SessionActor.scala +++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala @@ -3,7 +3,7 @@ package net.psforever.actors.session import akka.actor.{Actor, ActorRef, Cancellable, MDCContextAware, typed} import net.psforever.actors.session.normal.NormalMode -import net.psforever.actors.session.support.ZoningOperations +import net.psforever.actors.session.support.{CommonHandlerFunctions, CommonHandlerLogic, ZoningOperations} import net.psforever.objects.TurretDeployable import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.containable.Containable @@ -18,12 +18,13 @@ import net.psforever.services.CavernRotationService import net.psforever.services.CavernRotationService.SendCavernRotationUpdates import net.psforever.services.ServiceManager.LookupResult import net.psforever.services.account.{PlayerToken, ReceiveAccountData} -import net.psforever.services.avatar.AvatarServiceResponse +import net.psforever.services.avatar.AvatarStamp +import net.psforever.services.base.envelope.{GenericResponseEnvelope, Undelivered} import net.psforever.services.chat.ChatService -import net.psforever.services.galaxy.GalaxyServiceResponse -import net.psforever.services.local.LocalServiceResponse +import net.psforever.services.galaxy.GalaxyStamp +import net.psforever.services.local.LocalStamp import net.psforever.services.teamwork.SquadServiceResponse -import net.psforever.services.vehicle.VehicleServiceResponse +import net.psforever.services.vehicle.VehicleStamp import org.joda.time.LocalDateTime import org.log4s.MDC @@ -94,6 +95,26 @@ object SessionActor { private final case object PokeClient extends Command final case class SetMode(mode: PlayerMode) extends Command + + /** + * Determine if a response handler would process a given reply message, ignoring its guards. + * Treat handlers already ignoring their guards as "determined (will not pass)". + * @see `CommonHandlerFunctions.IgnoreFilter_=` + * @param reply message + * @param handler message handler + * @return `true`, if the response handler will process the response; + * `false`, if the handler is skipped or if it would not process + */ + private def HandlerAcceptingMessageTest(reply: Any)(handler: CommonHandlerFunctions): Boolean = { + if (handler.IgnoreFilter) { + false + } else { + handler.IgnoreFilter = true + val result = handler.isDefinedAt(reply) + handler.IgnoreFilter = false + result + } + } } class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], connectionId: String, sessionId: Long) @@ -106,6 +127,9 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con private[this] val data = new SessionData(middlewareActor, context) private[this] var mode: PlayerMode = NormalMode private[this] var logic: ModeLogic = _ + private[this] var listOfHandlers: Seq[CommonHandlerFunctions] = List.empty + + private val commonHandlerLogic: CommonHandlerLogic = new CommonHandlerLogic(data, context) override def postStop(): Unit = { clientKeepAlive.cancel() @@ -119,7 +143,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con buffer.addOne(msg) case _ if data.whenAllEventBusesLoaded() => context.become(inTheGame) - logic = mode.setup(data) + changeModeSetup(mode) buffer.foreach { self.tell(_, self) } //we forget the original sender, shouldn't be doing callbacks at this point buffer.clear() case _ => () @@ -160,37 +184,39 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con if (mode != newMode) { logic.switchFrom(data.session) mode = newMode - logic = newMode.setup(data) + changeModeSetup(newMode) } logic.switchTo(data.session) } + private def changeModeSetup(newMode: PlayerMode): Unit = { + logic = newMode.setup(data) + listOfHandlers = List( + logic.avatarResponse, + logic.local, + logic.vehicleResponse, + logic.galaxy, + commonHandlerLogic + ) + } + private def parse(sender: ActorRef): Receive = { /* really common messages (very frequently, every life) */ case packet: PlanetSideGamePacket => handleGamePkt(packet) - case AvatarServiceResponse(toChannel, guid, reply) => - logic.avatarResponse.handle(toChannel, guid, reply) + case SquadServiceResponse(_, excluded, reply) => + logic.squad.handle(reply, excluded) - case GalaxyServiceResponse(_, reply) => - logic.galaxy.handle(reply) - - case LocalServiceResponse(toChannel, guid, reply) => - logic.local.handle(toChannel, guid, reply) + case envelope: GenericResponseEnvelope => + handleGenericResponseEnvelope(envelope) case Mountable.MountMessages(tplayer, reply) => logic.mountResponse.handle(tplayer, reply) - case SquadServiceResponse(_, excluded, response) => - logic.squad.handle(response, excluded) - case Terminal.TerminalMessage(tplayer, msg, order) => logic.terminals.handle(tplayer, msg, order) - case VehicleServiceResponse(toChannel, guid, reply) => - logic.vehicleResponse.handle(toChannel, guid, reply) - case ChatService.MessageResponse(fromSession, message, _) => logic.chat.handleIncomingMessage(message, fromSession) @@ -369,6 +395,52 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con logic.general.handleReceiveDefaultMessage(default, sender) } + private def handleGenericResponseEnvelope(envelope: GenericResponseEnvelope): Unit = { + //try use the stamp to match the specific handler + envelope.stamp match { + case Undelivered => + val GenericResponseEnvelope(_, _, reply) = envelope + log.error(s"received a message's response that was not processed by an event system - $reply") + case AvatarStamp => + handleEnvelopeWithResponseHandler(logic.avatarResponse, envelope) + case LocalStamp => + handleEnvelopeWithResponseHandler(logic.local, envelope) + case VehicleStamp => + handleEnvelopeWithResponseHandler(logic.vehicleResponse, envelope) + case GalaxyStamp => + handleEnvelopeWithResponseHandler(logic.galaxy, envelope) + case unknownStamp => + log.error(s"received a message from an unknown event system - reply: $envelope, stamp: $unknownStamp") + } + //println(s"event-system-rtt: ${System.currentTimeMillis() - envelope.time} ms") + } + + private def handleEnvelopeWithResponseHandler( + responseHandler: CommonHandlerFunctions, + envelope: GenericResponseEnvelope + ): Unit = { + val GenericResponseEnvelope(toChannel, guid, reply) = envelope + //try the expected handler with the input response + if (!responseHandler.handle(toChannel, guid, reply)) { + //test the expected handler again, ignoring guard booleans; if it would have been handled, stop with this + responseHandler.IgnoreFilter = true + if (!responseHandler.isDefinedAt(reply)) { + //find every handler that might accept the input response, ignoring guard booleans only for the search + //try each discovered handler against the input response until one works + val test: CommonHandlerFunctions => Boolean = SessionActor.HandlerAcceptingMessageTest(reply) + listOfHandlers.filter(test) match { + case Nil => + log.error(s"received completely unhandled response message - $reply for ${envelope.stamp}:$toChannel") + case first :: Nil => + first.tryToHandle(reply) + case first :: others => + first.tryToHandle(reply) || others.exists(_.tryToHandle(reply)) + } + } + responseHandler.IgnoreFilter = false + } + } + private def handleGamePkt: PlanetSideGamePacket => Unit = { case packet: ConnectToWorldRequestMessage => logic.general.handleConnectToWorldRequest(packet) diff --git a/src/main/scala/net/psforever/actors/session/csr/AvatarHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/csr/AvatarHandlerLogic.scala index 764a6c89e..f1750d6c8 100644 --- a/src/main/scala/net/psforever/actors/session/csr/AvatarHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/csr/AvatarHandlerLogic.scala @@ -1,6 +1,7 @@ // Copyright (c) 2024 PSForever package net.psforever.actors.session.csr +import akka.actor.Actor.Receive import akka.actor.{ActorContext, typed} import net.psforever.actors.session.SessionActor import net.psforever.actors.session.normal.NormalMode @@ -12,7 +13,9 @@ import net.psforever.objects.serverobject.containable.ContainableBehavior import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.vital.RevivingActivity import net.psforever.packet.game.{AvatarImplantMessage, CreateShortcutMessage, ImplantAction} -import net.psforever.services.avatar.AvatarServiceResponse +import net.psforever.services.avatar.{AvatarAction, AvatarStamp} +import net.psforever.services.base.envelope.GenericResponseEnvelope +import net.psforever.services.base.message.{ChangeAmmo, ChangeFireState_Start, ChangeFireState_Stop, ReloadTool, WeaponDryFire} import net.psforever.types.ImplantType // @@ -25,9 +28,7 @@ import net.psforever.objects.inventory.InventoryItem import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal} import net.psforever.objects.zones.Zoning import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent -import net.psforever.packet.game.{ArmorChangedMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChatMsg, DestroyMessage, DrowningTarget, GenericActionMessage, GenericObjectActionMessage, HitHint, ItemTransactionResultMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectHeldMessage, OxygenStateMessage, PlanetsideAttributeMessage, PlayerStateMessage, ProjectileStateMessage, ReloadMessage, SetEmpireMessage, UseItemMessage, WeaponDryFireMessage} -import net.psforever.services.avatar.AvatarResponse -import net.psforever.services.Service +import net.psforever.packet.game.{ArmorChangedMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChatMsg, DestroyMessage, DrowningTarget, GenericActionMessage, GenericObjectActionMessage, ItemTransactionResultMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectHeldMessage, OxygenStateMessage, PlanetsideAttributeMessage, PlayerStateMessage, ProjectileStateMessage, ReloadMessage, UseItemMessage, WeaponDryFireMessage} import net.psforever.types.{ChatMessageType, PlanetSideGUID, TransactionType, Vector3} import net.psforever.util.Config @@ -42,547 +43,496 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor - /** - * na - * @param toChannel na - * @param guid na - * @param reply na - */ - def handle(toChannel: String, guid: PlanetSideGUID, reply: AvatarResponse.Response): Unit = { - val resolvedPlayerGuid = if (player != null && player.HasGUID) { - player.GUID - } else { - Service.defaultPlayerGUID - } - val isNotSameTarget = resolvedPlayerGuid != guid - val isSameTarget = !isNotSameTarget - reply match { - /* special messages */ - case AvatarResponse.TeardownConnection() if player.spectator => - context.self ! SessionActor.SetMode(CustomerServiceRepresentativeMode) - context.self.forward(AvatarServiceResponse(toChannel, guid, reply)) + def receive: Receive = { + /* special messages */ + case AvatarAction.TeardownConnection + if TestFilter(() => { player.spectator }) => + context.self ! SessionActor.SetMode(CustomerServiceRepresentativeMode) + context.self.forward(GenericResponseEnvelope(AvatarStamp, "", FilterGuid, AvatarAction.TeardownConnection)) - case AvatarResponse.TeardownConnection() => - context.self ! SessionActor.SetMode(NormalMode) - context.self.forward(AvatarServiceResponse(toChannel, guid, reply)) + case AvatarAction.TeardownConnection => + context.self ! SessionActor.SetMode(NormalMode) + context.self.forward(GenericResponseEnvelope(AvatarStamp, "", FilterGuid, AvatarAction.TeardownConnection)) - /* really common messages (very frequently, every life) */ - case pstate @ AvatarResponse.PlayerState( - pos, - vel, - yaw, - pitch, - yawUpper, - _, - isCrouching, - isJumping, - jumpThrust, - isCloaking, - isNotRendered, - canSeeReallyFar - ) if isNotSameTarget => - val pstateToSave = pstate.copy(timestamp = 0) - val (lastMsg, lastTime, lastPosition, wasVisible, wasShooting) = ops.lastSeenStreamMessage.get(guid.guid) match { - case Some(SessionAvatarHandlers.LastUpstream(Some(msg), visible, shooting, time)) => (Some(msg), time, msg.pos, visible, shooting) - case _ => (None, 0L, Vector3.Zero, false, None) - } - val drawConfig = Config.app.game.playerDraw //m - val maxRange = drawConfig.rangeMax * drawConfig.rangeMax //sq.m - val ourPosition = player.Position //xyz - val currentDistance = Vector3.DistanceSquared(ourPosition, pos) //sq.m - val inDrawableRange = currentDistance <= maxRange - val now = System.currentTimeMillis() //ms - if ( - sessionLogic.zoning.zoningStatus != Zoning.Status.Deconstructing && - !isNotRendered && inDrawableRange + /* really common messages (very frequently, every life) */ + case pstate @ AvatarAction.PlayerState( + pos, + vel, + yaw, + pitch, + yawUpper, + _, + isCrouching, + isJumping, + jumpThrust, + isCloaking, + isNotRendered, + canSeeReallyFar + ) if TestFilter(NotSameTargetTest) => + val pstateToSave = pstate.copy(timestamp = 0) + val (lastMsg, lastTime, lastPosition, wasVisible, wasShooting) = ops.lastSeenStreamMessage.get(FilterGuid.guid) match { + case Some(SessionAvatarHandlers.LastUpstream(Some(msg), visible, shooting, time)) => (Some(msg), time, msg.pos, visible, shooting) + case _ => (None, 0L, Vector3.Zero, false, None) + } + val drawConfig = Config.app.game.playerDraw //m + val maxRange = drawConfig.rangeMax * drawConfig.rangeMax //sq.m + val ourPosition = player.Position //xyz + val currentDistance = Vector3.DistanceSquared(ourPosition, pos) //sq.m + val inDrawableRange = currentDistance <= maxRange + val now = System.currentTimeMillis() //ms + if ( + sessionLogic.zoning.zoningStatus != Zoning.Status.Deconstructing && + !isNotRendered && inDrawableRange + ) { + //conditions where visibility is assured + val durationSince = now - lastTime //ms + lazy val previouslyInDrawableRange = Vector3.DistanceSquared(ourPosition, lastPosition) <= maxRange + lazy val targetDelay = { + val populationOver = math.max( + 0, + sessionLogic.localSector.livePlayerList.size - drawConfig.populationThreshold + ) + val distanceAdjustment = math.pow(populationOver / drawConfig.populationStep * drawConfig.rangeStep, 2) //sq.m + val adjustedDistance = currentDistance + distanceAdjustment //sq.m + drawConfig.ranges.lastIndexWhere { dist => adjustedDistance > dist * dist } match { + case -1 => 1 + case index => drawConfig.delays(index) + } + } //ms + if (!wasVisible || + !previouslyInDrawableRange || + durationSince > drawConfig.delayMax || + (!lastMsg.contains(pstateToSave) && + (canSeeReallyFar || + currentDistance < drawConfig.rangeMin * drawConfig.rangeMin || + sessionLogic.general.canSeeReallyFar || + durationSince > targetDelay + ) + ) ) { - //conditions where visibility is assured - val durationSince = now - lastTime //ms - lazy val previouslyInDrawableRange = Vector3.DistanceSquared(ourPosition, lastPosition) <= maxRange - lazy val targetDelay = { - val populationOver = math.max( - 0, - sessionLogic.localSector.livePlayerList.size - drawConfig.populationThreshold + //must draw + sendResponse( + PlayerStateMessage( + FilterGuid, + pos, + vel, + yaw, + pitch, + yawUpper, + timestamp = 0, //is this okay? + isCrouching, + isJumping, + jumpThrust, + isCloaking ) - val distanceAdjustment = math.pow(populationOver / drawConfig.populationStep * drawConfig.rangeStep, 2) //sq.m - val adjustedDistance = currentDistance + distanceAdjustment //sq.m - drawConfig.ranges.lastIndexWhere { dist => adjustedDistance > dist * dist } match { - case -1 => 1 - case index => drawConfig.delays(index) - } - } //ms - if (!wasVisible || - !previouslyInDrawableRange || - durationSince > drawConfig.delayMax || - (!lastMsg.contains(pstateToSave) && - (canSeeReallyFar || - currentDistance < drawConfig.rangeMin * drawConfig.rangeMin || - sessionLogic.general.canSeeReallyFar || - durationSince > targetDelay - ) - ) - ) { - //must draw - sendResponse( - PlayerStateMessage( - guid, - pos, - vel, - yaw, - pitch, - yawUpper, - timestamp = 0, //is this okay? - isCrouching, - isJumping, - jumpThrust, - isCloaking - ) - ) - ops.lastSeenStreamMessage.put(guid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, wasShooting, now)) - } else { - //is visible, but skip reinforcement - ops.lastSeenStreamMessage.put(guid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, wasShooting, lastTime)) - } + ) + ops.lastSeenStreamMessage.put(FilterGuid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, wasShooting, now)) } else { - //conditions where the target is not currently visible - if (wasVisible) { - //the target was JUST PREVIOUSLY visible; one last draw to move target beyond a renderable distance - val lat = (1 + ops.hidingPlayerRandomizer.nextInt(continent.map.scale.height.toInt)).toFloat - sendResponse( - PlayerStateMessage( - guid, - Vector3(1f, lat, 1f), - vel=None, - facingYaw=0f, - facingPitch=0f, - facingYawUpper=0f, - timestamp=0, //is this okay? - is_cloaked = isCloaking - ) + //is visible, but skip reinforcement + ops.lastSeenStreamMessage.put(FilterGuid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, wasShooting, lastTime)) + } + } else { + //conditions where the target is not currently visible + if (wasVisible) { + //the target was JUST PREVIOUSLY visible; one last draw to move target beyond a renderable distance + val lat = (1 + ops.hidingPlayerRandomizer.nextInt(continent.map.scale.height.toInt)).toFloat + sendResponse( + PlayerStateMessage( + FilterGuid, + Vector3(1f, lat, 1f), + vel=None, + facingYaw=0f, + facingPitch=0f, + facingYawUpper=0f, + timestamp=0, //is this okay? + is_cloaked = isCloaking ) - ops.lastSeenStreamMessage.put(guid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, wasShooting, now)) - } else { - //skip drawing altogether - ops.lastSeenStreamMessage.put(guid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, wasShooting, lastTime)) - } + ) + ops.lastSeenStreamMessage.put(FilterGuid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, wasShooting, now)) + } else { + //skip drawing altogether + ops.lastSeenStreamMessage.put(FilterGuid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, wasShooting, lastTime)) + } + } + + case AvatarAction.AvatarImplant(ImplantAction.Add, implant_slot, value) + if TestFilter(() => { value == ImplantType.SecondWind.value }) => + sendResponse(AvatarImplantMessage(ResolvedGuid, ImplantAction.Add, implant_slot, 7)) + //second wind does not normally load its icon into the shortcut hotbar + avatar + .shortcuts + .zipWithIndex + .find { case (s, _) => s.isEmpty} + .foreach { case (_, index) => + sendResponse(CreateShortcutMessage(ResolvedGuid, index + 1, Some(ImplantType.SecondWind.shortcut))) } - case AvatarResponse.AvatarImplant(ImplantAction.Add, implant_slot, value) - if value == ImplantType.SecondWind.value => - sendResponse(AvatarImplantMessage(resolvedPlayerGuid, ImplantAction.Add, implant_slot, 7)) - //second wind does not normally load its icon into the shortcut hotbar - avatar - .shortcuts - .zipWithIndex - .find { case (s, _) => s.isEmpty} - .foreach { case (_, index) => - sendResponse(CreateShortcutMessage(resolvedPlayerGuid, index + 1, Some(ImplantType.SecondWind.shortcut))) - } - - case AvatarResponse.AvatarImplant(ImplantAction.Remove, implant_slot, value) - if value == ImplantType.SecondWind.value => - sendResponse(AvatarImplantMessage(resolvedPlayerGuid, ImplantAction.Remove, implant_slot, value)) - //second wind does not normally unload its icon from the shortcut hotbar - val shortcut = { - val imp = ImplantType.SecondWind.shortcut - net.psforever.objects.avatar.Shortcut(imp.code, imp.tile) //case class - } - avatar - .shortcuts - .zipWithIndex - .find { case (s, _) => s.contains(shortcut) } - .foreach { case (_, index) => - sendResponse(CreateShortcutMessage(resolvedPlayerGuid, index + 1, None)) - } - - case AvatarResponse.AvatarImplant(action, implant_slot, value) => - sendResponse(AvatarImplantMessage(resolvedPlayerGuid, action, implant_slot, value)) - - case AvatarResponse.ObjectHeld(slot, _) - if isSameTarget && player.VisibleSlots.contains(slot) => - sendResponse(ObjectHeldMessage(guid, slot, unk1=true)) - //Stop using proximity terminals if player unholsters a weapon - continent.GUID(sessionLogic.terminals.usingMedicalTerminal).collect { - case term: Terminal with ProximityUnit => sessionLogic.terminals.StopUsingProximityUnit(term) - } - if (sessionLogic.zoning.zoningStatus == Zoning.Status.Deconstructing) { - sessionLogic.zoning.spawn.stopDeconstructing() + case AvatarAction.AvatarImplant(ImplantAction.Remove, implant_slot, value) + if TestFilter(() => { value == ImplantType.SecondWind.value }) => + sendResponse(AvatarImplantMessage(ResolvedGuid, ImplantAction.Remove, implant_slot, value)) + //second wind does not normally unload its icon from the shortcut hotbar + val shortcut = { + val imp = ImplantType.SecondWind.shortcut + net.psforever.objects.avatar.Shortcut(imp.code, imp.tile) //case class + } + avatar + .shortcuts + .zipWithIndex + .find { case (s, _) => s.contains(shortcut) } + .foreach { case (_, index) => + sendResponse(CreateShortcutMessage(ResolvedGuid, index + 1, None)) } - case AvatarResponse.ObjectHeld(slot, _) - if isSameTarget && slot > -1 => - sendResponse(ObjectHeldMessage(guid, slot, unk1=true)) + case AvatarAction.AvatarImplant(action, implant_slot, value) => + sendResponse(AvatarImplantMessage(ResolvedGuid, action, implant_slot, value)) - case AvatarResponse.ObjectHeld(_, _) - if isSameTarget => () + case AvatarAction.ObjectHeld(slot, _) + if TestFilter(() => { SameTarget && player.VisibleSlots.contains(slot) }) => + sendResponse(ObjectHeldMessage(FilterGuid, slot, unk1=true)) + //Stop using proximity terminals if player unholsters a weapon + continent.GUID(sessionLogic.terminals.usingMedicalTerminal).collect { + case term: Terminal with ProximityUnit => sessionLogic.terminals.StopUsingProximityUnit(term) + } + if (sessionLogic.zoning.zoningStatus == Zoning.Status.Deconstructing) { + sessionLogic.zoning.spawn.stopDeconstructing() + } - case AvatarResponse.ObjectHeld(_, previousSlot) => - sendResponse(ObjectHeldMessage(guid, previousSlot, unk1=false)) + case AvatarAction.ObjectHeld(slot, _) + if TestFilter(() => {SameTarget && slot > -1 }) => + sendResponse(ObjectHeldMessage(FilterGuid, slot, unk1=true)) - case AvatarResponse.ChangeFireState_Start(weaponGuid) - if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { _.visible } => - sendResponse(ChangeFireStateMessage_Start(weaponGuid)) - val entry = ops.lastSeenStreamMessage(guid.guid) - ops.lastSeenStreamMessage.put(guid.guid, entry.copy(shooting = Some(weaponGuid))) + case AvatarAction.ObjectHeld(_, _) + if TestFilter(SameTargetTest) => () - case AvatarResponse.ChangeFireState_Start(weaponGuid) - if isNotSameTarget => - sendResponse(ChangeFireStateMessage_Start(weaponGuid)) + case AvatarAction.ObjectHeld(_, previousSlot) => + sendResponse(ObjectHeldMessage(FilterGuid, previousSlot, unk1=false)) - case AvatarResponse.ChangeFireState_Stop(weaponGuid) - if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { msg => msg.visible || msg.shooting.nonEmpty } => - sendResponse(ChangeFireStateMessage_Stop(weaponGuid)) - val entry = ops.lastSeenStreamMessage(guid.guid) - ops.lastSeenStreamMessage.put(guid.guid, entry.copy(shooting = None)) + case ChangeFireState_Start(weaponGuid) + if TestFilter(() => { NotSameTarget && ops.lastSeenStreamMessage.get(FilterGuid.guid).exists { _.visible } }) => + sendResponse(ChangeFireStateMessage_Start(weaponGuid)) + val entry = ops.lastSeenStreamMessage(FilterGuid.guid) + ops.lastSeenStreamMessage.put(FilterGuid.guid, entry.copy(shooting = Some(weaponGuid))) - case AvatarResponse.ChangeFireState_Stop(weaponGuid) - if isNotSameTarget => - sendResponse(ChangeFireStateMessage_Stop(weaponGuid)) + case ChangeFireState_Stop(weaponGuid) + if TestFilter(() => { NotSameTarget && ops.lastSeenStreamMessage.get(FilterGuid.guid).exists { msg => msg.visible || msg.shooting.nonEmpty } }) => + sendResponse(ChangeFireStateMessage_Stop(weaponGuid)) + val entry = ops.lastSeenStreamMessage(FilterGuid.guid) + ops.lastSeenStreamMessage.put(FilterGuid.guid, entry.copy(shooting = None)) - case AvatarResponse.LoadPlayer(pkt) if isNotSameTarget => - sendResponse(pkt) + case AvatarAction.LoadCreatedPlayer(pkt) + if TestFilter(NotSameTargetTest) => + sendResponse(pkt) - case AvatarResponse.EquipmentInHand(pkt) if isNotSameTarget => - sendResponse(pkt) + case AvatarAction.EquipmentCreatedInHand(pkt) + if TestFilter(NotSameTargetTest) => + sendResponse(pkt) - case AvatarResponse.PlanetsideAttribute(attributeType, attributeValue) if isNotSameTarget => - sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue)) + case AvatarAction.Destroy(victim, killer, weapon, pos) => + // guid = victim // killer = killer + sendResponse(DestroyMessage(victim, killer, weapon, pos)) - case AvatarResponse.PlanetsideAttributeToAll(attributeType, attributeValue) => - sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue)) + case AvatarAction.DestroyDisplay(killer, victim, method, unk) => + sendResponse(ops.destroyDisplayMessage(killer, victim, method, unk)) - case AvatarResponse.PlanetsideAttributeSelf(attributeType, attributeValue) if isSameTarget => - sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue)) + case AvatarAction.TerminalOrderResult(terminalGuid, action, result) + if TestFilter(() => { result && (action == TransactionType.Buy || action == TransactionType.Loadout) }) => + sendResponse(ItemTransactionResultMessage(terminalGuid, action, result)) + sessionLogic.terminals.lastTerminalOrderFulfillment = true + AvatarActor.savePlayerData(player) - case AvatarResponse.GenericObjectAction(objectGuid, actionCode) if isNotSameTarget => - sendResponse(GenericObjectActionMessage(objectGuid, actionCode)) + case AvatarAction.TerminalOrderResult(terminalGuid, action, result) => + sendResponse(ItemTransactionResultMessage(terminalGuid, action, result)) + sessionLogic.terminals.lastTerminalOrderFulfillment = true - case AvatarResponse.HitHint(sourceGuid) if player.isAlive => - sendResponse(HitHint(sourceGuid, guid)) - sessionLogic.zoning.CancelZoningProcess() - - case AvatarResponse.Destroy(victim, killer, weapon, pos) => - // guid = victim // killer = killer - sendResponse(DestroyMessage(victim, killer, weapon, pos)) - - case AvatarResponse.DestroyDisplay(killer, victim, method, unk) => - sendResponse(ops.destroyDisplayMessage(killer, victim, method, unk)) - - case AvatarResponse.TerminalOrderResult(terminalGuid, action, result) - if result && (action == TransactionType.Buy || action == TransactionType.Loadout) => - sendResponse(ItemTransactionResultMessage(terminalGuid, action, result)) - sessionLogic.terminals.lastTerminalOrderFulfillment = true - AvatarActor.savePlayerData(player) - - case AvatarResponse.TerminalOrderResult(terminalGuid, action, result) => - sendResponse(ItemTransactionResultMessage(terminalGuid, action, result)) - sessionLogic.terminals.lastTerminalOrderFulfillment = true - - case AvatarResponse.ChangeExosuit( - target, - armor, - exosuit, - subtype, - _, - maxhand, - oldHolsters, - holsters, - oldInventory, - inventory, - drop, - delete - ) if resolvedPlayerGuid == target => - sendResponse(ArmorChangedMessage(target, exosuit, subtype)) - sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) - //happening to this player - //cleanup - sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=false)) - (oldHolsters ++ oldInventory ++ delete).foreach { - case (_, dguid) => sendResponse(ObjectDeleteMessage(dguid, unk1=0)) - } - //functionally delete - delete.foreach { case (obj, _) => TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) } - //redraw - if (maxhand) { - TaskWorkflow.execute(HoldNewEquipmentUp(player)( - Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)), - 0 - )) - } - //draw free hand - player.FreeHand.Equipment.foreach { obj => + case AvatarAction.ChangeExosuit( + target, + armor, + exosuit, + subtype, + _, + maxhand, + oldHolsters, + holsters, + oldInventory, + inventory, + drop, + delete + ) if TestFilter(() => {ResolvedGuid == target }) => + sendResponse(ArmorChangedMessage(target, exosuit, subtype)) + sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) + //happening to this player + //cleanup + sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=false)) + (oldHolsters ++ oldInventory ++ delete).foreach { + case (_, dguid) => sendResponse(ObjectDeleteMessage(dguid, unk1=0)) + } + //functionally delete + delete.foreach { case (obj, _) => TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) } + //redraw + if (maxhand) { + TaskWorkflow.execute(HoldNewEquipmentUp(player)( + Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)), + 0 + )) + } + //draw free hand + player.FreeHand.Equipment.foreach { obj => + val definition = obj.Definition + sendResponse( + ObjectCreateDetailedMessage( + definition.ObjectId, + obj.GUID, + ObjectCreateMessageParent(target, Player.FreeHandSlot), + definition.Packet.DetailedConstructorData(obj).get + ) + ) + } + //draw holsters and inventory + (holsters ++ inventory).foreach { + case InventoryItem(obj, index) => val definition = obj.Definition sendResponse( ObjectCreateDetailedMessage( definition.ObjectId, obj.GUID, - ObjectCreateMessageParent(target, Player.FreeHandSlot), + ObjectCreateMessageParent(target, index), definition.Packet.DetailedConstructorData(obj).get ) ) - } - //draw holsters and inventory - (holsters ++ inventory).foreach { - case InventoryItem(obj, index) => - val definition = obj.Definition - sendResponse( - ObjectCreateDetailedMessage( - definition.ObjectId, - obj.GUID, - ObjectCreateMessageParent(target, index), - definition.Packet.DetailedConstructorData(obj).get - ) + } + DropLeftovers(player)(drop) + + case AvatarAction.ChangeExosuit(target, armor, exosuit, subtype, slot, _, oldHolsters, holsters, _, _, drop, delete) => + sendResponse(ArmorChangedMessage(target, exosuit, subtype)) + sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) + //happening to some other player + sendResponse(ObjectHeldMessage(target, slot, unk1 = false)) + //cleanup + val dropPred = ContainableBehavior.DropPredicate(player) + val deleteFromDrop = drop.filterNot(dropPred) + (oldHolsters ++ delete ++ deleteFromDrop.map(f =>(f.obj, f.GUID))) + .distinctBy(_._2) + .foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) } + //draw holsters + holsters.foreach { + case InventoryItem(obj, index) => + val definition = obj.Definition + sendResponse( + ObjectCreateMessage( + definition.ObjectId, + obj.GUID, + ObjectCreateMessageParent(target, index), + definition.Packet.ConstructorData(obj).get ) - } - DropLeftovers(player)(drop) - - case AvatarResponse.ChangeExosuit(target, armor, exosuit, subtype, slot, _, oldHolsters, holsters, _, _, drop, delete) => - sendResponse(ArmorChangedMessage(target, exosuit, subtype)) - sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) - //happening to some other player - sendResponse(ObjectHeldMessage(target, slot, unk1 = false)) - //cleanup - val dropPred = ContainableBehavior.DropPredicate(player) - val deleteFromDrop = drop.filterNot(dropPred) - (oldHolsters ++ delete ++ deleteFromDrop.map(f =>(f.obj, f.GUID))) - .distinctBy(_._2) - .foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) } - //draw holsters - holsters.foreach { - case InventoryItem(obj, index) => - val definition = obj.Definition - sendResponse( - ObjectCreateMessage( - definition.ObjectId, - obj.GUID, - ObjectCreateMessageParent(target, index), - definition.Packet.ConstructorData(obj).get - ) - ) - } - - case AvatarResponse.ChangeLoadout( - target, - armor, - exosuit, - subtype, - _, - maxhand, - oldHolsters, - holsters, - oldInventory, - inventory, - drops - ) if resolvedPlayerGuid == target => - sendResponse(ArmorChangedMessage(target, exosuit, subtype)) - sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) - //happening to this player - sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=true)) - //cleanup - (oldHolsters ++ oldInventory).foreach { - case (obj, objGuid) => - sendResponse(ObjectDeleteMessage(objGuid, unk1=0)) - TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) - } - drops.foreach(item => sendResponse(ObjectDeleteMessage(item.obj.GUID, unk1=0))) - //redraw - if (maxhand) { - sendResponse(PlanetsideAttributeMessage(target, attribute_type=7, player.Capacitor.toLong)) - TaskWorkflow.execute(HoldNewEquipmentUp(player)( - Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)), - slot = 0 - )) - } - (holsters ++ inventory).foreach { case InventoryItem(item, slot) => - TaskWorkflow.execute(PutLoadoutEquipmentInInventory(player)(item, slot)) - } - DropLeftovers(player)(drops) - - case AvatarResponse.ChangeLoadout(target, armor, exosuit, subtype, slot, _, oldHolsters, _, _, _, _) => - //redraw handled by callbacks - sendResponse(ArmorChangedMessage(target, exosuit, subtype)) - sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) - //happening to some other player - sendResponse(ObjectHeldMessage(target, slot, unk1=false)) - //cleanup - oldHolsters.foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) } - - case AvatarResponse.UseKit(kguid, kObjId) => - sendResponse( - UseItemMessage( - resolvedPlayerGuid, - kguid, - resolvedPlayerGuid, - unk2 = 4294967295L, - unk3 = false, - unk4 = Vector3.Zero, - unk5 = Vector3.Zero, - unk6 = 126, - unk7 = 0, //sequence time? - unk8 = 137, - kObjId ) - ) - sendResponse(ObjectDeleteMessage(kguid, unk1=0)) + } - case AvatarResponse.KitNotUsed(_, "") => - sessionLogic.general.kitToBeUsed = None - - case AvatarResponse.KitNotUsed(_, msg) => - sessionLogic.general.kitToBeUsed = None - sendResponse(ChatMsg(ChatMessageType.UNK_225, msg)) - - case AvatarResponse.SendResponse(msg) => - sendResponse(msg) - - case AvatarResponse.SendResponseTargeted(targetGuid, msg) if resolvedPlayerGuid == targetGuid => - sendResponse(msg) - - /* common messages (maybe once every respawn) */ - case AvatarResponse.Reload(itemGuid) - if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { _.visible } => - sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0)) - - case AvatarResponse.Killed(_, mount) => - //pure logic - sessionLogic.shooting.shotsWhileDead = 0 - sessionLogic.zoning.CancelZoningProcess() - sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive - sessionLogic.zoning.zoningStatus = Zoning.Status.None - continent.GUID(mount).collect { - case obj: Vehicle if obj.Destroyed => - ops.killedWhileMounted(obj, resolvedPlayerGuid) - sessionLogic.vehicles.ConditionalDriverVehicleControl(obj) - sessionLogic.general.unaccessContainer(obj) - - case obj: Vehicle => - ops.killedWhileMounted(obj, resolvedPlayerGuid) - sessionLogic.vehicles.ConditionalDriverVehicleControl(obj) - - case obj: PlanetSideGameObject with Mountable with Container if obj.Destroyed => - ops.killedWhileMounted(obj, resolvedPlayerGuid) - sessionLogic.general.unaccessContainer(obj) - - case obj: PlanetSideGameObject with Mountable with Container => - ops.killedWhileMounted(obj, resolvedPlayerGuid) - - case obj: PlanetSideGameObject with Mountable => - ops.killedWhileMounted(obj, resolvedPlayerGuid) - } - //player state changes - sessionLogic.general.dropSpecialSlotItem() - sessionLogic.general.toggleMaxSpecialState(enable = false) - player.FreeHand.Equipment.foreach(DropEquipmentFromInventory(player)(_)) - AvatarActor.updateToolDischargeFor(avatar) - AvatarActor.savePlayerLocation(player) - ops.revive() - avatarActor ! AvatarActor.InitializeImplants - //render - CustomerServiceRepresentativeMode.renderPlayer(sessionLogic, continent, player) - - case AvatarResponse.Release(tplayer) if isNotSameTarget => - sessionLogic.zoning.spawn.DepictPlayerAsCorpse(tplayer) - - case AvatarResponse.Revive(revivalTargetGuid) - if resolvedPlayerGuid == revivalTargetGuid => - ops.revive() - player.Actor ! Player.Revive - player.History - .findLast { _.isInstanceOf[RevivingActivity] } - .map { - case activity: RevivingActivity - if System.currentTimeMillis() - activity.time < 5000L => - val reviveMessage = s"@YouHaveBeenMessage^revived~^${activity.user.unique.name}~" - sendResponse(ChatMsg(ChatMessageType.UNK_227, reviveMessage)) - None - } - - /* uncommon messages (utility, or once in a while) */ - case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) - if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { _.visible } => - ops.changeAmmoProcedures(weapon_guid, previous_guid, ammo_id, ammo_guid, weapon_slot, ammo_data) - sendResponse(ChangeAmmoMessage(weapon_guid, 1)) - - case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) - if isNotSameTarget => - ops.changeAmmoProcedures(weapon_guid, previous_guid, ammo_id, ammo_guid, weapon_slot, ammo_data) - - case AvatarResponse.ChangeFireMode(itemGuid, mode) if isNotSameTarget => - sendResponse(ChangeFireModeMessage(itemGuid, mode)) - - case AvatarResponse.ConcealPlayer() => - sendResponse(GenericObjectActionMessage(guid, code=9)) - - case AvatarResponse.EnvironmentalDamage(_, _, _) => - //TODO damage marker? - sessionLogic.zoning.CancelZoningProcess() - - case AvatarResponse.DropItem(pkt) if isNotSameTarget => - sendResponse(pkt) - - case AvatarResponse.ObjectDelete(itemGuid, unk) if isNotSameTarget => - sendResponse(ObjectDeleteMessage(itemGuid, unk)) - - /* rare messages */ - case AvatarResponse.SetEmpire(objectGuid, faction) if isNotSameTarget => - sendResponse(SetEmpireMessage(objectGuid, faction)) - - case AvatarResponse.DropSpecialItem() => - sessionLogic.general.dropSpecialSlotItem() - - case AvatarResponse.OxygenState(player, vehicle) => - sendResponse(OxygenStateMessage( - DrowningTarget(player.guid, player.progress, player.state), - vehicle.flatMap { vinfo => Some(DrowningTarget(vinfo.guid, vinfo.progress, vinfo.state)) } + case AvatarAction.ChangeLoadout( + target, + armor, + exosuit, + subtype, + _, + maxhand, + oldHolsters, + holsters, + oldInventory, + inventory, + drops + ) if TestFilter(() => { ResolvedGuid == target }) => + sendResponse(ArmorChangedMessage(target, exosuit, subtype)) + sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) + //happening to this player + sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=true)) + //cleanup + (oldHolsters ++ oldInventory).foreach { + case (obj, objGuid) => + sendResponse(ObjectDeleteMessage(objGuid, unk1=0)) + TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) + } + drops.foreach(item => sendResponse(ObjectDeleteMessage(item.obj.GUID, unk1=0))) + //redraw + if (maxhand) { + sendResponse(PlanetsideAttributeMessage(target, attribute_type=7, player.Capacitor.toLong)) + TaskWorkflow.execute(HoldNewEquipmentUp(player)( + Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)), + slot = 0 )) + } + (holsters ++ inventory).foreach { case InventoryItem(item, slot) => + TaskWorkflow.execute(PutLoadoutEquipmentInInventory(player)(item, slot)) + } + DropLeftovers(player)(drops) - case AvatarResponse.LoadProjectile(pkt) if isNotSameTarget => - sendResponse(pkt) + case AvatarAction.ChangeLoadout(target, armor, exosuit, subtype, slot, _, oldHolsters, _, _, _, _) => + //redraw handled by callbacks + sendResponse(ArmorChangedMessage(target, exosuit, subtype)) + sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) + //happening to some other player + sendResponse(ObjectHeldMessage(target, slot, unk1=false)) + //cleanup + oldHolsters.foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) } - case AvatarResponse.ProjectileState(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid) if isNotSameTarget => - sendResponse(ProjectileStateMessage(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid)) - - case AvatarResponse.ProjectileExplodes(projectileGuid, projectile) => - sendResponse( - ProjectileStateMessage( - projectileGuid, - projectile.Position, - shot_vel = Vector3.Zero, - projectile.Orientation, - sequence_num=0, - end=true, - hit_target_guid=PlanetSideGUID(0) - ) + case AvatarAction.UseKit(kguid, kObjId) => + sendResponse( + UseItemMessage( + ResolvedGuid, + kguid, + ResolvedGuid, + unk2 = 4294967295L, + unk3 = false, + unk4 = Vector3.Zero, + unk5 = Vector3.Zero, + unk6 = 126, + unk7 = 0, //sequence time? + unk8 = 137, + kObjId ) - sendResponse(ObjectDeleteMessage(projectileGuid, unk1=2)) + ) + sendResponse(ObjectDeleteMessage(kguid, unk1=0)) - case AvatarResponse.ProjectileAutoLockAwareness(mode) => - sendResponse(GenericActionMessage(mode)) + case AvatarAction.KitNotUsed(_, "") => + sessionLogic.general.kitToBeUsed = None - case AvatarResponse.PutDownFDU(target) if isNotSameTarget => - sendResponse(GenericObjectActionMessage(target, code=53)) + case AvatarAction.KitNotUsed(_, msg) => + sessionLogic.general.kitToBeUsed = None + sendResponse(ChatMsg(ChatMessageType.UNK_225, msg)) - case AvatarResponse.StowEquipment(target, slot, item) if isNotSameTarget => - val definition = item.Definition - sendResponse( - ObjectCreateDetailedMessage( - definition.ObjectId, - item.GUID, - ObjectCreateMessageParent(target, slot), - definition.Packet.DetailedConstructorData(item).get - ) - ) - case AvatarResponse.WeaponDryFire(weaponGuid) - if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { _.visible } => - continent.GUID(weaponGuid).collect { - case tool: Tool if tool.Magazine == 0 => - // check that the magazine is still empty before sending WeaponDryFireMessage - // if it has been reloaded since then, other clients will not see it firing - sendResponse(WeaponDryFireMessage(weaponGuid)) + /* common messages (maybe once every respawn) */ + case ReloadTool(itemGuid) + if TestFilter(() => { NotSameTarget && ops.lastSeenStreamMessage.get(FilterGuid.guid).exists { _.visible } }) => + sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0)) + + case AvatarAction.Killed(_, mount) => + //pure logic + sessionLogic.shooting.shotsWhileDead = 0 + sessionLogic.zoning.CancelZoningProcess() + sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive + sessionLogic.zoning.zoningStatus = Zoning.Status.None + continent.GUID(mount).collect { + case obj: Vehicle if obj.Destroyed => + ops.killedWhileMounted(obj, ResolvedGuid) + sessionLogic.vehicles.ConditionalDriverVehicleControl(obj) + sessionLogic.general.unaccessContainer(obj) + + case obj: Vehicle => + ops.killedWhileMounted(obj, ResolvedGuid) + sessionLogic.vehicles.ConditionalDriverVehicleControl(obj) + + case obj: PlanetSideGameObject with Mountable with Container if obj.Destroyed => + ops.killedWhileMounted(obj, ResolvedGuid) + sessionLogic.general.unaccessContainer(obj) + + case obj: PlanetSideGameObject with Mountable with Container => + ops.killedWhileMounted(obj, ResolvedGuid) + + case obj: PlanetSideGameObject with Mountable => + ops.killedWhileMounted(obj, ResolvedGuid) + } + //player state changes + sessionLogic.general.dropSpecialSlotItem() + sessionLogic.general.toggleMaxSpecialState(enable = false) + player.FreeHand.Equipment.foreach(DropEquipmentFromInventory(player)(_)) + AvatarActor.updateToolDischargeFor(avatar) + AvatarActor.savePlayerLocation(player) + ops.revive() + avatarActor ! AvatarActor.InitializeImplants + //render + CustomerServiceRepresentativeMode.renderPlayer(sessionLogic, continent, player) + + case AvatarAction.ReleasePlayer(tplayer) + if TestFilter(NotSameTargetTest) => + sessionLogic.zoning.spawn.DepictPlayerAsCorpse(tplayer) + + case AvatarAction.Revive(revivalTargetGuid) + if TestFilter(() => { ResolvedGuid == revivalTargetGuid }) => + ops.revive() + player.Actor ! Player.Revive + player.History + .findLast { _.isInstanceOf[RevivingActivity] } + .map { + case activity: RevivingActivity + if System.currentTimeMillis() - activity.time < 5000L => + val reviveMessage = s"@YouHaveBeenMessage^revived~^${activity.user.unique.name}~" + sendResponse(ChatMsg(ChatMessageType.UNK_227, reviveMessage)) + None } - case _ => () - } + /* uncommon messages (utility, or once in a while) */ + case ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) + if TestFilter(NotSameTargetTest) => + ops.changeAmmoProcedure(weapon_guid, previous_guid, ammo_id, ammo_guid, weapon_slot, ammo_data) + sendResponse(ChangeAmmoMessage(weapon_guid, 1)) + + case AvatarAction.ChangeFireMode(itemGuid, mode) + if TestFilter(NotSameTargetTest) => + sendResponse(ChangeFireModeMessage(itemGuid, mode)) + + case AvatarAction.EnvironmentalDamage(_, _, _) => + //TODO damage marker? + sessionLogic.zoning.CancelZoningProcess() + + case AvatarAction.DropCreatedItem(pkt) + if TestFilter(NotSameTargetTest) => + sendResponse(pkt) + + /* rare messages */ + case AvatarAction.DropSpecialItem() => + sessionLogic.general.dropSpecialSlotItem() + + case AvatarAction.OxygenState(player, vehicle) => + sendResponse(OxygenStateMessage( + DrowningTarget(player.guid, player.progress, player.state), + vehicle.flatMap { vinfo => Some(DrowningTarget(vinfo.guid, vinfo.progress, vinfo.state)) } + )) + + case AvatarAction.LoadCreatedProjectile(pkt) + if TestFilter(NotSameTargetTest) => + sendResponse(pkt) + + case AvatarAction.ProjectileState(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid) + if TestFilter(NotSameTargetTest) => + sendResponse(ProjectileStateMessage(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid)) + + case AvatarAction.ProjectileExplodes(projectileGuid, projectile) => + sendResponse( + ProjectileStateMessage( + projectileGuid, + projectile.Position, + shot_vel = Vector3.Zero, + projectile.Orientation, + sequence_num=0, + end=true, + hit_target_guid=PlanetSideGUID(0) + ) + ) + sendResponse(ObjectDeleteMessage(projectileGuid, unk1=2)) + + case AvatarAction.ProjectileAutoLockAwareness(mode) => + sendResponse(GenericActionMessage(mode)) + + case AvatarAction.PutDownFDU(target) + if TestFilter(NotSameTargetTest) => + sendResponse(GenericObjectActionMessage(target, code=53)) + + case AvatarAction.StowEquipment(target, slot, item) + if TestFilter(NotSameTargetTest) => + val definition = item.Definition + sendResponse( + ObjectCreateDetailedMessage( + definition.ObjectId, + item.GUID, + ObjectCreateMessageParent(target, slot), + definition.Packet.DetailedConstructorData(item).get + ) + ) + + case WeaponDryFire(weaponGuid) + if TestFilter(() => { NotSameTarget && ops.lastSeenStreamMessage.get(FilterGuid.guid).exists { _.visible } }) => + continent.GUID(weaponGuid).collect { + case tool: Tool if tool.Magazine == 0 => + sendResponse(WeaponDryFireMessage(weaponGuid)) + } } } diff --git a/src/main/scala/net/psforever/actors/session/csr/ChatLogic.scala b/src/main/scala/net/psforever/actors/session/csr/ChatLogic.scala index 3d5d4c45b..13053039c 100644 --- a/src/main/scala/net/psforever/actors/session/csr/ChatLogic.scala +++ b/src/main/scala/net/psforever/actors/session/csr/ChatLogic.scala @@ -16,7 +16,9 @@ import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.zones.Zone import net.psforever.packet.game.{ChatMsg, SetChatFilterMessage} import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.{ObjectDelete, SetEmpire} import net.psforever.services.chat.{ChatChannel, DefaultChannel, SpectatorChannel, SquadChannel} import net.psforever.types.ChatMessageType.{CMT_TOGGLESPECTATORMODE, CMT_TOGGLE_GM} import net.psforever.types.{ChatMessageType, PlanetSideEmpire} @@ -312,17 +314,20 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext val events = continent.AvatarEvents seeSpectatorsIn = Some(continent) events ! Service.Join(s"spectator") - continent - .AllPlayers - .filter(_.spectator) - .foreach { spectator => - val guid = spectator.GUID - val definition = spectator.Definition - events ! AvatarServiceMessage( - channel, - AvatarAction.LoadPlayer(guid, definition.ObjectId, guid, definition.Packet.ConstructorData(spectator).get, None) - ) - } + events ! BundledEnvelope( + continent + .AllPlayers + .filter(_.spectator) + .map { spectator => + val guid = spectator.GUID + val definition = spectator.Definition + MessageEnvelope( + channel, + guid, + AvatarAction.LoadPlayer(definition.ObjectId, guid, definition.Packet.ConstructorData(spectator).get, None) + ) + } + ) true } @@ -330,17 +335,16 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext val channel = player.Name val events = continent.AvatarEvents seeSpectatorsIn = None - events ! Service.Leave(Some("spectator")) - continent - .AllPlayers - .filter(_.spectator) - .foreach { spectator => - val guid = spectator.GUID - events ! AvatarServiceMessage( - channel, - AvatarAction.ObjectDelete(guid, guid) - ) - } + events ! Service.Leave("spectator") + events ! BundledEnvelope( + continent + .AllPlayers + .filter(_.spectator) + .map { spectator => + val guid = spectator.GUID + MessageEnvelope(channel, guid, ObjectDelete(guid)) + } + ) true } @@ -392,9 +396,9 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext true case o: Deployable => o.Faction = foundFaction - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( continent.id, - AvatarAction.SetEmpire(Service.defaultPlayerGUID, o.GUID, foundFaction) + SetEmpire(o.GUID, foundFaction) ) true case o: Building => @@ -405,9 +409,9 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext true case o: PlanetSideGameObject with FactionAffinity => o.Faction = foundFaction - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( continent.id, - AvatarAction.SetEmpire(Service.defaultPlayerGUID, o.GUID, foundFaction) + SetEmpire(o.GUID, foundFaction) ) true } @@ -512,7 +516,7 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext .foreach { case (_, false, _) => () case (faction, true, _) => - //events ! AvatarServiceMessage(s"$faction", reloadZoneMsg) + //events ! MessageEnvelope(s"$faction", reloadZoneMsg) } } } diff --git a/src/main/scala/net/psforever/actors/session/csr/CustomerServiceRepresentativeMode.scala b/src/main/scala/net/psforever/actors/session/csr/CustomerServiceRepresentativeMode.scala index 0e454f71b..0ab1782fc 100644 --- a/src/main/scala/net/psforever/actors/session/csr/CustomerServiceRepresentativeMode.scala +++ b/src/main/scala/net/psforever/actors/session/csr/CustomerServiceRepresentativeMode.scala @@ -10,9 +10,10 @@ import net.psforever.objects.vital.Vitality import net.psforever.objects.zones.Zone import net.psforever.packet.game.{ChatMsg, ObjectCreateDetailedMessage, PlanetsideAttributeMessage} import net.psforever.packet.game.objectcreate.RibbonBars -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.PlanetsideAttribute import net.psforever.services.chat.{CustomerServiceChannel, SpectatorChannel} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import net.psforever.types.{ChatMessageType, MeritCommendation, PlanetSideGUID} class CustomerServiceRepresentativeMode(data: SessionData) extends ModeLogic { @@ -120,6 +121,10 @@ class CustomerServiceRepresentativeMode(data: SessionData) extends ModeLogic { .GUID(player.VehicleSeated) .collect { case obj: PlanetSideGameObject with Vitality => CustomerServiceRepresentativeMode.topOffHealth(data, obj) + obj + } + .getOrElse { + data.updateBlockMap(player, player.Position) } data.squad.updateSquad() } else { @@ -144,13 +149,16 @@ case object CustomerServiceRepresentativeMode extends PlayerMode { packet.DetailedConstructorData(player).get )) data.zoning.spawn.HandleSetCurrentAvatar(player) - zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.LoadPlayer( + zone.AvatarEvents ! MessageEnvelope( + zone.id, pguid, - objectClass, - pguid, - packet.ConstructorData(player).get, - None - )) + AvatarAction.LoadPlayer( + objectClass, + pguid, + packet.ConstructorData(player).get, + None + ) + ) } def topOffHealth(data: SessionData, obj: PlanetSideGameObject with Vitality): Unit = { @@ -174,14 +182,14 @@ case object CustomerServiceRepresentativeMode extends PlayerMode { player.Health = maxHealthOfPlayer.toInt player.LogActivity(player.ClearHistory().head) data.sendResponse(PlanetsideAttributeMessage(guid, 0, maxHealthOfPlayer)) - data.continent.AvatarEvents ! AvatarServiceMessage(zoneid, AvatarAction.PlanetsideAttribute(guid, 0, maxHealthOfPlayer)) + data.continent.AvatarEvents ! MessageEnvelope(zoneid, PlanetsideAttribute(guid, 0, maxHealthOfPlayer)) } //below half armor, full armor val maxArmor = player.MaxArmor.toLong if (player.Armor < maxArmor) { player.Armor = maxArmor.toInt data.sendResponse(PlanetsideAttributeMessage(guid, 4, maxArmor)) - data.continent.AvatarEvents ! AvatarServiceMessage(zoneid, AvatarAction.PlanetsideAttribute(guid, 4, maxArmor)) + data.continent.AvatarEvents ! MessageEnvelope(zoneid, PlanetsideAttribute(guid, 4, maxArmor)) } } @@ -194,9 +202,9 @@ case object CustomerServiceRepresentativeMode extends PlayerMode { val guid = vehicle.GUID vehicle.Shields = maxShieldsOfVehicle.toInt data.sendResponse(PlanetsideAttributeMessage(guid, shieldsUi, maxShieldsOfVehicle)) - data.continent.VehicleEvents ! VehicleServiceMessage( + data.continent.VehicleEvents ! MessageEnvelope( data.continent.id, - VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), guid, shieldsUi, maxShieldsOfVehicle) + PlanetsideAttribute(guid, shieldsUi, maxShieldsOfVehicle) ) } } @@ -208,9 +216,9 @@ case object CustomerServiceRepresentativeMode extends PlayerMode { if (obj.Health < maxHealthOf) { obj.Health = maxHealthOf.toInt data.sendResponse(PlanetsideAttributeMessage(guid, 0, maxHealthOf)) - data.continent.VehicleEvents ! VehicleServiceMessage( + data.continent.VehicleEvents ! MessageEnvelope( data.continent.id, - VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), guid, 0, maxHealthOf) + PlanetsideAttribute(guid, 0, maxHealthOf) ) } } 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 4f168065b..b795a07b4 100644 --- a/src/main/scala/net/psforever/actors/session/csr/GeneralLogic.scala +++ b/src/main/scala/net/psforever/actors/session/csr/GeneralLogic.scala @@ -33,9 +33,11 @@ import net.psforever.objects.zones.{ZoneProjectile, Zoning} 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} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.avatar.support.CorpseEnvelope +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.PlanetsideAttribute +import net.psforever.services.base.support.RemoverActor import net.psforever.types.{CapacitorStateType, ChatMessageType, Cosmetic, ExoSuitType, PlanetSideEmpire, PlanetSideGUID, Vector3} import scala.concurrent.duration._ @@ -132,10 +134,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex val eagleEye: Boolean = ops.canSeeReallyFar val isNotVisible: Boolean = sessionLogic.zoning.zoningStatus == Zoning.Status.Deconstructing || (player.isAlive && sessionLogic.zoning.spawn.deadState == DeadState.RespawnTime) - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( channel, + avatarGuid, AvatarAction.PlayerState( - avatarGuid, player.Position, player.Velocity, yaw, @@ -163,15 +165,8 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex } def handleEmote(pkt: EmoteMsg): Unit = { - val EmoteMsg(avatarGuid, emote) = pkt - val pZone = player.Zone - sendResponse(EmoteMsg(avatarGuid, emote)) - pZone.blockMap.sector(player).livePlayerList.collect { case t if t.GUID != player.GUID => - pZone.LocalEvents ! LocalServiceMessage(t.Name, LocalAction.SendResponse(EmoteMsg(avatarGuid, emote))) - } - pZone.AllPlayers.collect { case t if t.GUID != player.GUID && !t.allowInteraction => - pZone.LocalEvents ! LocalServiceMessage(t.Name, LocalAction.SendResponse(EmoteMsg(avatarGuid, emote))) - } + sendResponse(pkt) + ops.handleEmote(pkt) } def handleDropItem(pkt: DropItemMessage): Unit = { @@ -239,7 +234,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex case Some(obj: Player) if obj.isBackpack => obj.Position = Vector3.Zero - continent.AvatarEvents ! AvatarServiceMessage.Corpse(RemoverActor.ClearSpecific(List(obj), continent)) + continent.AvatarEvents ! CorpseEnvelope(RemoverActor.ClearSpecific(List(obj), continent)) case Some(obj: Player) => sessionLogic.general.suicide(obj) @@ -439,9 +434,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex ops.dropSpecialSlotItem() case GenericAction.MaxAnchorsExtend_RCV => player.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( continent.id, - AvatarAction.PlanetsideAttribute(player.GUID, 19, 1) + player.GUID, + PlanetsideAttribute(player.GUID, 19, 1) ) definition match { case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.trhev_burster => @@ -459,9 +455,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex } case GenericAction.MaxAnchorsRelease_RCV => player.UsingSpecial = SpecialExoSuitDefinition.Mode.Normal - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( continent.id, - AvatarAction.PlanetsideAttribute(player.GUID, 19, 0) + player.GUID, + PlanetsideAttribute(player.GUID, 19, 0) ) definition match { case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.trhev_burster => diff --git a/src/main/scala/net/psforever/actors/session/csr/MountHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/csr/MountHandlerLogic.scala index 1965825ab..e7a3b4494 100644 --- a/src/main/scala/net/psforever/actors/session/csr/MountHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/csr/MountHandlerLogic.scala @@ -7,7 +7,6 @@ import net.psforever.actors.zone.ZoneActor import net.psforever.objects.{GlobalDefinitions, PlanetSideGameObject, Player, Vehicle, Vehicles} import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.environment.interaction.ResetAllEnvironmentInteractions -import net.psforever.objects.serverobject.hackable.GenericHackables import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.structures.WarpGate import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech @@ -15,9 +14,9 @@ import net.psforever.objects.serverobject.turret.{FacilityTurret, WeaponTurret} import net.psforever.objects.vehicles.AccessPermissionGroup import net.psforever.objects.vital.InGameHistory import net.psforever.packet.game.{ChatMsg, DelayedPathMountMsg, DismountVehicleCargoMsg, DismountVehicleMsg, GenericObjectActionMessage, MountVehicleCargoMsg, MountVehicleMsg, ObjectDetachMessage, PlanetsideAttributeMessage, PlayerStasisMessage, PlayerStateShiftMessage, ShiftState} -import net.psforever.services.Service -import net.psforever.services.local.{LocalAction, LocalServiceMessage} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.{SendResponse, SetEmpire} +import net.psforever.services.vehicle.VehicleAction import net.psforever.types.{BailType, ChatMessageType, DriveState, PlanetSideGUID, Vector3} object MountHandlerLogic { @@ -160,7 +159,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act case Mountable.CanMount(obj: FacilityTurret, seatNumber, _) if obj.Definition == GlobalDefinitions.vanu_sentry_turret => sessionLogic.zoning.CancelZoningProcess() - obj.Zone.LocalEvents ! LocalServiceMessage(obj.Zone.id, LocalAction.SetEmpire(obj.GUID, player.Faction)) + obj.Zone.LocalEvents ! MessageEnvelope(obj.Zone.id, SetEmpire(obj.GUID, player.Faction)) sendResponse(PlanetsideAttributeMessage(obj.GUID, attribute_type=0, obj.Health)) ops.updateWeaponAtSeatPosition(obj, seatNumber) ops.MountingAction(tplayer, obj, seatNumber) @@ -199,9 +198,9 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act val (pos, zang) = Vehicles.dismountShuttle(obj, mountPoint) tplayer.Position = pos sendResponse(DelayedPathMountMsg(pguid, sguid, u1=60, u2=true)) - continent.LocalEvents ! LocalServiceMessage( + continent.LocalEvents ! MessageEnvelope( continent.id, - LocalAction.SendResponse(ObjectDetachMessage(sguid, pguid, pos, roll=0, pitch=0, zang)) + SendResponse(ObjectDetachMessage(sguid, pguid, pos, roll=0, pitch=0, zang)) ) sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive @@ -213,25 +212,21 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act ops.DismountAction(tplayer, obj, seatNum) continent.actor ! ZoneActor.RemoveFromBlockMap(player) //character doesn't need it //DismountAction(...) uses vehicle service, so use that service to coordinate the remainder of the messages - events ! VehicleServiceMessage( - player.Name, - VehicleAction.SendResponse(Service.defaultPlayerGUID, PlayerStasisMessage(pguid)) //the stasis message - ) //when the player dismounts, they will be positioned where the shuttle was when it disappeared in the sky //the player will fall to the ground and is perfectly vulnerable in this state //additionally, our player must exist in the current zone //having no in-game avatar target will throw us out of the map screen when deploying and cause softlock - events ! VehicleServiceMessage( - player.Name, - VehicleAction.SendResponse( - Service.defaultPlayerGUID, - PlayerStateShiftMessage(ShiftState(unk=0, obj.Position, obj.Orientation.z, vel=None)) //cower in the shuttle bay + events ! BundledEnvelope( + MessageEnvelope(player.Name, + SendResponse(Seq( + PlayerStasisMessage(pguid), + PlayerStateShiftMessage(ShiftState(unk=0, obj.Position, obj.Orientation.z, vel=None)) + )) + ), + MessageEnvelope(continent.id, pguid, + SendResponse(GenericObjectActionMessage(pguid, code=9)) /* conceal the player */ ) ) - events ! VehicleServiceMessage( - continent.id, - VehicleAction.SendResponse(pguid, GenericObjectActionMessage(pguid, code=9)) //conceal the player - ) sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive case Mountable.CanDismount(obj: Vehicle, seatNum, _) @@ -256,9 +251,10 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act ops.DismountVehicleAction(tplayer, obj, seatNum) case Mountable.CanDismount(obj: Vehicle, seat_num, _) => - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.KickPassenger(tplayer.GUID, seat_num, unk2=true, obj.GUID) + tplayer.GUID, + VehicleAction.KickPassenger(seat_num, unk2=true, obj.GUID) ) case Mountable.CanDismount(obj: PlanetSideGameObject with PlanetSideGameObject with Mountable with FactionAffinity with InGameHistory, seatNum, _) => diff --git a/src/main/scala/net/psforever/actors/session/csr/SpectateAsCustomerServiceRepresentativeMode.scala b/src/main/scala/net/psforever/actors/session/csr/SpectateAsCustomerServiceRepresentativeMode.scala index 592fbedd5..9e6510812 100644 --- a/src/main/scala/net/psforever/actors/session/csr/SpectateAsCustomerServiceRepresentativeMode.scala +++ b/src/main/scala/net/psforever/actors/session/csr/SpectateAsCustomerServiceRepresentativeMode.scala @@ -7,7 +7,9 @@ import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.{Player, Session, Vehicle} import net.psforever.objects.zones.Zone import net.psforever.packet.PlanetSidePacket -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.ObjectDelete import net.psforever.services.chat.SpectatorChannel import net.psforever.services.teamwork.{SquadAction, SquadServiceMessage} import net.psforever.types.{ChatMessageType, SquadRequestType} @@ -45,7 +47,7 @@ class SpectatorCSRModeLogic(data: SessionData) extends ModeLogic { // player.spectator = true data.chat.JoinChannel(SpectatorChannel) - continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.ObjectDelete(pguid, pguid)) + continent.AvatarEvents ! MessageEnvelope(continent.id, pguid, ObjectDelete(pguid)) sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, "on")) sendResponse(ChatMsg(ChatMessageType.UNK_225, "CSR SPECTATOR MODE ON")) } @@ -59,9 +61,10 @@ class SpectatorCSRModeLogic(data: SessionData) extends ModeLogic { // player.spectator = false data.chat.LeaveChannel(SpectatorChannel) - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( continent.id, - AvatarAction.LoadPlayer(pguid, avatarId, pguid, player.Definition.Packet.ConstructorData(player).get, None) + pguid, + AvatarAction.LoadPlayer(avatarId, pguid, player.Definition.Packet.ConstructorData(player).get, None) ) sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, "off")) } 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 6870f3c12..f49f3ad01 100644 --- a/src/main/scala/net/psforever/actors/session/csr/VehicleLogic.scala +++ b/src/main/scala/net/psforever/actors/session/csr/VehicleLogic.scala @@ -12,7 +12,9 @@ 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, VehicleStateMessage, VehicleSubStateMessage} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.CachedEnvelope +import net.psforever.services.vehicle.VehicleAction +import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.types.{DriveState, Vector3} object VehicleLogic { @@ -76,10 +78,10 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex obj.Position = position obj.Orientation = angle // - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, + player.GUID, VehicleAction.VehicleState( - player.GUID, vehicle_guid, unk1, position, @@ -92,7 +94,7 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex is_decelerating, obj.Cloaked ) - ) + ) //todo CachedMessage sessionLogic.squad.updateSquad() case (None, _) => //log.error(s"VehicleState: no vehicle $vehicle_guid found in zone") @@ -164,26 +166,11 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex obj.Position = position obj.Orientation = angle obj.DeploymentState = if (is_crouched || !notMountedState) DriveState.Kneeling else DriveState.Mobile - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.FrameVehicleState( - player.GUID, - vehicle_guid, - unk1, - position, - angle, - velocity, - unk2, - unk3, - unk4, - is_crouched, - is_airborne, - ascending_flight, - flight_time, - unk9, - unkA - ) - ) + player.GUID, + VehicleAction.FrameVehicleState(vehicle_guid, unk1, position, angle, velocity, unk2, unk3, unk4, is_crouched, is_airborne, ascending_flight, flight_time, unk9, unkA) + ) //todo CachedMessage sessionLogic.squad.updateSquad() case (None, _) => //log.error(s"VehicleState: no vehicle $vehicle_guid found in zone") @@ -234,10 +221,11 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex val angle = Vector3(0f, pitch, yaw) tool.Orientation = angle player.Orientation = angle - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.ChildObjectState(player.GUID, object_guid, pitch, yaw) - ) + player.GUID, + VehicleAction.ChildObjectState(object_guid, pitch, yaw) + ) //todo CachedMessage } //TODO status condition of "playing getting out of vehicle to allow for late packets without warning if (player.death_by == -1) { @@ -256,22 +244,10 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex obj.Velocity = vel sessionLogic.updateBlockMap(obj, pos) obj.zoneInteractions() - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! CachedEnvelope( continent.id, - VehicleAction.VehicleState( - player.GUID, - vehicle_guid, - unk1, - pos, - ang, - obj.Velocity, - obj.Flying, - 0, - 0, - 15, - unk5 = false, - obj.Cloaked - ) + player.GUID, + VehicleAction.VehicleState(vehicle_guid, unk1, pos, ang, obj.Velocity, obj.Flying, 0, 0, 15, unk5 = false, obj.Cloaked) ) } } @@ -328,9 +304,10 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex if (obj.DeploymentState != DriveState.Mobile) { obj.DeploymentState = DriveState.Mobile sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Mobile, 0, unk3=false, Vector3.Zero)) - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.DeployRequest(player.GUID, obj.GUID, DriveState.Mobile, 0, unk2=false, Vector3.Zero) + player.GUID, + VehicleAction.DeployRequest(obj.GUID, DriveState.Mobile, 0, unk2=false, Vector3.Zero) ) } } diff --git a/src/main/scala/net/psforever/actors/session/normal/AvatarHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/normal/AvatarHandlerLogic.scala index 586391cf8..4c5a324a1 100644 --- a/src/main/scala/net/psforever/actors/session/normal/AvatarHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/AvatarHandlerLogic.scala @@ -1,6 +1,7 @@ // Copyright (c) 2024 PSForever package net.psforever.actors.session.normal +import akka.actor.Actor.Receive import akka.actor.{ActorContext, typed} import net.psforever.actors.session.support.AvatarHandlerFunctions import net.psforever.actors.zone.ZoneActor @@ -12,7 +13,9 @@ import net.psforever.objects.sourcing.PlayerSource import net.psforever.objects.vital.RevivingActivity import net.psforever.objects.vital.interaction.Adversarial import net.psforever.packet.game.{AvatarImplantMessage, CreateShortcutMessage, ImplantAction, PlanetsideStringAttributeMessage} -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.{ChangeAmmo, ChangeFireState_Start, ChangeFireState_Stop, ObjectDelete, PlanetsideAttribute, ReloadTool, WeaponDryFire} import net.psforever.types.ImplantType import scala.concurrent.duration._ @@ -28,9 +31,7 @@ import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal} import net.psforever.objects.vital.etc.ExplodingEntityReason import net.psforever.objects.zones.Zoning import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent -import net.psforever.packet.game.{ArmorChangedMessage, AvatarDeadStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChatMsg, DeadState, DestroyMessage, DrowningTarget, GenericActionMessage, GenericObjectActionMessage, HitHint, ItemTransactionResultMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectHeldMessage, OxygenStateMessage, PlanetsideAttributeMessage, PlayerStateMessage, ProjectileStateMessage, ReloadMessage, SetEmpireMessage, UseItemMessage, WeaponDryFireMessage} -import net.psforever.services.avatar.AvatarResponse -import net.psforever.services.Service +import net.psforever.packet.game.{ArmorChangedMessage, AvatarDeadStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChatMsg, DeadState, DestroyMessage, DrowningTarget, GenericActionMessage, GenericObjectActionMessage, ItemTransactionResultMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectHeldMessage, OxygenStateMessage, PlanetsideAttributeMessage, PlayerStateMessage, ProjectileStateMessage, ReloadMessage, UseItemMessage, WeaponDryFireMessage} import net.psforever.types.{ChatMessageType, PlanetSideGUID, TransactionType, Vector3} import net.psforever.util.Config @@ -45,661 +46,609 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor - /** - * na - * @param toChannel na - * @param guid na - * @param reply na - */ - def handle(toChannel: String, guid: PlanetSideGUID, reply: AvatarResponse.Response): Unit = { - val resolvedPlayerGuid = if (player != null && player.HasGUID) { - player.GUID - } else { - Service.defaultPlayerGUID - } - val isNotSameTarget = resolvedPlayerGuid != guid - val isSameTarget = !isNotSameTarget - reply match { - /* special messages */ - case AvatarResponse.TeardownConnection() => - log.trace(s"ending ${player.Name}'s old session by event system request (relog)") - context.stop(context.self) + def receive: Receive = { + /* special messages */ + case AvatarAction.TeardownConnection => + log.trace(s"ending ${player.Name}'s old session by event system request (relog)") + context.stop(context.self) - /* really common messages (very frequently, every life) */ - case pstate @ AvatarResponse.PlayerState( - pos, - vel, - yaw, - pitch, - yawUpper, - _, - isCrouching, - isJumping, - jumpThrust, - isCloaking, - isNotRendered, - canSeeReallyFar - ) if isNotSameTarget => - val pstateToSave = pstate.copy(timestamp = 0) - val (lastMsg, lastTime, lastPosition, wasVisible, wasShooting) = ops.lastSeenStreamMessage.get(guid.guid) match { - case Some(SessionAvatarHandlers.LastUpstream(Some(msg), visible, shooting, time)) => (Some(msg), time, msg.pos, visible, shooting) - case _ => (None, 0L, Vector3.Zero, false, None) - } - val drawConfig = Config.app.game.playerDraw //m - val maxRange = drawConfig.rangeMax * drawConfig.rangeMax //sq.m - val ourPosition = player.Position //xyz - val currentDistance = Vector3.DistanceSquared(ourPosition, pos) //sq.m - val inDrawableRange = currentDistance <= maxRange - val now = System.currentTimeMillis() //ms - if ( - sessionLogic.zoning.zoningStatus != Zoning.Status.Deconstructing && - !isNotRendered && inDrawableRange + /* really common messages (very frequently, every life) */ + case pstate @ AvatarAction.PlayerState( + pos, + vel, + yaw, + pitch, + yawUpper, + _, + isCrouching, + isJumping, + jumpThrust, + isCloaking, + isNotRendered, + canSeeReallyFar + ) if TestFilter(NotSameTargetTest) => + val pstateToSave = pstate.copy(timestamp = 0) + val (lastMsg, lastTime, lastPosition, wasVisible, wasShooting) = ops.lastSeenStreamMessage.get(FilterGuid.guid) match { + case Some(SessionAvatarHandlers.LastUpstream(Some(msg), visible, shooting, time)) => (Some(msg), time, msg.pos, visible, shooting) + case _ => (None, 0L, Vector3.Zero, false, None) + } + val drawConfig = Config.app.game.playerDraw //m + val maxRange = drawConfig.rangeMax * drawConfig.rangeMax //sq.m + val ourPosition = player.Position //xyz + val currentDistance = Vector3.DistanceSquared(ourPosition, pos) //sq.m + val inDrawableRange = currentDistance <= maxRange + val now = System.currentTimeMillis() //ms + if ( + sessionLogic.zoning.zoningStatus != Zoning.Status.Deconstructing && + !isNotRendered && inDrawableRange + ) { + //conditions where visibility is assured + val durationSince = now - lastTime //ms + lazy val previouslyInDrawableRange = Vector3.DistanceSquared(ourPosition, lastPosition) <= maxRange + lazy val targetDelay = { + val populationOver = math.max( + 0, + sessionLogic.localSector.livePlayerList.size - drawConfig.populationThreshold + ) + val distanceAdjustment = math.pow(populationOver / drawConfig.populationStep * drawConfig.rangeStep, 2) //sq.m + val adjustedDistance = currentDistance + distanceAdjustment //sq.m + drawConfig.ranges.lastIndexWhere { dist => adjustedDistance > dist * dist } match { + case -1 => 1 + case index => drawConfig.delays(index) + } + } //ms + if (!wasVisible || + !previouslyInDrawableRange || + durationSince > drawConfig.delayMax || + (!lastMsg.contains(pstateToSave) && + (canSeeReallyFar || + currentDistance < drawConfig.rangeMin * drawConfig.rangeMin || + sessionLogic.general.canSeeReallyFar || + durationSince > targetDelay + ) + ) ) { - //conditions where visibility is assured - val durationSince = now - lastTime //ms - lazy val previouslyInDrawableRange = Vector3.DistanceSquared(ourPosition, lastPosition) <= maxRange - lazy val targetDelay = { - val populationOver = math.max( - 0, - sessionLogic.localSector.livePlayerList.size - drawConfig.populationThreshold + //must draw + sendResponse( + PlayerStateMessage( + FilterGuid, + pos, + vel, + yaw, + pitch, + yawUpper, + timestamp = 0, //is this okay? + isCrouching, + isJumping, + jumpThrust, + isCloaking ) - val distanceAdjustment = math.pow(populationOver / drawConfig.populationStep * drawConfig.rangeStep, 2) //sq.m - val adjustedDistance = currentDistance + distanceAdjustment //sq.m - drawConfig.ranges.lastIndexWhere { dist => adjustedDistance > dist * dist } match { - case -1 => 1 - case index => drawConfig.delays(index) - } - } //ms - if (!wasVisible || - !previouslyInDrawableRange || - durationSince > drawConfig.delayMax || - (!lastMsg.contains(pstateToSave) && - (canSeeReallyFar || - currentDistance < drawConfig.rangeMin * drawConfig.rangeMin || - sessionLogic.general.canSeeReallyFar || - durationSince > targetDelay - ) - ) - ) { - //must draw - sendResponse( - PlayerStateMessage( - guid, - pos, - vel, - yaw, - pitch, - yawUpper, - timestamp = 0, //is this okay? - isCrouching, - isJumping, - jumpThrust, - isCloaking - ) - ) - ops.lastSeenStreamMessage.put(guid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, wasShooting, now)) - } else { - //is visible, but skip reinforcement - ops.lastSeenStreamMessage.put(guid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, wasShooting, lastTime)) - } + ) + ops.lastSeenStreamMessage.put(FilterGuid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, wasShooting, now)) } else { - //conditions where the target is not currently visible - if (wasVisible) { - //the target was JUST PREVIOUSLY visible; one last draw to move target beyond a renderable distance - val lat = (1 + ops.hidingPlayerRandomizer.nextInt(continent.map.scale.height.toInt)).toFloat - sendResponse( - PlayerStateMessage( - guid, - Vector3(1f, lat, 1f), - vel=None, - facingYaw=0f, - facingPitch=0f, - facingYawUpper=0f, - timestamp=0, //is this okay? - is_cloaked = isCloaking - ) + //is visible, but skip reinforcement + ops.lastSeenStreamMessage.put(FilterGuid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, wasShooting, lastTime)) + } + } else { + //conditions where the target is not currently visible + if (wasVisible) { + //the target was JUST PREVIOUSLY visible; one last draw to move target beyond a renderable distance + val lat = (1 + ops.hidingPlayerRandomizer.nextInt(continent.map.scale.height.toInt)).toFloat + sendResponse( + PlayerStateMessage( + FilterGuid, + Vector3(1f, lat, 1f), + vel=None, + facingYaw=0f, + facingPitch=0f, + facingYawUpper=0f, + timestamp=0, //is this okay? + is_cloaked = isCloaking ) - ops.lastSeenStreamMessage.put(guid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, wasShooting, now)) - } else { - //skip drawing altogether - ops.lastSeenStreamMessage.put(guid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, wasShooting, lastTime)) + ) + ops.lastSeenStreamMessage.put(FilterGuid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, wasShooting, now)) + } else { + //skip drawing altogether + ops.lastSeenStreamMessage.put(FilterGuid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, wasShooting, lastTime)) + } + } + + case AvatarAction.AvatarImplant(ImplantAction.Add, implant_slot, value) + if TestFilter(() => { value == ImplantType.SecondWind.value }) => + sendResponse(AvatarImplantMessage(ResolvedGuid, ImplantAction.Add, implant_slot, 7)) + //second wind does not normally load its icon into the shortcut hotbar + avatar + .shortcuts + .zipWithIndex + .find { case (s, _) => s.isEmpty} + .foreach { case (_, index) => + sendResponse(CreateShortcutMessage(ResolvedGuid, index + 1, Some(ImplantType.SecondWind.shortcut))) + } + + case AvatarAction.AvatarImplant(ImplantAction.Remove, implant_slot, value) + if TestFilter(() => { value == ImplantType.SecondWind.value }) => + sendResponse(AvatarImplantMessage(ResolvedGuid, ImplantAction.Remove, implant_slot, value)) + //second wind does not normally unload its icon from the shortcut hotbar + val shortcut = { + val imp = ImplantType.SecondWind.shortcut + net.psforever.objects.avatar.Shortcut(imp.code, imp.tile) //case class + } + avatar + .shortcuts + .zipWithIndex + .find { case (s, _) => s.contains(shortcut) } + .foreach { case (_, index) => + sendResponse(CreateShortcutMessage(ResolvedGuid, index + 1, None)) + } + + case AvatarAction.AvatarImplant(action, implant_slot, value) => + sendResponse(AvatarImplantMessage(ResolvedGuid, action, implant_slot, value)) + + case AvatarAction.ObjectHeld(slot, _) + if TestFilter(() => { SameTarget && player.VisibleSlots.contains(slot) }) => + sendResponse(ObjectHeldMessage(FilterGuid, slot, unk1=true)) + //Stop using proximity terminals if player unholsters a weapon + continent.GUID(sessionLogic.terminals.usingMedicalTerminal).collect { + case term: Terminal with ProximityUnit => sessionLogic.terminals.StopUsingProximityUnit(term) + } + if (sessionLogic.zoning.zoningStatus == Zoning.Status.Deconstructing) { + sessionLogic.zoning.spawn.stopDeconstructing() + } + + case AvatarAction.ObjectHeld(slot, _) + if TestFilter(() => { SameTarget && slot > -1 }) => + sendResponse(ObjectHeldMessage(FilterGuid, slot, unk1=true)) + + case AvatarAction.ObjectHeld(_, _) + if TestFilter(SameTargetTest) => () + + case AvatarAction.ObjectHeld(_, previousSlot) => + sendResponse(ObjectHeldMessage(FilterGuid, previousSlot, unk1=false)) + + case ChangeFireState_Start(weaponGuid) + if TestFilter(() => { NotSameTarget && ops.lastSeenStreamMessage.get(FilterGuid.guid).exists { _.visible } }) => + sendResponse(ChangeFireStateMessage_Start(weaponGuid)) + val entry = ops.lastSeenStreamMessage(FilterGuid.guid) + ops.lastSeenStreamMessage.put(FilterGuid.guid, entry.copy(shooting = Some(weaponGuid))) + + case ChangeFireState_Stop(weaponGuid) + if TestFilter(() => { NotSameTarget && ops.lastSeenStreamMessage.get(FilterGuid.guid).exists { msg => msg.visible || msg.shooting.nonEmpty } }) => + sendResponse(ChangeFireStateMessage_Stop(weaponGuid)) + val entry = ops.lastSeenStreamMessage(FilterGuid.guid) + ops.lastSeenStreamMessage.put(FilterGuid.guid, entry.copy(shooting = None)) + + case AvatarAction.LoadCreatedPlayer(pkt) + if TestFilter(NotSameTargetTest) => + sendResponse(pkt) + + case AvatarAction.EquipmentCreatedInHand(pkt) + if TestFilter(NotSameTargetTest) => + sendResponse(pkt) + + case AvatarAction.PlanetsideStringAttribute(attributeType, attributeValue) => + sendResponse(PlanetsideStringAttributeMessage(FilterGuid, attributeType, attributeValue)) + + case AvatarAction.Destroy(victim, killer, weapon, pos) => + // guid = victim // killer = killer + sendResponse(DestroyMessage(victim, killer, weapon, pos)) + + case AvatarAction.DestroyDisplay(killer, victim, method, unk) => + sendResponse(ops.destroyDisplayMessage(killer, victim, method, unk)) + + case AvatarAction.TerminalOrderResult(terminalGuid, action, result) + if result && (action == TransactionType.Buy || action == TransactionType.Loadout) => + sendResponse(ItemTransactionResultMessage(terminalGuid, action, result)) + sessionLogic.terminals.lastTerminalOrderFulfillment = true + AvatarActor.savePlayerData(player) + sessionLogic.general.renewCharSavedTimer( + Config.app.game.savedMsg.interruptedByAction.fixed, + Config.app.game.savedMsg.interruptedByAction.variable + ) + + case AvatarAction.TerminalOrderResult(terminalGuid, action, result) => + sendResponse(ItemTransactionResultMessage(terminalGuid, action, result)) + sessionLogic.terminals.lastTerminalOrderFulfillment = true + + case AvatarAction.ChangeExosuit( + target, + armor, + exosuit, + subtype, + _, + maxhand, + oldHolsters, + holsters, + oldInventory, + inventory, + drop, + delete + ) if TestFilter(() => { ResolvedGuid == target }) => + sendResponse(ArmorChangedMessage(target, exosuit, subtype)) + sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) + //happening to this player + //cleanup + sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=false)) + (oldHolsters ++ oldInventory ++ delete).foreach { + case (_, dguid) => sendResponse(ObjectDeleteMessage(dguid, unk1=0)) + } + //functionally delete + if (delete.size > 1 || delete.nonEmpty && !delete.exists { + case (e: Tool, _) => GlobalDefinitions.isMaxArms(e.Definition) + case _ => false + }) { + /* + if going x -> max, you will have enough space in max inventory for any displaced holster equipment + for max -> max, don't care about the max weapon arm being deleted (allow for 1) + for any other x -> x, any deleted equipment will raise this comment + */ + sendResponse(ChatMsg(ChatMessageType.UNK_227, "@ItemsDeconstructed")) + } + delete.foreach { case (obj, _) => TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) } + //redraw + if (maxhand) { + sendResponse(PlanetsideAttributeMessage(target, attribute_type=7, player.Capacitor.toLong)) + val maxArmDefinition = GlobalDefinitions.MAXArms(subtype, player.Faction) + TaskWorkflow.execute(HoldNewEquipmentUp(player)(Tool(maxArmDefinition), slot = 0)) + player.avatar.purchaseCooldown(maxArmDefinition) + .collect(a => a) + .getOrElse { + avatarActor ! AvatarActor.UpdatePurchaseTime(maxArmDefinition) + None } - } - - case AvatarResponse.AvatarImplant(ImplantAction.Add, implant_slot, value) - if value == ImplantType.SecondWind.value => - sendResponse(AvatarImplantMessage(resolvedPlayerGuid, ImplantAction.Add, implant_slot, 7)) - //second wind does not normally load its icon into the shortcut hotbar - avatar - .shortcuts - .zipWithIndex - .find { case (s, _) => s.isEmpty} - .foreach { case (_, index) => - sendResponse(CreateShortcutMessage(resolvedPlayerGuid, index + 1, Some(ImplantType.SecondWind.shortcut))) - } - - case AvatarResponse.AvatarImplant(ImplantAction.Remove, implant_slot, value) - if value == ImplantType.SecondWind.value => - sendResponse(AvatarImplantMessage(resolvedPlayerGuid, ImplantAction.Remove, implant_slot, value)) - //second wind does not normally unload its icon from the shortcut hotbar - val shortcut = { - val imp = ImplantType.SecondWind.shortcut - net.psforever.objects.avatar.Shortcut(imp.code, imp.tile) //case class - } - avatar - .shortcuts - .zipWithIndex - .find { case (s, _) => s.contains(shortcut) } - .foreach { case (_, index) => - sendResponse(CreateShortcutMessage(resolvedPlayerGuid, index + 1, None)) - } - - case AvatarResponse.AvatarImplant(action, implant_slot, value) => - sendResponse(AvatarImplantMessage(resolvedPlayerGuid, action, implant_slot, value)) - - case AvatarResponse.ObjectHeld(slot, _) - if isSameTarget && player.VisibleSlots.contains(slot) => - sendResponse(ObjectHeldMessage(guid, slot, unk1=true)) - //Stop using proximity terminals if player unholsters a weapon - continent.GUID(sessionLogic.terminals.usingMedicalTerminal).collect { - case term: Terminal with ProximityUnit => sessionLogic.terminals.StopUsingProximityUnit(term) - } - if (sessionLogic.zoning.zoningStatus == Zoning.Status.Deconstructing) { - sessionLogic.zoning.spawn.stopDeconstructing() - } - - case AvatarResponse.ObjectHeld(slot, _) - if isSameTarget && slot > -1 => - sendResponse(ObjectHeldMessage(guid, slot, unk1=true)) - - case AvatarResponse.ObjectHeld(_, _) - if isSameTarget => () - - case AvatarResponse.ObjectHeld(_, previousSlot) => - sendResponse(ObjectHeldMessage(guid, previousSlot, unk1=false)) - - case AvatarResponse.ChangeFireState_Start(weaponGuid) - if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { _.visible } => - sendResponse(ChangeFireStateMessage_Start(weaponGuid)) - val entry = ops.lastSeenStreamMessage(guid.guid) - ops.lastSeenStreamMessage.put(guid.guid, entry.copy(shooting = Some(weaponGuid))) - - case AvatarResponse.ChangeFireState_Start(weaponGuid) - if isNotSameTarget => - sendResponse(ChangeFireStateMessage_Start(weaponGuid)) - - case AvatarResponse.ChangeFireState_Stop(weaponGuid) - if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { msg => msg.visible || msg.shooting.nonEmpty } => - sendResponse(ChangeFireStateMessage_Stop(weaponGuid)) - val entry = ops.lastSeenStreamMessage(guid.guid) - ops.lastSeenStreamMessage.put(guid.guid, entry.copy(shooting = None)) - - case AvatarResponse.ChangeFireState_Stop(weaponGuid) - if isNotSameTarget => - sendResponse(ChangeFireStateMessage_Stop(weaponGuid)) - - case AvatarResponse.LoadPlayer(pkt) if isNotSameTarget => - sendResponse(pkt) - - case AvatarResponse.EquipmentInHand(pkt) if isNotSameTarget => - sendResponse(pkt) - - case AvatarResponse.PlanetsideAttribute(attributeType, attributeValue) if isNotSameTarget => - sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue)) - - case AvatarResponse.PlanetsideAttributeToAll(attributeType, attributeValue) => - sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue)) - - case AvatarResponse.PlanetsideAttributeSelf(attributeType, attributeValue) if isSameTarget => - sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue)) - - case AvatarResponse.PlanetsideStringAttribute(attributeType, attributeValue) => - sendResponse(PlanetsideStringAttributeMessage(guid, attributeType, attributeValue)) - - case AvatarResponse.GenericObjectAction(objectGuid, actionCode) if isNotSameTarget => - sendResponse(GenericObjectActionMessage(objectGuid, actionCode)) - - case AvatarResponse.HitHint(sourceGuid) if player.isAlive => - sendResponse(HitHint(sourceGuid, guid)) - sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg") - - case AvatarResponse.Destroy(victim, killer, weapon, pos) => - // guid = victim // killer = killer - sendResponse(DestroyMessage(victim, killer, weapon, pos)) - - case AvatarResponse.DestroyDisplay(killer, victim, method, unk) => - sendResponse(ops.destroyDisplayMessage(killer, victim, method, unk)) - - case AvatarResponse.TerminalOrderResult(terminalGuid, action, result) - if result && (action == TransactionType.Buy || action == TransactionType.Loadout) => - sendResponse(ItemTransactionResultMessage(terminalGuid, action, result)) - sessionLogic.terminals.lastTerminalOrderFulfillment = true - AvatarActor.savePlayerData(player) - sessionLogic.general.renewCharSavedTimer( - Config.app.game.savedMsg.interruptedByAction.fixed, - Config.app.game.savedMsg.interruptedByAction.variable + } + //draw free hand + player.FreeHand.Equipment.foreach { obj => + val definition = obj.Definition + sendResponse( + ObjectCreateDetailedMessage( + definition.ObjectId, + obj.GUID, + ObjectCreateMessageParent(target, Player.FreeHandSlot), + definition.Packet.DetailedConstructorData(obj).get + ) ) - - case AvatarResponse.TerminalOrderResult(terminalGuid, action, result) => - sendResponse(ItemTransactionResultMessage(terminalGuid, action, result)) - sessionLogic.terminals.lastTerminalOrderFulfillment = true - - case AvatarResponse.ChangeExosuit( - target, - armor, - exosuit, - subtype, - _, - maxhand, - oldHolsters, - holsters, - oldInventory, - inventory, - drop, - delete - ) if resolvedPlayerGuid == target => - sendResponse(ArmorChangedMessage(target, exosuit, subtype)) - sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) - //happening to this player - //cleanup - sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=false)) - (oldHolsters ++ oldInventory ++ delete).foreach { - case (_, dguid) => sendResponse(ObjectDeleteMessage(dguid, unk1=0)) - } - //functionally delete - if (delete.size > 1 || delete.nonEmpty && !delete.exists { - case (e: Tool, _) => GlobalDefinitions.isMaxArms(e.Definition) - case _ => false - }) { - /* - if going x -> max, you will have enough space in max inventory for any displaced holster equipment - for max -> max, don't care about the max weapon arm being deleted (allow for 1) - for any other x -> x, any deleted equipment will raise this comment - */ - sendResponse(ChatMsg(ChatMessageType.UNK_227, "@ItemsDeconstructed")) - } - delete.foreach { case (obj, _) => TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) } - //redraw - if (maxhand) { - sendResponse(PlanetsideAttributeMessage(target, attribute_type=7, player.Capacitor.toLong)) - val maxArmDefinition = GlobalDefinitions.MAXArms(subtype, player.Faction) - TaskWorkflow.execute(HoldNewEquipmentUp(player)(Tool(maxArmDefinition), slot = 0)) - player.avatar.purchaseCooldown(maxArmDefinition) - .collect(a => a) - .getOrElse { - avatarActor ! AvatarActor.UpdatePurchaseTime(maxArmDefinition) - None - } - } - //draw free hand - player.FreeHand.Equipment.foreach { obj => + } + //draw holsters and inventory + (holsters ++ inventory).foreach { + case InventoryItem(obj, index) => val definition = obj.Definition sendResponse( ObjectCreateDetailedMessage( definition.ObjectId, obj.GUID, - ObjectCreateMessageParent(target, Player.FreeHandSlot), + ObjectCreateMessageParent(target, index), definition.Packet.DetailedConstructorData(obj).get ) ) - } - //draw holsters and inventory - (holsters ++ inventory).foreach { - case InventoryItem(obj, index) => - val definition = obj.Definition - sendResponse( - ObjectCreateDetailedMessage( - definition.ObjectId, - obj.GUID, - ObjectCreateMessageParent(target, index), - definition.Packet.DetailedConstructorData(obj).get - ) + } + DropLeftovers(player)(drop) + //deactivate non-passive implants + avatarActor ! AvatarActor.DeactivateActiveImplants + + case AvatarAction.ChangeExosuit(target, armor, exosuit, subtype, slot, _, oldHolsters, holsters, _, _, drop, delete) => + sendResponse(ArmorChangedMessage(target, exosuit, subtype)) + sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) + //happening to some other player + sendResponse(ObjectHeldMessage(target, slot, unk1 = false)) + //cleanup + val dropPred = ContainableBehavior.DropPredicate(player) + val deleteFromDrop = drop.filterNot(dropPred) + (oldHolsters ++ delete ++ deleteFromDrop.map(f =>(f.obj, f.GUID))) + .distinctBy(_._2) + .foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) } + //draw holsters + holsters.foreach { + case InventoryItem(obj, index) => + val definition = obj.Definition + sendResponse( + ObjectCreateMessage( + definition.ObjectId, + obj.GUID, + ObjectCreateMessageParent(target, index), + definition.Packet.ConstructorData(obj).get ) - } - DropLeftovers(player)(drop) - //deactivate non-passive implants - avatarActor ! AvatarActor.DeactivateActiveImplants - - case AvatarResponse.ChangeExosuit(target, armor, exosuit, subtype, slot, _, oldHolsters, holsters, _, _, drop, delete) => - sendResponse(ArmorChangedMessage(target, exosuit, subtype)) - sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) - //happening to some other player - sendResponse(ObjectHeldMessage(target, slot, unk1 = false)) - //cleanup - val dropPred = ContainableBehavior.DropPredicate(player) - val deleteFromDrop = drop.filterNot(dropPred) - (oldHolsters ++ delete ++ deleteFromDrop.map(f =>(f.obj, f.GUID))) - .distinctBy(_._2) - .foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) } - //draw holsters - holsters.foreach { - case InventoryItem(obj, index) => - val definition = obj.Definition - sendResponse( - ObjectCreateMessage( - definition.ObjectId, - obj.GUID, - ObjectCreateMessageParent(target, index), - definition.Packet.ConstructorData(obj).get - ) - ) - } - - case AvatarResponse.ChangeLoadout( - target, - armor, - exosuit, - subtype, - _, - maxhand, - oldHolsters, - holsters, - oldInventory, - inventory, - drops - ) if resolvedPlayerGuid == target => - sendResponse(ArmorChangedMessage(target, exosuit, subtype)) - sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) - //happening to this player - sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=true)) - //cleanup - (oldHolsters ++ oldInventory).foreach { - case (obj, objGuid) => - sendResponse(ObjectDeleteMessage(objGuid, unk1=0)) - TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) - } - drops.foreach(item => sendResponse(ObjectDeleteMessage(item.obj.GUID, unk1=0))) - //redraw - if (maxhand) { - val maxArmWeapon = GlobalDefinitions.MAXArms(subtype, player.Faction) - sendResponse(PlanetsideAttributeMessage(target, attribute_type=7, player.Capacitor.toLong)) - TaskWorkflow.execute(HoldNewEquipmentUp(player)(Tool(maxArmWeapon), slot = 0)) - player.avatar.purchaseCooldown(maxArmWeapon) - if (!oldHolsters.exists { case (e, _) => e.Definition == maxArmWeapon } && - player.avatar.purchaseCooldown(maxArmWeapon).isEmpty) { - avatarActor ! AvatarActor.UpdatePurchaseTime(maxArmWeapon) //switching for first time causes cooldown - } - } - sessionLogic.general.applyPurchaseTimersBeforePackingLoadout(player, player, holsters ++ inventory) - DropLeftovers(player)(drops) - //deactivate non-passive implants - avatarActor ! AvatarActor.DeactivateActiveImplants - - case AvatarResponse.ChangeLoadout(target, armor, exosuit, subtype, slot, _, oldHolsters, _, _, _, _) => - //redraw handled by callbacks - sendResponse(ArmorChangedMessage(target, exosuit, subtype)) - sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) - //happening to some other player - sendResponse(ObjectHeldMessage(target, slot, unk1=false)) - //cleanup - oldHolsters.foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) } - - case AvatarResponse.UseKit(kguid, kObjId) => - sendResponse( - UseItemMessage( - resolvedPlayerGuid, - kguid, - resolvedPlayerGuid, - unk2 = 4294967295L, - unk3 = false, - unk4 = Vector3.Zero, - unk5 = Vector3.Zero, - unk6 = 126, - unk7 = 0, //sequence time? - unk8 = 137, - kObjId ) + } + + case AvatarAction.ChangeLoadout( + target, + armor, + exosuit, + subtype, + _, + maxhand, + oldHolsters, + holsters, + oldInventory, + inventory, + drops + ) if TestFilter(() => { ResolvedGuid == target }) => + sendResponse(ArmorChangedMessage(target, exosuit, subtype)) + sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) + //happening to this player + sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=true)) + //cleanup + (oldHolsters ++ oldInventory).foreach { + case (obj, objGuid) => + sendResponse(ObjectDeleteMessage(objGuid, unk1=0)) + TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) + } + drops.foreach(item => sendResponse(ObjectDeleteMessage(item.obj.GUID, unk1=0))) + //redraw + if (maxhand) { + val maxArmWeapon = GlobalDefinitions.MAXArms(subtype, player.Faction) + sendResponse(PlanetsideAttributeMessage(target, attribute_type=7, player.Capacitor.toLong)) + TaskWorkflow.execute(HoldNewEquipmentUp(player)(Tool(maxArmWeapon), slot = 0)) + player.avatar.purchaseCooldown(maxArmWeapon) + if (!oldHolsters.exists { case (e, _) => e.Definition == maxArmWeapon } && + player.avatar.purchaseCooldown(maxArmWeapon).isEmpty) { + avatarActor ! AvatarActor.UpdatePurchaseTime(maxArmWeapon) //switching for first time causes cooldown + } + } + sessionLogic.general.applyPurchaseTimersBeforePackingLoadout(player, player, holsters ++ inventory) + DropLeftovers(player)(drops) + //deactivate non-passive implants + avatarActor ! AvatarActor.DeactivateActiveImplants + + case AvatarAction.ChangeLoadout(target, armor, exosuit, subtype, slot, _, oldHolsters, _, _, _, _) => + //redraw handled by callbacks + sendResponse(ArmorChangedMessage(target, exosuit, subtype)) + sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) + //happening to some other player + sendResponse(ObjectHeldMessage(target, slot, unk1=false)) + //cleanup + oldHolsters.foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) } + + case AvatarAction.UseKit(kguid, kObjId) => + sendResponse( + UseItemMessage( + ResolvedGuid, + kguid, + ResolvedGuid, + unk2 = 4294967295L, + unk3 = false, + unk4 = Vector3.Zero, + unk5 = Vector3.Zero, + unk6 = 126, + unk7 = 0, //sequence time? + unk8 = 137, + kObjId ) - sendResponse(ObjectDeleteMessage(kguid, unk1=0)) + ) + sendResponse(ObjectDeleteMessage(kguid, unk1=0)) - case AvatarResponse.KitNotUsed(_, "") => - sessionLogic.general.kitToBeUsed = None + case AvatarAction.KitNotUsed(_, "") => + sessionLogic.general.kitToBeUsed = None - case AvatarResponse.KitNotUsed(_, msg) => - sessionLogic.general.kitToBeUsed = None - sendResponse(ChatMsg(ChatMessageType.UNK_225, msg)) + case AvatarAction.KitNotUsed(_, msg) => + sessionLogic.general.kitToBeUsed = None + sendResponse(ChatMsg(ChatMessageType.UNK_225, msg)) - case AvatarResponse.UpdateKillsDeathsAssists(_, kda) => - avatarActor ! AvatarActor.UpdateKillsDeathsAssists(kda) + case AvatarAction.UpdateKillsDeathsAssists(_, kda) => + avatarActor ! AvatarActor.UpdateKillsDeathsAssists(kda) - case AvatarResponse.AwardBep(charId, bep, expType) => - //if the target player, always award (some) BEP - if (charId == player.CharId) { - avatarActor ! AvatarActor.AwardBep(bep, expType) - } + case AvatarAction.AwardBep(charId, bep, expType) => + //if the target player, always award (some) BEP + if (charId == player.CharId) { + avatarActor ! AvatarActor.AwardBep(bep, expType) + } - case AvatarResponse.AwardCep(charId, cep) => - //if the target player, always award (some) CEP - if (charId == player.CharId) { - avatarActor ! AvatarActor.AwardCep(cep) - } + case AvatarAction.AwardCep(charId, cep) => + //if the target player, always award (some) CEP + if (charId == player.CharId) { + avatarActor ! AvatarActor.AwardCep(cep) + } - case AvatarResponse.FacilityCaptureRewards(buildingId, zoneNumber, cep) => - ops.facilityCaptureRewards(buildingId, zoneNumber, cep) + case AvatarAction.FacilityCaptureRewards(buildingId, zoneNumber, cep) => + ops.facilityCaptureRewards(buildingId, zoneNumber, cep) - case AvatarResponse.ShareKillExperienceWithSquad(killer, exp) => - ops.shareKillExperienceWithSquad(killer, exp) + case AvatarAction.ShareKillExperienceWithSquad(killer, exp) => + ops.shareKillExperienceWithSquad(killer, exp) - case AvatarResponse.ShareAntExperienceWithSquad(owner, exp, vehicle) => - ops.shareAntExperienceWithSquad(owner, exp, vehicle) + case AvatarAction.ShareAntExperienceWithSquad(owner, exp, vehicle) => + ops.shareAntExperienceWithSquad(owner, exp, vehicle) - case AvatarResponse.RemoveFromOutfitChat(outfit_id) => - ops.removeFromOutfitChat(outfit_id) + case AvatarAction.RemoveFromOutfitChat(outfit_id) => + ops.removeFromOutfitChat(outfit_id) - case AvatarResponse.SendResponse(msg) => - sendResponse(msg) + /* common messages (maybe once every respawn) */ + case ReloadTool(itemGuid) + if TestFilter(() => { NotSameTarget && ops.lastSeenStreamMessage.get(FilterGuid.guid).exists { _.visible } }) => + sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0)) - case AvatarResponse.SendResponseTargeted(targetGuid, msg) if resolvedPlayerGuid == targetGuid => - sendResponse(msg) - - /* common messages (maybe once every respawn) */ - case AvatarResponse.Reload(itemGuid) - if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { _.visible } => - sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0)) - - case AvatarResponse.Killed(cause, mount) => - //log and chat messages - //destroy display - val zoneChannel = continent.id - val events = continent.AvatarEvents - val pentry = PlayerSource(player) - cause - .adversarial - .collect { case out @ Adversarial(attacker, _, _) if attacker != PlayerSource.Nobody => out } - .orElse { - player.LastDamage.collect { - case attack if System.currentTimeMillis() - attack.interaction.hitTime < (10 seconds).toMillis => - attack - .adversarial - .collect { case out @ Adversarial(attacker, _, _) if attacker != PlayerSource.Nobody => out } - }.flatten - } match { - case Some(adversarial) => - events ! AvatarServiceMessage( - zoneChannel, - AvatarAction.DestroyDisplay(adversarial.attacker, pentry, adversarial.implement) - ) - case _ => - events ! AvatarServiceMessage(zoneChannel, AvatarAction.DestroyDisplay(pentry, pentry, 0)) - } - //events chat and log - val excuse = player.LastDamage.flatMap { damage => - val interaction = damage.interaction - val reason = interaction.cause - val adversarial = interaction.adversarial.map { _.attacker } - reason match { - case r: ExplodingEntityReason if r.entity.isInstanceOf[VehicleSpawnPad] => - //also, @SVCP_Killed_TooCloseToPadOnCreate^n~ or "... within n meters of pad ..." - sendResponse(ChatMsg(ChatMessageType.UNK_227, "@SVCP_Killed_OnPadOnCreate")) - case _ => () - } - adversarial.map {_.Name }.orElse { Some(s"a ${reason.getClass.getSimpleName}") } - }.getOrElse { s"an unfortunate circumstance (probably ${player.Sex.pronounObject} own fault)" } - log.info(s"${player.Name} has died, killed by $excuse") - if (sessionLogic.shooting.shotsWhileDead > 0) { - log.warn( - s"SHOTS_WHILE_DEAD: client of ${avatar.name} fired ${sessionLogic.shooting.shotsWhileDead} rounds while character was dead on server" + case AvatarAction.Killed(cause, mount) => + //log and chat messages + //destroy display + val zoneChannel = continent.id + val events = continent.AvatarEvents + val pentry = PlayerSource(player) + cause + .adversarial + .collect { case out @ Adversarial(attacker, _, _) if attacker != PlayerSource.Nobody => out } + .orElse { + player.LastDamage.collect { + case attack if System.currentTimeMillis() - attack.interaction.hitTime < (10 seconds).toMillis => + attack + .adversarial + .collect { case out @ Adversarial(attacker, _, _) if attacker != PlayerSource.Nobody => out } + }.flatten + } match { + case Some(adversarial) => + events ! MessageEnvelope( + zoneChannel, + AvatarAction.DestroyDisplay(adversarial.attacker, pentry, adversarial.implement) ) - sessionLogic.shooting.shotsWhileDead = 0 + case _ => + events ! MessageEnvelope(zoneChannel, AvatarAction.DestroyDisplay(pentry, pentry, 0)) + } + //events chat and log + val excuse = player.LastDamage.flatMap { damage => + val interaction = damage.interaction + val reason = interaction.cause + val adversarial = interaction.adversarial.map { _.attacker } + reason match { + case r: ExplodingEntityReason if r.entity.isInstanceOf[VehicleSpawnPad] => + //also, @SVCP_Killed_TooCloseToPadOnCreate^n~ or "... within n meters of pad ..." + sendResponse(ChatMsg(ChatMessageType.UNK_227, "@SVCP_Killed_OnPadOnCreate")) + case _ => () } - //TODO other methods of death? - sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason(msg = "cancel") - sessionLogic.general.renewCharSavedTimer(fixedLen = 1800L, varLen = 0L) - continent.actor ! ZoneActor.RewardThisDeath(player) - - //player state changes - sessionLogic.zoning.spawn.avatarActive = false - AvatarActor.updateToolDischargeFor(avatar) - player.FreeHand.Equipment.foreach { item => - DropEquipmentFromInventory(player)(item) - } - sessionLogic.general.dropSpecialSlotItem() - sessionLogic.general.toggleMaxSpecialState(enable = false) - sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive - sessionLogic.zoning.zoningStatus = Zoning.Status.None - sessionLogic.zoning.spawn.deadState = DeadState.Dead - continent.GUID(mount).collect { - case obj: Vehicle => - killedWhileMounted(obj, resolvedPlayerGuid) - sessionLogic.vehicles.ConditionalDriverVehicleControl(obj) - sessionLogic.general.unaccessContainer(obj) - - case obj: PlanetSideGameObject with Mountable with Container => - killedWhileMounted(obj, resolvedPlayerGuid) - sessionLogic.general.unaccessContainer(obj) - - case obj: PlanetSideGameObject with Mountable => - killedWhileMounted(obj, resolvedPlayerGuid) - } - sessionLogic.actionsToCancel() - sessionLogic.terminals.CancelAllProximityUnits() - AvatarActor.savePlayerLocation(player) - sessionLogic.zoning.spawn.ShiftPosition = Some(player.Position) - - //respawn - val respawnTimer = 300000 //milliseconds - sendResponse(AvatarDeadStateMessage(DeadState.Dead, respawnTimer, respawnTimer, player.Position, player.Faction, unk5=true)) - sessionLogic.zoning.spawn.reviveTimer.cancel() - sessionLogic.zoning.spawn.reviveTimer = Default.Cancellable - if (player.death_by == 0) { - sessionLogic.zoning.spawn.randomRespawn(300.seconds) - } else { - sessionLogic.zoning.spawn.HandleReleaseAvatar(player, continent) - } - - case AvatarResponse.Release(tplayer) if isNotSameTarget => - sessionLogic.zoning.spawn.DepictPlayerAsCorpse(tplayer) - - case AvatarResponse.Revive(revivalTargetGuid) - if resolvedPlayerGuid == revivalTargetGuid => - log.info(s"No time for rest, ${player.Name}. Back on your feet!") - ops.revive() - player.Actor ! Player.Revive - player.History - .findLast { _.isInstanceOf[RevivingActivity] } - .map { - case activity: RevivingActivity - if System.currentTimeMillis() - activity.time < 5000L => - val reviveMessage = s"@YouHaveBeenMessage^revived~^${activity.user.unique.name}~" - sendResponse(ChatMsg(ChatMessageType.UNK_227, reviveMessage)) - None - } - - /* uncommon messages (utility, or once in a while) */ - case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) - if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { _.visible } => - ops.changeAmmoProcedures(weapon_guid, previous_guid, ammo_id, ammo_guid, weapon_slot, ammo_data) - sendResponse(ChangeAmmoMessage(weapon_guid, 1)) - - case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) - if isNotSameTarget => - ops.changeAmmoProcedures(weapon_guid, previous_guid, ammo_id, ammo_guid, weapon_slot, ammo_data) - - case AvatarResponse.ChangeFireMode(itemGuid, mode) if isNotSameTarget => - sendResponse(ChangeFireModeMessage(itemGuid, mode)) - - case AvatarResponse.ConcealPlayer() => - sendResponse(GenericObjectActionMessage(guid, code=9)) - - case AvatarResponse.EnvironmentalDamage(_, _, _) => - //TODO damage marker? - sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg") - - case AvatarResponse.DropItem(pkt) if isNotSameTarget => - sendResponse(pkt) - - case AvatarResponse.ObjectDelete(itemGuid, unk) if isNotSameTarget => - sendResponse(ObjectDeleteMessage(itemGuid, unk)) - - /* rare messages */ - case AvatarResponse.SetEmpire(objectGuid, faction) if isNotSameTarget => - sendResponse(SetEmpireMessage(objectGuid, faction)) - - case AvatarResponse.DropSpecialItem() => - sessionLogic.general.dropSpecialSlotItem() - - case AvatarResponse.OxygenState(player, vehicle) => - sendResponse(OxygenStateMessage( - DrowningTarget(player.guid, player.progress, player.state), - vehicle.flatMap { vinfo => Some(DrowningTarget(vinfo.guid, vinfo.progress, vinfo.state)) } - )) - - case AvatarResponse.LoadProjectile(pkt) if isNotSameTarget => - sendResponse(pkt) - - case AvatarResponse.ProjectileState(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid) if isNotSameTarget => - sendResponse(ProjectileStateMessage(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid)) - - case AvatarResponse.ProjectileExplodes(projectileGuid, projectile) => - sendResponse( - ProjectileStateMessage( - projectileGuid, - projectile.Position, - shot_vel = Vector3.Zero, - projectile.Orientation, - sequence_num=0, - end=true, - hit_target_guid=PlanetSideGUID(0) - ) + adversarial.map {_.Name }.orElse { Some(s"a ${reason.getClass.getSimpleName}") } + }.getOrElse { s"an unfortunate circumstance (probably ${player.Sex.pronounObject} own fault)" } + log.info(s"${player.Name} has died, killed by $excuse") + if (sessionLogic.shooting.shotsWhileDead > 0) { + log.warn( + s"SHOTS_WHILE_DEAD: client of ${avatar.name} fired ${sessionLogic.shooting.shotsWhileDead} rounds while character was dead on server" ) - sendResponse(ObjectDeleteMessage(projectileGuid, unk1=2)) + sessionLogic.shooting.shotsWhileDead = 0 + } + //TODO other methods of death? + sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason(msg = "cancel") + sessionLogic.general.renewCharSavedTimer(fixedLen = 1800L, varLen = 0L) + continent.actor ! ZoneActor.RewardThisDeath(player) - case AvatarResponse.ProjectileAutoLockAwareness(mode) => - sendResponse(GenericActionMessage(mode)) + //player state changes + sessionLogic.zoning.spawn.avatarActive = false + AvatarActor.updateToolDischargeFor(avatar) + player.FreeHand.Equipment.foreach { item => + DropEquipmentFromInventory(player)(item) + } + sessionLogic.general.dropSpecialSlotItem() + sessionLogic.general.toggleMaxSpecialState(enable = false) + sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive + sessionLogic.zoning.zoningStatus = Zoning.Status.None + sessionLogic.zoning.spawn.deadState = DeadState.Dead + continent.GUID(mount).collect { + case obj: Vehicle => + killedWhileMounted(obj, ResolvedGuid) + sessionLogic.vehicles.ConditionalDriverVehicleControl(obj) + sessionLogic.general.unaccessContainer(obj) - case AvatarResponse.PutDownFDU(target) if isNotSameTarget => - sendResponse(GenericObjectActionMessage(target, code=53)) + case obj: PlanetSideGameObject with Mountable with Container => + killedWhileMounted(obj, ResolvedGuid) + sessionLogic.general.unaccessContainer(obj) - case AvatarResponse.StowEquipment(target, slot, item) if isNotSameTarget => - val definition = item.Definition - sendResponse( - ObjectCreateDetailedMessage( - definition.ObjectId, - item.GUID, - ObjectCreateMessageParent(target, slot), - definition.Packet.DetailedConstructorData(item).get - ) - ) + case obj: PlanetSideGameObject with Mountable => + killedWhileMounted(obj, ResolvedGuid) + } + sessionLogic.actionsToCancel() + sessionLogic.terminals.CancelAllProximityUnits() + AvatarActor.savePlayerLocation(player) + sessionLogic.zoning.spawn.ShiftPosition = Some(player.Position) - case AvatarResponse.WeaponDryFire(weaponGuid) - if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { _.visible } => - continent.GUID(weaponGuid).collect { - case tool: Tool if tool.Magazine == 0 => - // check that the magazine is still empty before sending WeaponDryFireMessage - // if it has been reloaded since then, other clients will not see it firing - sendResponse(WeaponDryFireMessage(weaponGuid)) + //respawn + val respawnTimer = 300000 //milliseconds + sendResponse(AvatarDeadStateMessage(DeadState.Dead, respawnTimer, respawnTimer, player.Position, player.Faction, unk5=true)) + sessionLogic.zoning.spawn.reviveTimer.cancel() + sessionLogic.zoning.spawn.reviveTimer = Default.Cancellable + if (player.death_by == 0) { + sessionLogic.zoning.spawn.randomRespawn(300.seconds) + } else { + sessionLogic.zoning.spawn.HandleReleaseAvatar(player, continent) + } + + case AvatarAction.ReleasePlayer(tplayer) + if TestFilter(NotSameTargetTest) => + sessionLogic.zoning.spawn.DepictPlayerAsCorpse(tplayer) + + case AvatarAction.Revive(revivalTargetGuid) + if TestFilter(() => { ResolvedGuid == revivalTargetGuid }) => + log.info(s"No time for rest, ${player.Name}. Back on your feet!") + ops.revive() + player.Actor ! Player.Revive + player.History + .findLast { _.isInstanceOf[RevivingActivity] } + .map { + case activity: RevivingActivity + if System.currentTimeMillis() - activity.time < 5000L => + val reviveMessage = s"@YouHaveBeenMessage^revived~^${activity.user.unique.name}~" + sendResponse(ChatMsg(ChatMessageType.UNK_227, reviveMessage)) + None } - case _ => () - } + /* uncommon messages (utility, or once in a while) */ + case ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) + if TestFilter(NotSameTargetTest) => + ops.changeAmmoProcedure(weapon_guid, previous_guid, ammo_id, ammo_guid, weapon_slot, ammo_data) + sendResponse(ChangeAmmoMessage(weapon_guid, 1)) + + case AvatarAction.ChangeFireMode(itemGuid, mode) + if TestFilter(NotSameTargetTest) => + sendResponse(ChangeFireModeMessage(itemGuid, mode)) + + case AvatarAction.EnvironmentalDamage(_, _, _) => + //TODO damage marker? + sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg") + + case AvatarAction.DropCreatedItem(pkt) + if TestFilter(NotSameTargetTest) => + sendResponse(pkt) + + /* rare messages */ + case AvatarAction.DropSpecialItem() => + sessionLogic.general.dropSpecialSlotItem() + + case AvatarAction.OxygenState(player, vehicle) => + sendResponse(OxygenStateMessage( + DrowningTarget(player.guid, player.progress, player.state), + vehicle.flatMap { vinfo => Some(DrowningTarget(vinfo.guid, vinfo.progress, vinfo.state)) } + )) + + case AvatarAction.LoadCreatedProjectile(pkt) + if TestFilter(NotSameTargetTest) => + sendResponse(pkt) + + case AvatarAction.ProjectileState(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid) + if TestFilter(NotSameTargetTest) => + sendResponse(ProjectileStateMessage(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid)) + + case AvatarAction.ProjectileExplodes(projectileGuid, projectile) => + sendResponse( + ProjectileStateMessage( + projectileGuid, + projectile.Position, + shot_vel = Vector3.Zero, + projectile.Orientation, + sequence_num=0, + end=true, + hit_target_guid=PlanetSideGUID(0) + ) + ) + sendResponse(ObjectDeleteMessage(projectileGuid, unk1=2)) + + case AvatarAction.ProjectileAutoLockAwareness(mode) => + sendResponse(GenericActionMessage(mode)) + + case AvatarAction.PutDownFDU(target) + if TestFilter(NotSameTargetTest) => + sendResponse(GenericObjectActionMessage(target, code=53)) + + case AvatarAction.StowEquipment(target, slot, item) + if TestFilter(NotSameTargetTest) => + val definition = item.Definition + sendResponse( + ObjectCreateDetailedMessage( + definition.ObjectId, + item.GUID, + ObjectCreateMessageParent(target, slot), + definition.Packet.DetailedConstructorData(item).get + ) + ) + + case WeaponDryFire(weaponGuid) + if TestFilter(() => { NotSameTarget && ops.lastSeenStreamMessage.get(FilterGuid.guid).exists { _.visible } }) => + continent.GUID(weaponGuid).collect { + case tool: Tool if tool.Magazine == 0 => + sendResponse(WeaponDryFireMessage(weaponGuid)) + } } def killedWhileMounted(obj: PlanetSideGameObject with Mountable, playerGuid: PlanetSideGUID): Unit = { - val events = continent.AvatarEvents ops.killedWhileMounted(obj, playerGuid) - //make player invisible on client - events ! AvatarServiceMessage(player.Name, AvatarAction.PlanetsideAttributeToAll(playerGuid, 29, 1)) - //only the dead player should "see" their own body, so that the death camera has something to focus on - events ! AvatarServiceMessage(continent.id, AvatarAction.ObjectDelete(playerGuid, playerGuid)) + continent.AvatarEvents ! BundledEnvelope( + /* make player invisible on client */ + MessageEnvelope(player.Name, PlanetsideAttribute(playerGuid, 29, 1)), + /* only the dead player should "see" their own body, so that the death camera has something to focus on */ + MessageEnvelope(continent.id, playerGuid, ObjectDelete(playerGuid)) + ) } } diff --git a/src/main/scala/net/psforever/actors/session/normal/GalaxyHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/normal/GalaxyHandlerLogic.scala index fe2d4b873..5bc18cbf9 100644 --- a/src/main/scala/net/psforever/actors/session/normal/GalaxyHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/GalaxyHandlerLogic.scala @@ -1,11 +1,13 @@ // Copyright (c) 2024 PSForever package net.psforever.actors.session.normal +import akka.actor.Actor.Receive import akka.actor.{ActorContext, ActorRef, typed} import net.psforever.actors.session.AvatarActor -import net.psforever.actors.session.support.{GalaxyHandlerFunctions, SessionGalaxyHandlers, SessionData} +import net.psforever.actors.session.support.{GalaxyHandlerFunctions, SessionData, SessionGalaxyHandlers} import net.psforever.packet.game.{BroadcastWarpgateUpdateMessage, FriendsResponse, HotSpotUpdateMessage, ZoneInfoMessage, ZonePopulationUpdateMessage, HotSpotInfo => PacketHotSpotInfo} -import net.psforever.services.galaxy.{GalaxyAction, GalaxyResponse, GalaxyServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.galaxy.GalaxyAction import net.psforever.types.{MemberAction, PlanetSideEmpire} object GalaxyHandlerLogic { @@ -26,68 +28,62 @@ class GalaxyHandlerLogic(val ops: SessionGalaxyHandlers, implicit val context: A def handleUpdateIgnoredPlayers(pkt: FriendsResponse): Unit = { sendResponse(pkt) pkt.friends.foreach { f => - galaxyService ! GalaxyServiceMessage(GalaxyAction.LogStatusChange(f.name)) + galaxyService ! MessageEnvelope("", GalaxyAction.LogStatusChange(f.name)) } } /* response handlers */ - def handle(reply: GalaxyResponse.Response): Unit = { - reply match { - case GalaxyResponse.HotSpotUpdate(zone_index, priority, hot_spot_info) => - sendResponse( - HotSpotUpdateMessage( - zone_index, - priority, - hot_spot_info.map { spot => PacketHotSpotInfo(spot.DisplayLocation.x, spot.DisplayLocation.y, 40) } - ) + def receive: Receive = { + case GalaxyAction.HotSpotUpdate(zone_index, priority, hot_spot_info) => + sendResponse( + HotSpotUpdateMessage( + zone_index, + priority, + hot_spot_info.map { spot => PacketHotSpotInfo(spot.DisplayLocation.x, spot.DisplayLocation.y, 40) } ) + ) - case GalaxyResponse.MapUpdate(msg) => - sendResponse(msg) - import net.psforever.actors.zone.ZoneActor - import net.psforever.zones.Zones - Zones.zones.find(_.Number == msg.continent_id) match { - case Some(zone) => - zone.actor ! ZoneActor.BuildingInfoState(msg) - case None => - } + case GalaxyAction.MapUpdate(msg) => + sendResponse(msg) + import net.psforever.actors.zone.ZoneActor + import net.psforever.zones.Zones + Zones.zones.find(_.Number == msg.continent_id) match { + case Some(zone) => + zone.actor ! ZoneActor.BuildingInfoState(msg) + case None => + } - case GalaxyResponse.UpdateBroadcastPrivileges(zoneId, gateMapId, fromFactions, toFactions) => - val faction = player.Faction - val from = fromFactions.contains(faction) - val to = toFactions.contains(faction) - if (from && !to) { - sendResponse(BroadcastWarpgateUpdateMessage(zoneId, gateMapId, PlanetSideEmpire.NEUTRAL)) - } else if (!from && to) { - sendResponse(BroadcastWarpgateUpdateMessage(zoneId, gateMapId, faction)) - } + case GalaxyAction.UpdateBroadcastPrivileges(zoneId, gateMapId, fromFactions, toFactions) => + val faction = player.Faction + val from = fromFactions.contains(faction) + val to = toFactions.contains(faction) + if (from && !to) { + sendResponse(BroadcastWarpgateUpdateMessage(zoneId, gateMapId, PlanetSideEmpire.NEUTRAL)) + } else if (!from && to) { + sendResponse(BroadcastWarpgateUpdateMessage(zoneId, gateMapId, faction)) + } - case GalaxyResponse.FlagMapUpdate(msg) => - sendResponse(msg) + case GalaxyAction.FlagMapUpdate(msg) => + sendResponse(msg) - case GalaxyResponse.TransferPassenger(temp_channel, vehicle, _, manifest) => - sessionLogic.zoning.handleTransferPassenger(temp_channel, vehicle, manifest) + case GalaxyAction.TransferPassenger(_, temp_channel, vehicle, _, manifest) => + sessionLogic.zoning.handleTransferPassenger(temp_channel, vehicle, manifest) - case GalaxyResponse.LockedZoneUpdate(zone, time) => - sendResponse(ZoneInfoMessage(zone.Number, empire_status=false, lock_time=time)) + case GalaxyAction.LockedZoneUpdate(zone, time) => + sendResponse(ZoneInfoMessage(zone.Number, empire_status=false, lock_time=time)) - case GalaxyResponse.UnlockedZoneUpdate(zone) => - sendResponse(ZoneInfoMessage(zone.Number, empire_status=true, lock_time=0L)) - val popBO = 0 - val pop = zone.LivePlayers.distinctBy(_.CharId) - val popTR = pop.count(_.Faction == PlanetSideEmpire.TR) - val popNC = pop.count(_.Faction == PlanetSideEmpire.NC) - val popVS = pop.count(_.Faction == PlanetSideEmpire.VS) - sendResponse(ZonePopulationUpdateMessage(zone.Number, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO)) + case GalaxyAction.UnlockedZoneUpdate(zone) => + sendResponse(ZoneInfoMessage(zone.Number, empire_status=true, lock_time=0L)) + val popBO = 0 + val pop = zone.LivePlayers.distinctBy(_.CharId) + val popTR = pop.count(_.Faction == PlanetSideEmpire.TR) + val popNC = pop.count(_.Faction == PlanetSideEmpire.NC) + val popVS = pop.count(_.Faction == PlanetSideEmpire.VS) + sendResponse(ZonePopulationUpdateMessage(zone.Number, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO)) - case GalaxyResponse.LogStatusChange(name) if avatar.people.friend.exists(_.name.equals(name)) => - avatarActor ! AvatarActor.MemberListRequest(MemberAction.UpdateFriend, name) - - case GalaxyResponse.SendResponse(msg) => - sendResponse(msg) - - case _ => () - } + case GalaxyAction.LogStatusChange(name) + if TestFilter(() => avatar.people.friend.exists(_.name.equals(name))) => + avatarActor ! AvatarActor.MemberListRequest(MemberAction.UpdateFriend, name) } } 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 df8a88363..076df89ac 100644 --- a/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala @@ -42,8 +42,9 @@ import net.psforever.objects.zones.{ZoneProjectile, Zoning} 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.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.MessageEnvelope +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} import net.psforever.util.Config @@ -179,10 +180,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex case _ => false }) - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( continent.id, + avatarGuid, AvatarAction.PlayerState( - avatarGuid, player.Position, player.Velocity, yaw, @@ -196,7 +197,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex isNotVisible, eagleEye ) - ) + ) //todo CachedMessage sessionLogic.squad.updateSquad() if (player.death_by == -1) { sessionLogic.kickedByAdministration() @@ -213,15 +214,8 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex } def handleEmote(pkt: EmoteMsg): Unit = { - val EmoteMsg(avatarGuid, emote) = pkt - val pZone = player.Zone - sendResponse(EmoteMsg(avatarGuid, emote)) - pZone.blockMap.sector(player).livePlayerList.collect { case t if t.GUID != player.GUID => - pZone.LocalEvents ! LocalServiceMessage(t.Name, LocalAction.SendResponse(EmoteMsg(avatarGuid, emote))) - } - pZone.AllPlayers.collect { case t if t.GUID != player.GUID && !t.allowInteraction => - pZone.LocalEvents ! LocalServiceMessage(t.Name, LocalAction.SendResponse(EmoteMsg(avatarGuid, emote))) - } + sendResponse(pkt) + ops.handleEmote(pkt) } def handleDropItem(pkt: DropItemMessage): Unit = { @@ -525,9 +519,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex case GenericAction.MaxAnchorsExtend_RCV => log.info(s"${player.Name} has anchored ${player.Sex.pronounObject}self to the ground") player.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( continent.id, - AvatarAction.PlanetsideAttribute(player.GUID, 19, 1) + player.GUID, + PlanetsideAttribute(player.GUID, 19, 1) ) definition match { case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.trhev_burster => @@ -546,9 +541,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex case GenericAction.MaxAnchorsRelease_RCV => log.info(s"${player.Name} has released the anchors") player.UsingSpecial = SpecialExoSuitDefinition.Mode.Normal - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( continent.id, - AvatarAction.PlanetsideAttribute(player.GUID, 19, 0) + player.GUID, + PlanetsideAttribute(player.GUID, 19, 0) ) definition match { case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.trhev_burster => diff --git a/src/main/scala/net/psforever/actors/session/normal/LocalHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/normal/LocalHandlerLogic.scala index 2f12efd95..003a60b08 100644 --- a/src/main/scala/net/psforever/actors/session/normal/LocalHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/LocalHandlerLogic.scala @@ -1,17 +1,19 @@ // Copyright (c) 2024 PSForever package net.psforever.actors.session.normal +import akka.actor.Actor.Receive import akka.actor.ActorContext import net.psforever.actors.session.support.SpawnOperations.ActivityQueuedTask import net.psforever.actors.session.support.{LocalHandlerFunctions, SessionData, SessionLocalHandlers, SpawnOperations} import net.psforever.objects.ce.Deployable import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.vehicles.MountableWeapons -import net.psforever.objects.{BoomerDeployable, ExplosiveDeployable, TelepadDeployable, Tool, TurretDeployable} -import net.psforever.packet.game.{ChatMsg, DeployableObjectsInfoMessage, GenericActionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HackMessage, HackState, HackState1, InventoryStateMessage, ObjectAttachMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, OrbitalShuttleTimeMsg, PadAndShuttlePair, PlanetsideAttributeMessage, ProximityTerminalUseMessage, SetEmpireMessage, TriggerEffectMessage, TriggerSoundMessage, TriggeredSound, VehicleStateMessage} +import net.psforever.objects.{BoomerDeployable, Default, ExplosiveDeployable, TelepadDeployable, Tool, TurretDeployable} +import net.psforever.packet.game.{ChatMsg, DeployableObjectsInfoMessage, GenericActionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HackMessage, HackState, HackState1, InventoryStateMessage, ObjectAttachMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, OrbitalShuttleTimeMsg, PadAndShuttlePair, PlanetsideAttributeMessage, ProximityTerminalUseMessage, TriggerEffectMessage, TriggerSoundMessage, TriggeredSound, VehicleStateMessage} +import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} import net.psforever.services.{InterstellarClusterService, Service} -import net.psforever.services.local.LocalResponse -import net.psforever.types.{ChatMessageType, PlanetSideGUID, SpawnGroup} +import net.psforever.services.local.LocalAction +import net.psforever.types.{ChatMessageType, SpawnGroup} object LocalHandlerLogic { def apply(ops: SessionLocalHandlers): LocalHandlerLogic = { @@ -34,244 +36,218 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act /* response handlers */ - /** - * na - * @param toChannel na - * @param guid na - * @param reply na - */ - def handle(toChannel: String, guid: PlanetSideGUID, reply: LocalResponse.Response): Unit = { - val resolvedPlayerGuid = if (player.HasGUID) { - player.GUID - } else { - Service.defaultPlayerGUID - } - val isNotSameTarget = resolvedPlayerGuid != guid - reply match { - case LocalResponse.DeployableMapIcon(behavior, deployInfo) if isNotSameTarget => - sendResponse(DeployableObjectsInfoMessage(behavior, deployInfo)) - - case LocalResponse.DeployableUIFor(item) => - sessionLogic.general.updateDeployableUIElements(avatar.deployables.UpdateUIElement(item)) - - case LocalResponse.Detonate(dguid, _: BoomerDeployable) => - sendResponse(TriggerEffectMessage(dguid, "detonate_boomer")) - sendResponse(PlanetsideAttributeMessage(dguid, attribute_type=29, attribute_value=1)) - sendResponse(ObjectDeleteMessage(dguid, unk1=0)) - - case LocalResponse.Detonate(dguid, _: ExplosiveDeployable) => - sendResponse(GenericObjectActionMessage(dguid, code=19)) - sendResponse(PlanetsideAttributeMessage(dguid, attribute_type=29, attribute_value=1)) - sendResponse(ObjectDeleteMessage(dguid, unk1=0)) - - case LocalResponse.Detonate(_, obj) => - log.warn(s"LocalResponse.Detonate: ${obj.Definition.Name} not configured to explode correctly") - - case LocalResponse.DoorOpens(doorGuid) if isNotSameTarget => - val pos = player.Position.xy - val range = ops.doorLoadRange() - val foundDoor = continent - .blockMap - .sector(pos, range) - .amenityList - .collect { case door: Door => door } - .find(_.GUID == doorGuid) - val doorExistsInRange: Boolean = foundDoor.nonEmpty - if (doorExistsInRange) { - sendResponse(GenericObjectStateMsg(doorGuid, state=16)) - } - - case LocalResponse.DoorCloses(doorGuid) => //door closes for everyone - sendResponse(GenericObjectStateMsg(doorGuid, state=17)) - - case LocalResponse.EliminateDeployable(obj: TurretDeployable, dguid, _, _) if obj.Destroyed => - sendResponse(ObjectDeleteMessage(dguid, unk1=0)) - - case LocalResponse.EliminateDeployable(obj: TurretDeployable, dguid, pos, _) => - obj.Destroyed = true - ops.DeconstructDeployable( - obj, - dguid, - pos, - obj.Orientation, - deletionType= if (obj.MountPoints.isEmpty) { 2 } else { 1 } - ) - - case LocalResponse.EliminateDeployable(obj: ExplosiveDeployable, dguid, _, _) - if obj.Destroyed || obj.Jammed || obj.Health == 0 => - sendResponse(ObjectDeleteMessage(dguid, unk1=0)) - - case LocalResponse.EliminateDeployable(obj: ExplosiveDeployable, dguid, pos, effect) => - obj.Destroyed = true - ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect) - - case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Active && obj.Destroyed => - //if active, deactivate - obj.Active = false - ops.deactivateTelpadDeployableMessages(dguid) - //standard deployable elimination behavior - sendResponse(ObjectDeleteMessage(dguid, unk1=0)) - - case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) if obj.Active => - //if active, deactivate - obj.Active = false - ops.deactivateTelpadDeployableMessages(dguid) - //standard deployable elimination behavior - obj.Destroyed = true - ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2) - - case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) if obj.Destroyed => - //standard deployable elimination behavior - sendResponse(ObjectDeleteMessage(dguid, unk1=0)) - - case LocalResponse.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) => - //standard deployable elimination behavior - obj.Destroyed = true - ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2) - - case LocalResponse.EliminateDeployable(obj, dguid, _, _) if obj.Destroyed => - sendResponse(ObjectDeleteMessage(dguid, unk1=0)) - - case LocalResponse.EliminateDeployable(obj, dguid, pos, effect) => - obj.Destroyed = true - ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect) - - case LocalResponse.SendHackMessageHackCleared(targetGuid, unk1, unk2) => - sendResponse(HackMessage(HackState1.Unk0, targetGuid, guid, progress=0, unk1.toFloat, HackState.HackCleared, unk2)) - - case LocalResponse.HackObject(targetGuid, unk1, unk2) => - sessionLogic.general.hackObject(targetGuid, unk1, unk2) - - case LocalResponse.PlanetsideAttribute(targetGuid, attributeType, attributeValue) => - sessionLogic.general.sendPlanetsideAttributeMessage(targetGuid, attributeType, attributeValue) - - case LocalResponse.GenericObjectAction(targetGuid, actionNumber) => - sendResponse(GenericObjectActionMessage(targetGuid, actionNumber)) - - case LocalResponse.GenericActionMessage(actionNumber) => - sendResponse(GenericActionMessage(actionNumber)) - - case LocalResponse.ChatMessage(msg) => - sendResponse(msg) - - case LocalResponse.SendPacket(packet) => - sendResponse(packet) - - case LocalResponse.LluSpawned(llu) => - // Create LLU on client - sendResponse(ObjectCreateMessage( - llu.Definition.ObjectId, - llu.GUID, - llu.Definition.Packet.ConstructorData(llu).get - )) - sendResponse(TriggerSoundMessage(TriggeredSound.LLUMaterialize, llu.Position, unk=20, volume=0.8000001f)) - - case LocalResponse.LluDespawned(lluGuid, position) => - sendResponse(TriggerSoundMessage(TriggeredSound.LLUDeconstruct, position, unk=20, volume=0.8000001f)) - sendResponse(ObjectDeleteMessage(lluGuid, unk1=0)) - // If the player was holding the LLU, remove it from their tracked special item slot - sessionLogic.general.specialItemSlotGuid.collect { case guid if guid == lluGuid => - sessionLogic.general.specialItemSlotGuid = None - player.Carrying = None - } - - case LocalResponse.ObjectDelete(objectGuid, unk) if isNotSameTarget => - sendResponse(ObjectDeleteMessage(objectGuid, unk)) - - case LocalResponse.ProximityTerminalEffect(object_guid, true) => - sendResponse(ProximityTerminalUseMessage(Service.defaultPlayerGUID, object_guid, unk=true)) - - case LocalResponse.ProximityTerminalEffect(objectGuid, false) => - sendResponse(ProximityTerminalUseMessage(Service.defaultPlayerGUID, objectGuid, unk=false)) - sessionLogic.terminals.ForgetAllProximityTerminals(objectGuid) - - case LocalResponse.RouterTelepadMessage(msg) => - sendResponse(ChatMsg(ChatMessageType.UNK_229, wideContents=false, recipient="", msg, note=None)) - - case LocalResponse.RouterTelepadTransport(passengerGuid, srcGuid, destGuid) => - sessionLogic.general.useRouterTelepadEffect(passengerGuid, srcGuid, destGuid) - - case LocalResponse.SendResponse(msg) => - msg match { - case m: GenericObjectActionMessage => - // delay building virus alert if player is dead/respawning - if ((m.code == 58 || m.code == 60 || m.code == 61) && !sessionLogic.zoning.spawn.startEnqueueSquadMessages) { - sessionLogic.zoning.spawn.enqueueNewActivity(ActivityQueuedTask( - SpawnOperations.delaySendGenericObjectActionMessage(msg), 1)) - } - else sendResponse(msg) - case _ => - sendResponse(msg) - } - - case LocalResponse.SetEmpire(objectGuid, empire) => - sendResponse(SetEmpireMessage(objectGuid, empire)) - - case LocalResponse.ShuttleEvent(ev) => - val msg = OrbitalShuttleTimeMsg( - ev.u1, - ev.u2, - ev.t1, - ev.t2, - ev.t3, - pairs=ev.pairs.map { case ((a, b), c) => PadAndShuttlePair(a, b, c) } - ) - sendResponse(msg) - - case LocalResponse.ShuttleDock(pguid, sguid, slot) => - sendResponse(ObjectAttachMessage(pguid, sguid, slot)) - - case LocalResponse.ShuttleUndock(pguid, sguid, pos, orient) => - sendResponse(ObjectDetachMessage(pguid, sguid, pos, orient)) - - case LocalResponse.ShuttleState(sguid, pos, orient, state) => - sendResponse(VehicleStateMessage(sguid, unk1=0, pos, orient, vel=None, Some(state), unk3=0, unk4=0, wheel_direction=15, is_decelerating=false, is_cloaked=false)) - - case LocalResponse.ToggleTeleportSystem(router, systemPlan) => - sessionLogic.general.toggleTeleportSystem(router, systemPlan) - - case LocalResponse.TriggerEffect(targetGuid, effect, effectInfo, triggerLocation) => - sendResponse(TriggerEffectMessage(targetGuid, effect, effectInfo, triggerLocation)) - - case LocalResponse.TriggerSound(sound, pos, unk, volume) => - sendResponse(TriggerSoundMessage(sound, pos, unk, volume)) - - case LocalResponse.UpdateForceDomeStatus(buildingGuid, true) => - sendResponse(GenericObjectActionMessage(buildingGuid, 11)) - - case LocalResponse.UpdateForceDomeStatus(buildingGuid, false) => - sendResponse(GenericObjectActionMessage(buildingGuid, 12)) - - case LocalResponse.RechargeVehicleWeapon(vehicleGuid, weaponGuid) if resolvedPlayerGuid == guid => - continent.GUID(vehicleGuid) - .collect { case vehicle: MountableWeapons => (vehicle, vehicle.PassengerInSeat(player)) } - .collect { case (vehicle, Some(seat_num)) => vehicle.WeaponControlledFromSeat(seat_num) } - .getOrElse(Set.empty) - .collect { case weapon: Tool if weapon.GUID == weaponGuid => - sendResponse(InventoryStateMessage(weapon.AmmoSlot.Box.GUID, weapon.GUID, weapon.Magazine)) - } - - case LocalResponse.ForceZoneChange(zone) => - //todo we might be able to piggyback this for squad recalls later - if(session.zone eq zone) { - sessionLogic.zoning.zoneReload = true - zone.AvatarEvents ! Service.Leave() - zone.LocalEvents ! Service.Leave() - zone.VehicleEvents ! Service.Leave() - zone.AvatarEvents ! Service.Join(player.Name) //must manually restore this subscriptions - sessionLogic.zoning.spawn.handleNewPlayerLoaded(player) //will restart subscriptions and dispatch a LoadMapMessage - } else { - import akka.actor.typed.scaladsl.adapter._ - sessionLogic.cluster ! InterstellarClusterService.GetRandomSpawnPoint( - zone.Number, - player.Faction, - Seq(SpawnGroup.Facility, SpawnGroup.Tower, SpawnGroup.AMS), - context.self + def receive: Receive = { + case SendResponse(msgs) => + msgs.foreach { + case msg @ (m: GenericObjectActionMessage) + if (m.code == 58 || m.code == 60 || m.code == 61) && !sessionLogic.zoning.spawn.startEnqueueSquadMessages => + // delay building virus alert if player is dead/respawning + sessionLogic.zoning.spawn.enqueueNewActivity(ActivityQueuedTask( + SpawnOperations.delaySendGenericObjectActionMessage(msg), 1) ) + case msg => + sendResponse(msg) + } + + case LocalAction.DeployableMapIcon(behavior, deployInfo) + if TestFilter(NotSameTargetTest) => + sendResponse(DeployableObjectsInfoMessage(behavior, deployInfo)) + + case LocalAction.DeployableUIFor(item) => + sessionLogic.general.updateDeployableUIElements(avatar.deployables.UpdateUIElement(item)) + + case LocalAction.Detonate(dguid, _: BoomerDeployable) => + sendResponse(TriggerEffectMessage(dguid, "detonate_boomer")) + sendResponse(PlanetsideAttributeMessage(dguid, attribute_type=29, attribute_value=1)) + sendResponse(ObjectDeleteMessage(dguid, unk1=0)) + + case LocalAction.Detonate(dguid, _: ExplosiveDeployable) => + sendResponse(GenericObjectActionMessage(dguid, code=19)) + sendResponse(PlanetsideAttributeMessage(dguid, attribute_type=29, attribute_value=1)) + sendResponse(ObjectDeleteMessage(dguid, unk1=0)) + + case LocalAction.Detonate(_, obj) => + log.warn(s"LocalAction.Detonate: ${obj.Definition.Name} not configured to explode correctly") + + case LocalAction.DoorOpens(_, door) + if TestFilter(NotSameTargetTest) => + val doorGuid = door.GUID + val pos = player.Position.xy + val range = ops.doorLoadRange() + val foundDoor = continent + .blockMap + .sector(pos, range) + .amenityList + .collect { case door: Door => door } + .find(_.GUID == doorGuid) + val doorExistsInRange: Boolean = foundDoor.nonEmpty + if (doorExistsInRange) { + sendResponse(GenericObjectStateMsg(doorGuid, state=16)) + } + + case LocalAction.DoorCloses(doorGuid) => //door closes for everyone + sendResponse(GenericObjectStateMsg(doorGuid, state=17)) + + case LocalAction.EliminateDeployable(obj: TurretDeployable, dguid, _, _) + if TestFilter(() => obj.Destroyed) => + sendResponse(ObjectDeleteMessage(dguid, unk1=0)) + + case LocalAction.EliminateDeployable(obj: TurretDeployable, dguid, pos, _) => + obj.Destroyed = true + ops.DeconstructDeployable( + obj, + dguid, + pos, + obj.Orientation, + deletionType= if (obj.MountPoints.isEmpty) { 2 } else { 1 } + ) + + case LocalAction.EliminateDeployable(obj: ExplosiveDeployable, dguid, _, _) + if TestFilter(() => { obj.Destroyed || obj.Jammed || obj.Health == 0 }) => + sendResponse(ObjectDeleteMessage(dguid, unk1=0)) + + case LocalAction.EliminateDeployable(obj: ExplosiveDeployable, dguid, pos, effect) => + obj.Destroyed = true + ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect) + + case LocalAction.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) + if TestFilter(() => { obj.Active && obj.Destroyed }) => + //if active, deactivate + obj.Active = false + ops.deactivateTelpadDeployableMessages(dguid) + //standard deployable elimination behavior + sendResponse(ObjectDeleteMessage(dguid, unk1=0)) + + case LocalAction.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) + if TestFilter(() => obj.Active) => + //if active, deactivate + obj.Active = false + ops.deactivateTelpadDeployableMessages(dguid) + //standard deployable elimination behavior + obj.Destroyed = true + ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2) + + case LocalAction.EliminateDeployable(obj: TelepadDeployable, dguid, _, _) + if TestFilter(() => obj.Destroyed) => + //standard deployable elimination behavior + sendResponse(ObjectDeleteMessage(dguid, unk1=0)) + + case LocalAction.EliminateDeployable(obj: TelepadDeployable, dguid, pos, _) => + //standard deployable elimination behavior + obj.Destroyed = true + ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, deletionType=2) + + case LocalAction.EliminateDeployable(obj, dguid, _, _) + if TestFilter(() => obj.Destroyed) => + sendResponse(ObjectDeleteMessage(dguid, unk1=0)) + + case LocalAction.EliminateDeployable(obj, dguid, pos, effect) => + obj.Destroyed = true + ops.DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect) + + case LocalAction.HackClear(targetGuid, unk1, unk2) => + sendResponse(HackMessage(HackState1.Unk0, targetGuid, FilterGuid, progress=0, unk1.toFloat, HackState.HackCleared, unk2)) + + case LocalAction.HackObject(targetGuid, unk1, unk2) => + sessionLogic.general.hackObject(targetGuid, unk1, unk2) + + case LocalAction.GenericActionMessage(actionNumber) => + sendResponse(GenericActionMessage(actionNumber)) + + case LocalAction.LluSpawned(llu) => + // Create LLU on client + sendResponse(ObjectCreateMessage( + llu.Definition.ObjectId, + llu.GUID, + llu.Definition.Packet.ConstructorData(llu).get + )) + sendResponse(TriggerSoundMessage(TriggeredSound.LLUMaterialize, llu.Position, unk=20, volume=0.8000001f)) + + case LocalAction.LluDespawned(lluGuid, position) => + sendResponse(TriggerSoundMessage(TriggeredSound.LLUDeconstruct, position, unk=20, volume=0.8000001f)) + sendResponse(ObjectDeleteMessage(lluGuid, unk1=0)) + // If the player was holding the LLU, remove it from their tracked special item slot + sessionLogic.general.specialItemSlotGuid.collect { case guid if guid == lluGuid => + sessionLogic.general.specialItemSlotGuid = None + player.Carrying = None + } + + case LocalAction.ProximityTerminalEffect(object_guid, true) => + sendResponse(ProximityTerminalUseMessage(Default.GUID0, object_guid, unk=true)) + + case LocalAction.ProximityTerminalEffect(objectGuid, false) => + sendResponse(ProximityTerminalUseMessage(Default.GUID0, objectGuid, unk=false)) + sessionLogic.terminals.ForgetAllProximityTerminals(objectGuid) + + case LocalAction.RouterTelepadMessage(msg) => + sendResponse(ChatMsg(ChatMessageType.UNK_229, wideContents=false, recipient="", msg, note=None)) + + case LocalAction.RouterTelepadTransport(passengerGuid, srcGuid, destGuid) => + sessionLogic.general.useRouterTelepadEffect(passengerGuid, srcGuid, destGuid) + + case LocalAction.ShuttleEvent(ev) => + val msg = OrbitalShuttleTimeMsg( + ev.u1, + ev.u2, + ev.t1, + ev.t2, + ev.t3, + pairs=ev.pairs.map { case ((a, b), c) => PadAndShuttlePair(a, b, c) } + ) + sendResponse(msg) + + case LocalAction.ShuttleDock(pguid, sguid, slot) => + sendResponse(ObjectAttachMessage(pguid, sguid, slot)) + + case LocalAction.ShuttleUndock(pguid, sguid, pos, orient) => + sendResponse(ObjectDetachMessage(pguid, sguid, pos, orient)) + + case LocalAction.ShuttleState(sguid, pos, orient, state) => + sendResponse(VehicleStateMessage(sguid, unk1=0, pos, orient, vel=None, Some(state), unk3=0, unk4=0, wheel_direction=15, is_decelerating=false, is_cloaked=false)) + + case LocalAction.ToggleTeleportSystem(router, systemPlan) => + sessionLogic.general.toggleTeleportSystem(router, systemPlan) + + case LocalAction.TriggerEffectAtLocation(targetGuid, effect, effectInfo, triggerLocation) => + sendResponse(TriggerEffectMessage(targetGuid, effect, effectInfo, triggerLocation)) + + case LocalAction.TriggerSound(sound, pos, unk, volume) => + sendResponse(TriggerSoundMessage(sound, pos, unk, volume)) + + case LocalAction.UpdateForceDomeStatus(buildingGuid, true) => + sendResponse(GenericObjectActionMessage(buildingGuid, 11)) + + case LocalAction.UpdateForceDomeStatus(buildingGuid, false) => + sendResponse(GenericObjectActionMessage(buildingGuid, 12)) + + case LocalAction.RechargeVehicleWeapon(vehicleGuid, weaponGuid) + if TestFilter(SameTargetTest) => + continent.GUID(vehicleGuid) + .collect { case vehicle: MountableWeapons => (vehicle, vehicle.PassengerInSeat(player)) } + .collect { case (vehicle, Some(seat_num)) => vehicle.WeaponControlledFromSeat(seat_num) } + .getOrElse(Set.empty) + .collect { case weapon: Tool if weapon.GUID == weaponGuid => + sendResponse(InventoryStateMessage(weapon.AmmoSlot.Box.GUID, weapon.GUID, weapon.Magazine)) } - case _ => () - } + case LocalAction.ForceZoneChange(zone) => + //todo we might be able to piggyback this for squad recalls later + if(session.zone eq zone) { + sessionLogic.zoning.zoneReload = true + zone.AvatarEvents ! Service.LeaveAll + zone.LocalEvents ! Service.LeaveAll + zone.VehicleEvents ! Service.LeaveAll + zone.AvatarEvents ! Service.Join(player.Name) //must manually restore this subscriptions + sessionLogic.zoning.spawn.handleNewPlayerLoaded(player) //will restart subscriptions and dispatch a LoadMapMessage + } else { + import akka.actor.typed.scaladsl.adapter._ + sessionLogic.cluster ! InterstellarClusterService.GetRandomSpawnPoint( + zone.Number, + player.Faction, + Seq(SpawnGroup.Facility, SpawnGroup.Tower, SpawnGroup.AMS), + context.self + ) + } } /* support functions */ diff --git a/src/main/scala/net/psforever/actors/session/normal/MountHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/normal/MountHandlerLogic.scala index ae8399757..aa0f86714 100644 --- a/src/main/scala/net/psforever/actors/session/normal/MountHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/MountHandlerLogic.scala @@ -16,9 +16,9 @@ import net.psforever.objects.serverobject.turret.{FacilityTurret, WeaponTurret} import net.psforever.objects.vehicles.AccessPermissionGroup import net.psforever.objects.vital.InGameHistory import net.psforever.packet.game.{ChatMsg, DelayedPathMountMsg, DismountVehicleCargoMsg, DismountVehicleMsg, GenericObjectActionMessage, MountVehicleCargoMsg, MountVehicleMsg, ObjectDetachMessage, PlanetsideAttributeMessage, PlayerStasisMessage, PlayerStateShiftMessage, ShiftState} -import net.psforever.services.Service -import net.psforever.services.local.{LocalAction, LocalServiceMessage} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.{SendResponse, SetEmpire} +import net.psforever.services.vehicle.VehicleAction import net.psforever.types.{BailType, ChatMessageType, DriveState, PlanetSideGUID, Vector3} object MountHandlerLogic { @@ -191,7 +191,7 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act if obj.Definition == GlobalDefinitions.vanu_sentry_turret => log.info(s"${player.Name} mounts the ${obj.Definition.Name}") sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_mount") - obj.Zone.LocalEvents ! LocalServiceMessage(obj.Zone.id, LocalAction.SetEmpire(obj.GUID, player.Faction)) + obj.Zone.LocalEvents ! MessageEnvelope(obj.Zone.id, SetEmpire(obj.GUID, player.Faction)) sendResponse(PlanetsideAttributeMessage(obj.GUID, attribute_type=0, obj.Health)) ops.updateWeaponAtSeatPosition(obj, seatNumber) ops.MountingAction(tplayer, obj, seatNumber) @@ -234,9 +234,9 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act val (pos, zang) = Vehicles.dismountShuttle(obj, mountPoint) tplayer.Position = pos sendResponse(DelayedPathMountMsg(pguid, sguid, u1=60, u2=true)) - continent.LocalEvents ! LocalServiceMessage( + continent.LocalEvents ! MessageEnvelope( continent.id, - LocalAction.SendResponse(ObjectDetachMessage(sguid, pguid, pos, roll=0, pitch=0, zang)) + SendResponse(ObjectDetachMessage(sguid, pguid, pos, roll=0, pitch=0, zang)) ) sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive @@ -249,25 +249,21 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act ops.DismountAction(tplayer, obj, seatNum) continent.actor ! ZoneActor.RemoveFromBlockMap(player) //character doesn't need it //DismountAction(...) uses vehicle service, so use that service to coordinate the remainder of the messages - events ! VehicleServiceMessage( - player.Name, - VehicleAction.SendResponse(Service.defaultPlayerGUID, PlayerStasisMessage(pguid)) //the stasis message - ) //when the player dismounts, they will be positioned where the shuttle was when it disappeared in the sky //the player will fall to the ground and is perfectly vulnerable in this state //additionally, our player must exist in the current zone //having no in-game avatar target will throw us out of the map screen when deploying and cause softlock - events ! VehicleServiceMessage( - player.Name, - VehicleAction.SendResponse( - Service.defaultPlayerGUID, - PlayerStateShiftMessage(ShiftState(unk=0, obj.Position, obj.Orientation.z, vel=None)) //cower in the shuttle bay + events ! BundledEnvelope( + MessageEnvelope(player.Name, + SendResponse(Seq( + PlayerStasisMessage(pguid), + PlayerStateShiftMessage(ShiftState(unk=0, obj.Position, obj.Orientation.z, vel=None)) + )) + ), + MessageEnvelope(continent.id, pguid, + SendResponse(GenericObjectActionMessage(pguid, code=9)) /* conceal the player */ ) ) - events ! VehicleServiceMessage( - continent.id, - VehicleAction.SendResponse(pguid, GenericObjectActionMessage(pguid, code=9)) //conceal the player - ) sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive case Mountable.CanDismount(obj: Vehicle, seatNum, _) @@ -293,9 +289,10 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act ops.DismountVehicleAction(tplayer, obj, seatNum) case Mountable.CanDismount(obj: Vehicle, seat_num, _) => - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.KickPassenger(tplayer.GUID, seat_num, unk2=true, obj.GUID) + tplayer.GUID, + VehicleAction.KickPassenger(seat_num, unk2=true, obj.GUID) ) case Mountable.CanDismount(obj: PlanetSideGameObject with Mountable with FactionAffinity with InGameHistory, seatNum, _) => diff --git a/src/main/scala/net/psforever/actors/session/normal/VehicleHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/normal/VehicleHandlerLogic.scala index f6102dedb..da83bc6ed 100644 --- a/src/main/scala/net/psforever/actors/session/normal/VehicleHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/VehicleHandlerLogic.scala @@ -1,21 +1,23 @@ // Copyright (c) 2024 PSForever package net.psforever.actors.session.normal +import akka.actor.Actor.Receive import akka.actor.{ActorContext, ActorRef, typed} import net.psforever.actors.session.AvatarActor import net.psforever.actors.session.support.{SessionData, SessionVehicleHandlers, VehicleHandlerFunctions} import net.psforever.objects.avatar.SpecialCarry -import net.psforever.objects.{GlobalDefinitions, Player, Tool, Vehicle, Vehicles} +import net.psforever.objects.{GlobalDefinitions, Player, Vehicle, Vehicles} import net.psforever.objects.equipment.{Equipment, JammableMountedWeapons, JammableUnit} import net.psforever.objects.guid.{GUIDTask, TaskWorkflow} import net.psforever.objects.serverobject.interior.Sidedness.OutsideOf import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.pad.VehicleSpawnPad import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent -import net.psforever.packet.game.{ChangeAmmoMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChatMsg, ChildObjectStateMessage, DeadState, DeployRequestMessage, DismountVehicleMsg, FrameVehicleStateMessage, GenericObjectActionMessage, HitHint, InventoryStateMessage, ObjectAttachMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, PlanetsideAttributeMessage, ReloadMessage, ServerVehicleOverrideMsg, VehicleStateMessage, WeaponDryFireMessage} +import net.psforever.packet.game.{ChatMsg, ChildObjectStateMessage, DeadState, DeployRequestMessage, DismountVehicleMsg, FrameVehicleStateMessage, GenericObjectActionMessage, InventoryStateMessage, ObjectAttachMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, PlanetsideAttributeMessage, ServerVehicleOverrideMsg, VehicleStateMessage} import net.psforever.services.Service +import net.psforever.services.base.envelope.GenericResponseEnvelope import net.psforever.services.local.support.CaptureFlagManager -import net.psforever.services.vehicle.{VehicleResponse, VehicleServiceResponse} +import net.psforever.services.vehicle.{VehicleAction, VehicleStamp} import net.psforever.types.{BailType, ChatMessageType, PlanetSideGUID, Vector3} object VehicleHandlerLogic { @@ -31,365 +33,319 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context: private val galaxyService: ActorRef = ops.galaxyService - /** - * na - * - * @param toChannel na - * @param guid na - * @param reply na - */ - def handle(toChannel: String, guid: PlanetSideGUID, reply: VehicleResponse.Response): Unit = { - val resolvedPlayerGuid = if (player.HasGUID) { - player.GUID - } else { - PlanetSideGUID(-1) - } - val isNotSameTarget = resolvedPlayerGuid != guid - reply match { - case VehicleResponse.VehicleState( - vehicleGuid, - unk1, - pos, - orient, - vel, - unk2, - unk3, - unk4, - wheelDirection, - unk5, - unk6 - ) if isNotSameTarget && player.VehicleSeated.contains(vehicleGuid) => - //player who is also in the vehicle (not driver) - sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, orient, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6)) - player.Position = pos - player.Orientation = orient - player.Velocity = vel - sessionLogic.updateLocalBlockMap(pos) - //llu destruction check - if (player.Carrying.contains(SpecialCarry.CaptureFlag)) { - continent - .GUID(player.VehicleSeated) - .collect { case vehicle: Vehicle => - CaptureFlagManager.ReasonToLoseFlagViolently(continent, sessionLogic.general.specialItemSlotGuid, vehicle) - } - } - - case VehicleResponse.VehicleState( - vehicleGuid, - unk1, - pos, - ang, - vel, - unk2, - unk3, - unk4, - wheelDirection, - unk5, - unk6 - ) if isNotSameTarget => - //player who is watching the vehicle from the outside - sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, ang, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6)) - - case VehicleResponse.ChildObjectState(objectGuid, pitch, yaw) if isNotSameTarget => - sendResponse(ChildObjectStateMessage(objectGuid, pitch, yaw)) - - case VehicleResponse.FrameVehicleState(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA) - if isNotSameTarget => - sendResponse(FrameVehicleStateMessage(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA)) - - case VehicleResponse.ChangeFireState_Start(weaponGuid) if isNotSameTarget => - sendResponse(ChangeFireStateMessage_Start(weaponGuid)) - - case VehicleResponse.ChangeFireState_Stop(weaponGuid) if isNotSameTarget => - sendResponse(ChangeFireStateMessage_Stop(weaponGuid)) - - case VehicleResponse.Reload(itemGuid) if isNotSameTarget => - sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0)) - - case VehicleResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) if isNotSameTarget => - sendResponse(ObjectDetachMessage(weapon_guid, previous_guid, Vector3.Zero, 0)) - //TODO? sendResponse(ObjectDeleteMessage(previousAmmoGuid, 0)) - sendResponse( - ObjectCreateMessage( - ammo_id, - ammo_guid, - ObjectCreateMessageParent(weapon_guid, weapon_slot), - ammo_data - ) - ) - sendResponse(ChangeAmmoMessage(weapon_guid, 1)) - - case VehicleResponse.WeaponDryFire(weaponGuid) if isNotSameTarget => - continent.GUID(weaponGuid).collect { - case tool: Tool if tool.Magazine == 0 => - // check that the magazine is still empty before sending WeaponDryFireMessage - // if it has been reloaded since then, other clients will not see it firing - sendResponse(WeaponDryFireMessage(weaponGuid)) - } - - case VehicleResponse.DismountVehicle(bailType, wasKickedByDriver) if isNotSameTarget => - sendResponse(DismountVehicleMsg(guid, bailType, wasKickedByDriver)) - - case VehicleResponse.MountVehicle(vehicleGuid, seat) if isNotSameTarget => - sendResponse(ObjectAttachMessage(vehicleGuid, guid, seat)) - - case VehicleResponse.DeployRequest(objectGuid, state, unk1, unk2, pos) if isNotSameTarget => - sendResponse(DeployRequestMessage(guid, objectGuid, state, unk1, unk2, pos)) - - case VehicleResponse.SendResponse(msg) => - sendResponse(msg) - - case VehicleResponse.AttachToRails(vehicleGuid, padGuid) => - sendResponse(ObjectAttachMessage(padGuid, vehicleGuid, slot=3)) - - case VehicleResponse.ConcealPlayer(playerGuid) => - sendResponse(GenericObjectActionMessage(playerGuid, code=9)) - - case VehicleResponse.DetachFromRails(vehicleGuid, padGuid, padPosition, padOrientationZ) => - val pad = continent.GUID(padGuid).get.asInstanceOf[VehicleSpawnPad].Definition - sendResponse( - ObjectDetachMessage( - padGuid, - vehicleGuid, - padPosition + Vector3.z(pad.VehicleCreationZOffset), - padOrientationZ + pad.VehicleCreationZOrientOffset - ) - ) - - case VehicleResponse.EquipmentInSlot(pkt) if isNotSameTarget => - sendResponse(pkt) - - case VehicleResponse.GenericObjectAction(objectGuid, action) if isNotSameTarget => - sendResponse(GenericObjectActionMessage(objectGuid, action)) - - case VehicleResponse.HitHint(sourceGuid) if player.isAlive => - sendResponse(HitHint(sourceGuid, player.GUID)) - - case VehicleResponse.InventoryState(obj, parentGuid, start, conData) if isNotSameTarget => - //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly? - val objGuid = obj.GUID - sendResponse(ObjectDeleteMessage(objGuid, unk1=0)) - sendResponse(ObjectCreateDetailedMessage( - obj.Definition.ObjectId, - objGuid, - ObjectCreateMessageParent(parentGuid, start), - conData - )) - - case VehicleResponse.KickPassenger(_, wasKickedByDriver, vehicleGuid) if resolvedPlayerGuid == guid => - //seat number (first field) seems to be correct if passenger is kicked manually by driver - //but always seems to return 4 if user is kicked by mount permissions changing - sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver)) - val typeOfRide = continent.GUID(vehicleGuid) match { - case Some(obj: Vehicle) => - sessionLogic.general.unaccessContainer(obj) - s"the ${obj.Definition.Name}'s seat by ${obj.OwnerName.getOrElse("the pilot")}" - case _ => - s"${player.Sex.possessive} ride" - } - log.info(s"${player.Name} has been kicked from $typeOfRide!") - player.WhichSide = OutsideOf - - case VehicleResponse.KickPassenger(_, wasKickedByDriver, _) => - //seat number (first field) seems to be correct if passenger is kicked manually by driver - //but always seems to return 4 if user is kicked by mount permissions changing - sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver)) - - case VehicleResponse.InventoryState2(objGuid, parentGuid, value) if isNotSameTarget => - sendResponse(InventoryStateMessage(objGuid, unk=0, parentGuid, value)) - - case VehicleResponse.LoadVehicle(vehicle, vtype, vguid, vdata) if isNotSameTarget => - //this is not be suitable for vehicles with people who are seated in it before it spawns (if that is possible) - sendResponse(ObjectCreateMessage(vtype, vguid, vdata)) - Vehicles.ReloadAccessPermissions(vehicle, player.Name) - - case VehicleResponse.ObjectDelete(itemGuid) if isNotSameTarget => - sendResponse(ObjectDeleteMessage(itemGuid, unk1=0)) - - case VehicleResponse.Ownership(vehicleGuid) if resolvedPlayerGuid == guid => - //Only the player that owns this vehicle needs the ownership packet - avatarActor ! AvatarActor.SetVehicle(Some(vehicleGuid)) - sendResponse(PlanetsideAttributeMessage(resolvedPlayerGuid, attribute_type=21, vehicleGuid)) - - case VehicleResponse.LoseOwnership(_, vehicleGuid) => - ops.announceAmsDecay(vehicleGuid,msg = "@ams_decaystarted") - - case VehicleResponse.PlanetsideAttribute(vehicleGuid, attributeType, attributeValue) if isNotSameTarget => - sendResponse(PlanetsideAttributeMessage(vehicleGuid, attributeType, attributeValue)) - - case VehicleResponse.ResetSpawnPad(padGuid) => - sendResponse(GenericObjectActionMessage(padGuid, code=23)) - - case VehicleResponse.RevealPlayer(playerGuid) => - sendResponse(GenericObjectActionMessage(playerGuid, code=10)) - - case VehicleResponse.SeatPermissions(vehicleGuid, seatGroup, permission) if isNotSameTarget => - sendResponse(PlanetsideAttributeMessage(vehicleGuid, seatGroup, permission)) - - case VehicleResponse.StowEquipment(vehicleGuid, slot, itemType, itemGuid, itemData) if isNotSameTarget => - //TODO prefer ObjectAttachMessage, but how to force ammo pools to update properly? - sendResponse(ObjectCreateDetailedMessage(itemType, itemGuid, ObjectCreateMessageParent(vehicleGuid, slot), itemData)) - - case VehicleResponse.UnloadVehicle(_, vehicleGuid) => - sendResponse(ObjectDeleteMessage(vehicleGuid, unk1=1)) - if (sessionLogic.zoning.spawn.prevSpawnPoint.map(_.Owner).exists { - case ams: Vehicle => - ams.GUID == vehicleGuid && - ams.OwnerGuid.isEmpty - case _ => - false - }) { - sessionLogic.zoning.spawn.prevSpawnPoint = None - sendResponse(ChatMsg(ChatMessageType.UNK_229, "@ams_decayed")) - } - - case VehicleResponse.UnstowEquipment(itemGuid) if isNotSameTarget => - //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly? - sendResponse(ObjectDeleteMessage(itemGuid, unk1=0)) - - case VehicleResponse.UpdateAmsSpawnPoint(list) => - sessionLogic.zoning.spawn.amsSpawnPoints = list.filter(tube => tube.Faction == player.Faction) - sessionLogic.zoning.spawn.DrawCurrentAmsSpawnPoint() - - case VehicleResponse.TransferPassengerChannel(oldChannel, tempChannel, vehicle, vehicleToDelete) if isNotSameTarget => - sessionLogic.zoning.interstellarFerry = Some(vehicle) - sessionLogic.zoning.interstellarFerryTopLevelGUID = Some(vehicleToDelete) - continent.VehicleEvents ! Service.Leave(Some(oldChannel)) //old vehicle-specific channel (was s"${vehicle.Actor}") - galaxyService ! Service.Join(tempChannel) //temporary vehicle-specific channel - log.debug(s"TransferPassengerChannel: ${player.Name} now subscribed to $tempChannel for vehicle gating") - - case VehicleResponse.KickCargo(vehicle, speed, delay) - if player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive && speed > 0 => - val strafe = 1 + Vehicles.CargoOrientation(vehicle) - val reverseSpeed = if (strafe > 1) { 0 } else { speed } - //strafe or reverse, not both - sessionLogic.vehicles.ServerVehicleOverrideWithPacket( - vehicle, - ServerVehicleOverrideMsg( - lock_accelerator=true, - lock_wheel=true, - reverse=true, - unk4=false, - lock_vthrust=0, - strafe, - reverseSpeed, - unk8=Some(0) - ) - ) - import scala.concurrent.ExecutionContext.Implicits.global - import scala.concurrent.duration._ - context.system.scheduler.scheduleOnce( - delay milliseconds, - context.self, - VehicleServiceResponse(toChannel, PlanetSideGUID(0), VehicleResponse.KickCargo(vehicle, speed=0, delay)) - ) - - case VehicleResponse.KickCargo(cargo, _, _) - if player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive => - sessionLogic.vehicles.TotalDriverVehicleControl(cargo) - - case VehicleResponse.StartPlayerSeatedInVehicle(vehicle, _) - if player.VisibleSlots.contains(player.DrawnSlot) => - player.DrawnSlot = Player.HandsDownSlot - startPlayerSeatedInVehicle(vehicle) - - case VehicleResponse.StartPlayerSeatedInVehicle(vehicle, _) => - startPlayerSeatedInVehicle(vehicle) - - case VehicleResponse.PlayerSeatedInVehicle(vehicle, _) => - Vehicles.ReloadAccessPermissions(vehicle, player.Name) - sessionLogic.vehicles.ServerVehicleOverrideWithPacket( - vehicle, - ServerVehicleOverrideMsg( - lock_accelerator=true, - lock_wheel=true, - reverse=true, - unk4=false, - lock_vthrust=1, - lock_strafe=0, - movement_speed=0, - unk8=Some(0) - ) - ) - sessionLogic.vehicles.serverVehicleControlVelocity = Some(0) - - case VehicleResponse.ServerVehicleOverrideStart(vehicle, _) => - val vdef = vehicle.Definition - sessionLogic.vehicles.ServerVehicleOverrideWithPacket( - vehicle, - ServerVehicleOverrideMsg( - lock_accelerator=true, - lock_wheel=true, - reverse=false, - unk4=false, - lock_vthrust=if (GlobalDefinitions.isFlightVehicle(vdef)) { 1 } else { 0 }, - lock_strafe=0, - movement_speed=vdef.AutoPilotSpeed1, - unk8=Some(0) - ) - ) - - case VehicleResponse.ServerVehicleOverrideEnd(vehicle, _) => - sessionLogic.vehicles.ServerVehicleOverrideStop(vehicle) - - case VehicleResponse.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, data) => - val str = s"${data.getOrElse("The vehicle spawn pad where you placed your order is blocked.")}" - val msg = if (str.contains("@")) { - ChatMsg(ChatMessageType.UNK_229, str) - } else { - ChatMsg(ChatMessageType.CMT_OPEN, wideContents = true, recipient = "", str, note = None) - } - sendResponse(msg) - - case VehicleResponse.PeriodicReminder(_, data) => - val (isType, flag, msg): (ChatMessageType, Boolean, String) = data match { - case Some(msg: String) if msg.startsWith("@") => (ChatMessageType.UNK_227, false, msg) - case Some(msg: String) => (ChatMessageType.CMT_OPEN, true, msg) - case _ => (ChatMessageType.CMT_OPEN, true, "Your vehicle order has been cancelled.") - } - sendResponse(ChatMsg(isType, flag, recipient="", msg, None)) - - case VehicleResponse.ChangeLoadout(target, oldWeapons, addedWeapons, oldInventory, newInventory) - if player.avatar.vehicle.contains(target) => - //TODO when vehicle weapons can be changed without visual glitches, rewrite this - continent.GUID(target).collect { case vehicle: Vehicle => - import net.psforever.login.WorldSession.boolToInt - //owner: must unregister old equipment, and register and install new equipment - (oldWeapons ++ oldInventory).foreach { - case (obj, eguid) => - sendResponse(ObjectDeleteMessage(eguid, unk1=0)) - TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) + def receive: Receive = { + case VehicleAction.VehicleState( + vehicleGuid, + unk1, + pos, + orient, + vel, + unk2, + unk3, + unk4, + wheelDirection, + unk5, + unk6 + ) if TestFilter(() => { NotSameTarget && player.VehicleSeated.contains(vehicleGuid) }) => + //player who is also in the vehicle (not driver) + sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, orient, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6)) + player.Position = pos + player.Orientation = orient + player.Velocity = vel + sessionLogic.updateLocalBlockMap(pos) + //llu destruction check + if (player.Carrying.contains(SpecialCarry.CaptureFlag)) { + continent + .GUID(player.VehicleSeated) + .collect { case vehicle: Vehicle => + CaptureFlagManager.ReasonToLoseFlagViolently(continent, sessionLogic.general.specialItemSlotGuid, vehicle) } - sessionLogic.general.applyPurchaseTimersBeforePackingLoadout(player, vehicle, addedWeapons ++ newInventory) - //jammer or unjamm new weapons based on vehicle status - val vehicleJammered = vehicle.Jammed - addedWeapons - .map { _.obj } - .collect { - case jamItem: JammableUnit if jamItem.Jammed != vehicleJammered => - jamItem.Jammed = vehicleJammered - JammableMountedWeapons.JammedWeaponStatus(vehicle.Zone, jamItem, vehicleJammered) - } - changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory) - } + } - case VehicleResponse.ChangeLoadout(target, oldWeapons, _, oldInventory, _) - if sessionLogic.general.accessedContainer.map(_.GUID).contains(target) => - //TODO when vehicle weapons can be changed without visual glitches, rewrite this - continent.GUID(target).collect { case vehicle: Vehicle => - //external participant: observe changes to equipment - (oldWeapons ++ oldInventory).foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, unk1=0)) } - changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory) - } + case VehicleAction.VehicleState( + vehicleGuid, + unk1, + pos, + ang, + vel, + unk2, + unk3, + unk4, + wheelDirection, + unk5, + unk6 + ) if TestFilter(NotSameTargetTest) => + //player who is watching the vehicle from the outside + sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, ang, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6)) - case VehicleResponse.ChangeLoadout(target, oldWeapons, _, oldInventory, _) => - //TODO when vehicle weapons can be changed without visual glitches, rewrite this - continent.GUID(target).collect { case vehicle: Vehicle => - changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory) - } + case VehicleAction.ChildObjectState(objectGuid, pitch, yaw) + if TestFilter(NotSameTargetTest) => + sendResponse(ChildObjectStateMessage(objectGuid, pitch, yaw)) - case _ => () - } + case VehicleAction.FrameVehicleState(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA) + if TestFilter(NotSameTargetTest) => + sendResponse(FrameVehicleStateMessage(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA)) + + case VehicleAction.DismountVehicle(bailType, wasKickedByDriver) + if TestFilter(NotSameTargetTest) => + sendResponse(DismountVehicleMsg(FilterGuid, bailType, wasKickedByDriver)) + + case VehicleAction.MountVehicle(vehicleGuid, seat) + if TestFilter(NotSameTargetTest) => + sendResponse(ObjectAttachMessage(vehicleGuid, FilterGuid, seat)) + + case VehicleAction.DeployRequest(objectGuid, state, unk1, unk2, pos) + if TestFilter(NotSameTargetTest) => + sendResponse(DeployRequestMessage(FilterGuid, objectGuid, state, unk1, unk2, pos)) + + case VehicleAction.EquipmentCreatedInSlot(pkt) + if TestFilter(NotSameTargetTest) => + sendResponse(pkt) + + case VehicleAction.InventoryState(obj, parentGuid, start, conData) + if TestFilter(NotSameTargetTest) => + //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly? + val objGuid = obj.GUID + sendResponse(ObjectDeleteMessage(objGuid, unk1=0)) + sendResponse(ObjectCreateDetailedMessage( + obj.Definition.ObjectId, + objGuid, + ObjectCreateMessageParent(parentGuid, start), + conData + )) + + case VehicleAction.KickPassenger(_, wasKickedByDriver, vehicleGuid) + if TestFilter(SameTargetTest) => + //seat number (first field) seems to be correct if passenger is kicked manually by driver + //but always seems to return 4 if user is kicked by mount permissions changing + sendResponse(DismountVehicleMsg(FilterGuid, BailType.Kicked, wasKickedByDriver)) + val typeOfRide = continent.GUID(vehicleGuid) match { + case Some(obj: Vehicle) => + sessionLogic.general.unaccessContainer(obj) + s"the ${obj.Definition.Name}'s seat by ${obj.OwnerName.getOrElse("the pilot")}" + case _ => + s"${player.Sex.possessive} ride" + } + log.info(s"${player.Name} has been kicked from $typeOfRide!") + player.WhichSide = OutsideOf + + case VehicleAction.KickPassenger(_, wasKickedByDriver, _) => + //seat number (first field) seems to be correct if passenger is kicked manually by driver + //but always seems to return 4 if user is kicked by mount permissions changing + sendResponse(DismountVehicleMsg(FilterGuid, BailType.Kicked, wasKickedByDriver)) + + case VehicleAction.InventoryState2(objGuid, parentGuid, value) + if TestFilter(NotSameTargetTest) => + sendResponse(InventoryStateMessage(objGuid, unk=0, parentGuid, value)) + + case VehicleAction.LoadVehicle(vehicle, vtype, vguid, vdata) + if TestFilter(NotSameTargetTest) => + //this is not be suitable for vehicles with people who are seated in it before it spawns (if that is possible) + sendResponse(ObjectCreateMessage(vtype, vguid, vdata)) + Vehicles.ReloadAccessPermissions(vehicle, player.Name) + + case VehicleAction.Ownership(vehicleGuid) + if TestFilter(SameTargetTest) => + //Only the player that owns this vehicle needs the ownership packet + avatarActor ! AvatarActor.SetVehicle(Some(vehicleGuid)) + sendResponse(PlanetsideAttributeMessage(ResolvedGuid, attribute_type=21, vehicleGuid)) + + case VehicleAction.LoseOwnership(_, vehicleGuid) => + ops.announceAmsDecay(vehicleGuid,msg = "@ams_decaystarted") + + case VehicleAction.SeatPermissions(vehicleGuid, seatGroup, permission) + if TestFilter(NotSameTargetTest) => + sendResponse(PlanetsideAttributeMessage(vehicleGuid, seatGroup, permission)) + + case VehicleAction.StowCreatedEquipment(vehicleGuid, slot, itemType, itemGuid, itemData) + if TestFilter(NotSameTargetTest) => + //TODO prefer ObjectAttachMessage, but how to force ammo pools to update properly? + sendResponse(ObjectCreateDetailedMessage(itemType, itemGuid, ObjectCreateMessageParent(vehicleGuid, slot), itemData)) + + case VehicleAction.UnloadVehicle(_, vehicleGuid) => + sendResponse(ObjectDeleteMessage(vehicleGuid, unk1=1)) + if (sessionLogic.zoning.spawn.prevSpawnPoint.map(_.Owner).exists { + case ams: Vehicle => + ams.GUID == vehicleGuid && + ams.OwnerGuid.isEmpty + case _ => + false + }) { + sessionLogic.zoning.spawn.prevSpawnPoint = None + sendResponse(ChatMsg(ChatMessageType.UNK_229, "@ams_decayed")) + } + + case VehicleAction.UnstowEquipment(itemGuid) + if TestFilter(NotSameTargetTest) => + //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly? + sendResponse(ObjectDeleteMessage(itemGuid, unk1=0)) + + case VehicleAction.UpdateAmsSpawnList(list) => + sessionLogic.zoning.spawn.amsSpawnPoints = list.filter(tube => tube.Faction == player.Faction) + sessionLogic.zoning.spawn.DrawCurrentAmsSpawnPoint() + + case VehicleAction.TransferPassengerChannel(oldChannel, tempChannel, vehicle, vehicleToDelete) + if TestFilter(NotSameTargetTest) => + sessionLogic.zoning.interstellarFerry = Some(vehicle) + sessionLogic.zoning.interstellarFerryTopLevelGUID = Some(vehicleToDelete) + continent.VehicleEvents ! Service.Leave(oldChannel) //old vehicle-specific channel (was s"${vehicle.Actor}") + galaxyService ! Service.Join(tempChannel) //temporary vehicle-specific channel + log.debug(s"TransferPassengerChannel: ${player.Name} now subscribed to $tempChannel for vehicle gating") + + case VehicleAction.KickCargo(vehicle, speed, delay) + if TestFilter(() => { player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive && speed > 0 }) => + val strafe = 1 + Vehicles.CargoOrientation(vehicle) + val reverseSpeed = if (strafe > 1) { 0 } else { speed } + //strafe or reverse, not both + sessionLogic.vehicles.ServerVehicleOverrideWithPacket( + vehicle, + ServerVehicleOverrideMsg( + lock_accelerator=true, + lock_wheel=true, + reverse=true, + unk4=false, + lock_vthrust=0, + strafe, + reverseSpeed, + unk8=Some(0) + ) + ) + import scala.concurrent.ExecutionContext.Implicits.global + import scala.concurrent.duration._ + val resp = GenericResponseEnvelope( + VehicleStamp, + "", + PlanetSideGUID(0), + VehicleAction.KickCargo(vehicle, speed=0, delay) + ) + context.system.scheduler.scheduleOnce(delay milliseconds, context.self, resp) + + case VehicleAction.KickCargo(cargo, _, _) + if TestFilter(() => { player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive }) => + sessionLogic.vehicles.TotalDriverVehicleControl(cargo) + + case VehicleAction.ChangeLoadout(target, oldWeapons, addedWeapons, oldInventory, newInventory) + if TestFilter(() => { player.avatar.vehicle.contains(target) }) => + //TODO when vehicle weapons can be changed without visual glitches, rewrite this + continent.GUID(target).collect { case vehicle: Vehicle => + import net.psforever.login.WorldSession.boolToInt + //owner: must unregister old equipment, and register and install new equipment + (oldWeapons ++ oldInventory).foreach { + case (obj, eguid) => + sendResponse(ObjectDeleteMessage(eguid, unk1=0)) + TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) + } + sessionLogic.general.applyPurchaseTimersBeforePackingLoadout(player, vehicle, addedWeapons ++ newInventory) + //jammer or unjamm new weapons based on vehicle status + val vehicleJammered = vehicle.Jammed + addedWeapons + .map { _.obj } + .collect { + case jamItem: JammableUnit if jamItem.Jammed != vehicleJammered => + jamItem.Jammed = vehicleJammered + JammableMountedWeapons.JammedWeaponStatus(vehicle.Zone, jamItem, vehicleJammered) + } + changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory) + } + + case VehicleAction.ChangeLoadout(target, oldWeapons, _, oldInventory, _) + if TestFilter(() => { sessionLogic.general.accessedContainer.map(_.GUID).contains(target) }) => + //TODO when vehicle weapons can be changed without visual glitches, rewrite this + continent.GUID(target).collect { case vehicle: Vehicle => + //external participant: observe changes to equipment + (oldWeapons ++ oldInventory).foreach { case (_, eguid) => sendResponse(ObjectDeleteMessage(eguid, unk1=0)) } + changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory) + } + + case VehicleAction.ChangeLoadout(target, oldWeapons, _, oldInventory, _) => + //TODO when vehicle weapons can be changed without visual glitches, rewrite this + continent.GUID(target).collect { case vehicle: Vehicle => + changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory) + } + + case VehicleSpawnPad.AttachToRails(vehicle, pad) => + sendResponse(ObjectAttachMessage(pad.GUID, vehicle.GUID, slot=3)) + + case VehicleSpawnPad.ConcealPlayer(playerGuid) => + sendResponse(GenericObjectActionMessage(playerGuid, code=9)) + + case VehicleSpawnPad.DetachFromRails(vehicle, pad) => + val padDefinition = pad.Definition + sendResponse( + ObjectDetachMessage( + pad.GUID, + vehicle.GUID, + pad.Position + Vector3.z(padDefinition.VehicleCreationZOffset), + pad.Orientation.z + padDefinition.VehicleCreationZOrientOffset + ) + ) + + case VehicleSpawnPad.ResetSpawnPad(pad) => + sendResponse(GenericObjectActionMessage(pad.GUID, code=23)) + + case VehicleSpawnPad.RevealPlayer(playerGuid) => + sendResponse(GenericObjectActionMessage(playerGuid, code=10)) + + case VehicleSpawnPad.StartPlayerSeatedInVehicle(vehicle, _) + if TestFilter(() => { player.VisibleSlots.contains(player.DrawnSlot) }) => + player.DrawnSlot = Player.HandsDownSlot + startPlayerSeatedInVehicle(vehicle) + + case VehicleSpawnPad.StartPlayerSeatedInVehicle(vehicle, _) => + startPlayerSeatedInVehicle(vehicle) + + case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle, _) => + Vehicles.ReloadAccessPermissions(vehicle, player.Name) + sessionLogic.vehicles.ServerVehicleOverrideWithPacket( + vehicle, + ServerVehicleOverrideMsg( + lock_accelerator=true, + lock_wheel=true, + reverse=true, + unk4=false, + lock_vthrust=1, + lock_strafe=0, + movement_speed=0, + unk8=Some(0) + ) + ) + sessionLogic.vehicles.serverVehicleControlVelocity = Some(0) + + case VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, _) => + val vdef = vehicle.Definition + sessionLogic.vehicles.ServerVehicleOverrideWithPacket( + vehicle, + ServerVehicleOverrideMsg( + lock_accelerator=true, + lock_wheel=true, + reverse=false, + unk4=false, + lock_vthrust=if (GlobalDefinitions.isFlightVehicle(vdef)) { 1 } else { 0 }, + lock_strafe=0, + movement_speed=vdef.AutoPilotSpeed1, + unk8=Some(0) + ) + ) + + case VehicleSpawnPad.ServerVehicleOverrideEnd(vehicle, _) => + sessionLogic.vehicles.ServerVehicleOverrideStop(vehicle) + + case VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, data) => + val str = s"${data.getOrElse("The vehicle spawn pad where you placed your order is blocked.")}" + val msg = if (str.contains("@")) { + ChatMsg(ChatMessageType.UNK_229, str) + } else { + ChatMsg(ChatMessageType.CMT_OPEN, wideContents = true, recipient = "", str, note = None) + } + sendResponse(msg) + + case VehicleSpawnPad.PeriodicReminder(_, data) => + val (isType, flag, msg): (ChatMessageType, Boolean, String) = data match { + case Some(msg: String) if msg.startsWith("@") => (ChatMessageType.UNK_227, false, msg) + case Some(msg: String) => (ChatMessageType.CMT_OPEN, true, msg) + case _ => (ChatMessageType.CMT_OPEN, true, "Your vehicle order has been cancelled.") + } + sendResponse(ChatMsg(isType, flag, recipient="", msg, None)) } private def changeLoadoutDeleteOldEquipment( 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 883fbfe94..53affd906 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,9 @@ 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.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.CachedEnvelope +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.vehicle.VehicleAction import net.psforever.types.{ChatMessageType, DriveState, Vector3} object VehicleLogic { @@ -75,10 +77,10 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex obj.Position = position obj.Orientation = angle // - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! CachedEnvelope( continent.id, + player.GUID, VehicleAction.VehicleState( - player.GUID, vehicle_guid, unk1, position, @@ -162,26 +164,11 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex obj.Position = position obj.Orientation = angle obj.DeploymentState = if (is_crouched || !notMountedState) DriveState.Kneeling else DriveState.Mobile - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.FrameVehicleState( - player.GUID, - vehicle_guid, - unk1, - position, - angle, - velocity, - unk2, - unk3, - unk4, - is_crouched, - is_airborne, - ascending_flight, - flight_time, - unk9, - unkA - ) - ) + player.GUID, + VehicleAction.FrameVehicleState(vehicle_guid, unk1, position, angle, velocity, unk2, unk3, unk4, is_crouched, is_airborne, ascending_flight, flight_time, unk9, unkA) + ) //todo CachedMessage sessionLogic.squad.updateSquad() case (None, _) => //log.error(s"VehicleState: no vehicle $vehicle_guid found in zone") @@ -230,10 +217,11 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex val angle = Vector3(0f, pitch, yaw) tool.Orientation = angle player.Orientation = angle - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.ChildObjectState(player.GUID, object_guid, pitch, yaw) - ) + player.GUID, + VehicleAction.ChildObjectState(object_guid, pitch, yaw) + ) //todo CachedMessage } //TODO status condition of "playing getting out of vehicle to allow for late packets without warning if (player.death_by == -1) { @@ -252,22 +240,10 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex obj.Velocity = vel sessionLogic.updateBlockMap(obj, pos) obj.zoneInteractions() - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! CachedEnvelope( continent.id, - VehicleAction.VehicleState( - player.GUID, - vehicle_guid, - unk1, - pos, - ang, - obj.Velocity, - obj.Flying, - 0, - 0, - 15, - unk5 = false, - obj.Cloaked - ) + player.GUID, + VehicleAction.VehicleState(vehicle_guid, unk1, pos, ang, obj.Velocity, obj.Flying, 0, 0, 15, unk5 = false, obj.Cloaked) ) } } @@ -346,9 +322,10 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex val mobileShift: String = if (obj.DeploymentState != DriveState.Mobile) { obj.DeploymentState = DriveState.Mobile sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Mobile, 0, unk3=false, Vector3.Zero)) - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.DeployRequest(player.GUID, obj.GUID, DriveState.Mobile, 0, unk2=false, Vector3.Zero) + player.GUID, + VehicleAction.DeployRequest(obj.GUID, DriveState.Mobile, 0, unk2=false, Vector3.Zero) ) "; enforcing Mobile deployment state" } else { diff --git a/src/main/scala/net/psforever/actors/session/spectator/AvatarHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/spectator/AvatarHandlerLogic.scala index cebbb5ba2..1f994d452 100644 --- a/src/main/scala/net/psforever/actors/session/spectator/AvatarHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/spectator/AvatarHandlerLogic.scala @@ -1,6 +1,7 @@ // Copyright (c) 2024 PSForever package net.psforever.actors.session.spectator +import akka.actor.Actor.Receive import akka.actor.{ActorContext, typed} import net.psforever.actors.session.support.AvatarHandlerFunctions import net.psforever.actors.zone.ZoneActor @@ -8,6 +9,8 @@ import net.psforever.objects.Players import net.psforever.objects.avatar.scoring.Kill import net.psforever.objects.sourcing.PlayerSource import net.psforever.packet.game.{AvatarImplantMessage, ImplantAction} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{ChangeAmmo, ChangeFireState_Start, ChangeFireState_Stop, PlanetsideAttribute, ReloadTool, SendResponse, WeaponDryFire} import scala.concurrent.duration._ // @@ -22,9 +25,8 @@ import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal} import net.psforever.objects.vital.etc.ExplodingEntityReason import net.psforever.objects.zones.Zoning import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent -import net.psforever.packet.game.{ArmorChangedMessage, AvatarDeadStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChatMsg, DeadState, DestroyMessage, DrowningTarget, GenericActionMessage, GenericObjectActionMessage, HitHint, ItemTransactionResultMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectHeldMessage, OxygenStateMessage, PlanetsideAttributeMessage, PlayerStateMessage, ProjectileStateMessage, ReloadMessage, SetEmpireMessage, UseItemMessage, WeaponDryFireMessage} -import net.psforever.services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage} -import net.psforever.services.Service +import net.psforever.packet.game.{ArmorChangedMessage, AvatarDeadStateMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChatMsg, DeadState, DestroyMessage, DrowningTarget, GenericActionMessage, GenericObjectActionMessage, ItemTransactionResultMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectHeldMessage, OxygenStateMessage, PlanetsideAttributeMessage, PlayerStateMessage, ProjectileStateMessage, ReloadMessage, UseItemMessage, WeaponDryFireMessage} +import net.psforever.services.avatar.AvatarAction import net.psforever.types.{ChatMessageType, PlanetSideGUID, TransactionType, Vector3} import net.psforever.util.Config @@ -39,546 +41,500 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A private val avatarActor: typed.ActorRef[AvatarActor.Command] = ops.avatarActor - /** - * na - * @param toChannel na - * @param guid na - * @param reply na - */ - def handle(toChannel: String, guid: PlanetSideGUID, reply: AvatarResponse.Response): Unit = { - val resolvedPlayerGuid = if (player != null && player.HasGUID) { - player.GUID - } else { - Service.defaultPlayerGUID - } - val isNotSameTarget = resolvedPlayerGuid != guid - val isSameTarget = !isNotSameTarget - reply match { - /* special messages */ - case AvatarResponse.TeardownConnection() => - log.trace(s"ending ${player.Name}'s old session by event system request (relog)") - context.stop(context.self) + def receive: Receive = { + /* special messages */ + case AvatarAction.TeardownConnection => + log.trace(s"ending ${player.Name}'s old session by event system request (relog)") + context.stop(context.self) - /* really common messages (very frequently, every life) */ - case pstate @ AvatarResponse.PlayerState( - pos, - vel, - yaw, - pitch, - yawUpper, - _, - isCrouching, - isJumping, - jumpThrust, - isCloaking, - isNotRendered, - canSeeReallyFar - ) if isNotSameTarget => - val pstateToSave = pstate.copy(timestamp = 0) - val (lastMsg, lastTime, lastPosition, wasVisible, wasShooting) = ops.lastSeenStreamMessage.get(guid.guid) match { - case Some(SessionAvatarHandlers.LastUpstream(Some(msg), visible, shooting, time)) => (Some(msg), time, msg.pos, visible, shooting) - case _ => (None, 0L, Vector3.Zero, false, None) - } - val drawConfig = Config.app.game.playerDraw //m - val maxRange = drawConfig.rangeMax * drawConfig.rangeMax //sq.m - val ourPosition = player.Position //xyz - val currentDistance = Vector3.DistanceSquared(ourPosition, pos) //sq.m - val inDrawableRange = currentDistance <= maxRange - val now = System.currentTimeMillis() //ms - if ( - sessionLogic.zoning.zoningStatus != Zoning.Status.Deconstructing && - !isNotRendered && inDrawableRange + /* really common messages (very frequently, every life) */ + case pstate @ AvatarAction.PlayerState( + pos, + vel, + yaw, + pitch, + yawUpper, + _, + isCrouching, + isJumping, + jumpThrust, + isCloaking, + isNotRendered, + canSeeReallyFar + ) if TestFilter(NotSameTargetTest) => + val pstateToSave = pstate.copy(timestamp = 0) + val (lastMsg, lastTime, lastPosition, wasVisible, wasShooting) = ops.lastSeenStreamMessage.get(FilterGuid.guid) match { + case Some(SessionAvatarHandlers.LastUpstream(Some(msg), visible, shooting, time)) => (Some(msg), time, msg.pos, visible, shooting) + case _ => (None, 0L, Vector3.Zero, false, None) + } + val drawConfig = Config.app.game.playerDraw //m + val maxRange = drawConfig.rangeMax * drawConfig.rangeMax //sq.m + val ourPosition = player.Position //xyz + val currentDistance = Vector3.DistanceSquared(ourPosition, pos) //sq.m + val inDrawableRange = currentDistance <= maxRange + val now = System.currentTimeMillis() //ms + if ( + sessionLogic.zoning.zoningStatus != Zoning.Status.Deconstructing && + !isNotRendered && inDrawableRange + ) { + //conditions where visibility is assured + val durationSince = now - lastTime //ms + lazy val previouslyInDrawableRange = Vector3.DistanceSquared(ourPosition, lastPosition) <= maxRange + lazy val targetDelay = { + val populationOver = math.max( + 0, + sessionLogic.localSector.livePlayerList.size - drawConfig.populationThreshold + ) + val distanceAdjustment = math.pow(populationOver / drawConfig.populationStep * drawConfig.rangeStep, 2) //sq.m + val adjustedDistance = currentDistance + distanceAdjustment //sq.m + drawConfig.ranges.lastIndexWhere { dist => adjustedDistance > dist * dist } match { + case -1 => 1 + case index => drawConfig.delays(index) + } + } //ms + if (!wasVisible || + !previouslyInDrawableRange || + durationSince > drawConfig.delayMax || + (!lastMsg.contains(pstateToSave) && + (canSeeReallyFar || + currentDistance < drawConfig.rangeMin * drawConfig.rangeMin || + sessionLogic.general.canSeeReallyFar || + durationSince > targetDelay + ) + ) ) { - //conditions where visibility is assured - val durationSince = now - lastTime //ms - lazy val previouslyInDrawableRange = Vector3.DistanceSquared(ourPosition, lastPosition) <= maxRange - lazy val targetDelay = { - val populationOver = math.max( - 0, - sessionLogic.localSector.livePlayerList.size - drawConfig.populationThreshold + //must draw + sendResponse( + PlayerStateMessage( + FilterGuid, + pos, + vel, + yaw, + pitch, + yawUpper, + timestamp = 0, //is this okay? + isCrouching, + isJumping, + jumpThrust, + isCloaking ) - val distanceAdjustment = math.pow(populationOver / drawConfig.populationStep * drawConfig.rangeStep, 2) //sq.m - val adjustedDistance = currentDistance + distanceAdjustment //sq.m - drawConfig.ranges.lastIndexWhere { dist => adjustedDistance > dist * dist } match { - case -1 => 1 - case index => drawConfig.delays(index) - } - } //ms - if (!wasVisible || - !previouslyInDrawableRange || - durationSince > drawConfig.delayMax || - (!lastMsg.contains(pstateToSave) && - (canSeeReallyFar || - currentDistance < drawConfig.rangeMin * drawConfig.rangeMin || - sessionLogic.general.canSeeReallyFar || - durationSince > targetDelay - ) - ) - ) { - //must draw - sendResponse( - PlayerStateMessage( - guid, - pos, - vel, - yaw, - pitch, - yawUpper, - timestamp = 0, //is this okay? - isCrouching, - isJumping, - jumpThrust, - isCloaking - ) - ) - ops.lastSeenStreamMessage.put(guid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, wasShooting, now)) - } else { - //is visible, but skip reinforcement - ops.lastSeenStreamMessage.put(guid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, wasShooting, lastTime)) - } + ) + ops.lastSeenStreamMessage.put(FilterGuid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, wasShooting, now)) } else { - //conditions where the target is not currently visible - if (wasVisible) { - //the target was JUST PREVIOUSLY visible; one last draw to move target beyond a renderable distance - val lat = (1 + ops.hidingPlayerRandomizer.nextInt(continent.map.scale.height.toInt)).toFloat - sendResponse( - PlayerStateMessage( - guid, - Vector3(1f, lat, 1f), - vel=None, - facingYaw=0f, - facingPitch=0f, - facingYawUpper=0f, - timestamp=0, //is this okay? - is_cloaked = isCloaking - ) + //is visible, but skip reinforcement + ops.lastSeenStreamMessage.put(FilterGuid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, wasShooting, lastTime)) + } + } else { + //conditions where the target is not currently visible + if (wasVisible) { + //the target was JUST PREVIOUSLY visible; one last draw to move target beyond a renderable distance + val lat = (1 + ops.hidingPlayerRandomizer.nextInt(continent.map.scale.height.toInt)).toFloat + sendResponse( + PlayerStateMessage( + FilterGuid, + Vector3(1f, lat, 1f), + vel=None, + facingYaw=0f, + facingPitch=0f, + facingYawUpper=0f, + timestamp=0, //is this okay? + is_cloaked = isCloaking ) - ops.lastSeenStreamMessage.put(guid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, wasShooting, now)) - } else { - //skip drawing altogether - ops.lastSeenStreamMessage.put(guid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, wasShooting, lastTime)) - } + ) + ops.lastSeenStreamMessage.put(FilterGuid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, wasShooting, now)) + } else { + //skip drawing altogether + ops.lastSeenStreamMessage.put(FilterGuid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, wasShooting, lastTime)) } + } - case AvatarResponse.ObjectHeld(slot, _) - if isSameTarget && player.VisibleSlots.contains(slot) => - sendResponse(ObjectHeldMessage(guid, slot, unk1=true)) - //Stop using proximity terminals if player unholsters a weapon - continent.GUID(sessionLogic.terminals.usingMedicalTerminal).collect { - case term: Terminal with ProximityUnit => sessionLogic.terminals.StopUsingProximityUnit(term) - } - if (sessionLogic.zoning.zoningStatus == Zoning.Status.Deconstructing) { - sessionLogic.zoning.spawn.stopDeconstructing() - } + case AvatarAction.ObjectHeld(slot, _) + if TestFilter(() => { SameTarget && player.VisibleSlots.contains(slot) }) => + sendResponse(ObjectHeldMessage(FilterGuid, slot, unk1=true)) + //Stop using proximity terminals if player unholsters a weapon + continent.GUID(sessionLogic.terminals.usingMedicalTerminal).collect { + case term: Terminal with ProximityUnit => sessionLogic.terminals.StopUsingProximityUnit(term) + } + if (sessionLogic.zoning.zoningStatus == Zoning.Status.Deconstructing) { + sessionLogic.zoning.spawn.stopDeconstructing() + } - case AvatarResponse.ObjectHeld(slot, _) - if isSameTarget && slot > -1 => - sendResponse(ObjectHeldMessage(guid, slot, unk1=true)) + case AvatarAction.ObjectHeld(slot, _) + if TestFilter(() => { SameTarget && slot > -1 }) => + sendResponse(ObjectHeldMessage(FilterGuid, slot, unk1=true)) - case AvatarResponse.ObjectHeld(_, _) - if isSameTarget => () + case AvatarAction.ObjectHeld(_, _) + if TestFilter(SameTargetTest) => () - case AvatarResponse.ObjectHeld(_, previousSlot) => - sendResponse(ObjectHeldMessage(guid, previousSlot, unk1=false)) + case AvatarAction.ObjectHeld(_, previousSlot) => + sendResponse(ObjectHeldMessage(FilterGuid, previousSlot, unk1=false)) - case AvatarResponse.ChangeFireState_Start(weaponGuid) - if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { _.visible } => - sendResponse(ChangeFireStateMessage_Start(weaponGuid)) - val entry = ops.lastSeenStreamMessage(guid.guid) - ops.lastSeenStreamMessage.put(guid.guid, entry.copy(shooting = Some(weaponGuid))) + case ChangeFireState_Start(weaponGuid) + if TestFilter(() => { NotSameTarget && ops.lastSeenStreamMessage.get(FilterGuid.guid).exists { _.visible } }) => + sendResponse(ChangeFireStateMessage_Start(weaponGuid)) + val entry = ops.lastSeenStreamMessage(FilterGuid.guid) + ops.lastSeenStreamMessage.put(FilterGuid.guid, entry.copy(shooting = Some(weaponGuid))) - case AvatarResponse.ChangeFireState_Start(weaponGuid) - if isNotSameTarget => - sendResponse(ChangeFireStateMessage_Start(weaponGuid)) + case ChangeFireState_Stop(weaponGuid) + if TestFilter(() => { NotSameTarget && ops.lastSeenStreamMessage.get(FilterGuid.guid).exists { msg => msg.visible || msg.shooting.nonEmpty } }) => + sendResponse(ChangeFireStateMessage_Stop(weaponGuid)) + val entry = ops.lastSeenStreamMessage(FilterGuid.guid) + ops.lastSeenStreamMessage.put(FilterGuid.guid, entry.copy(shooting = None)) - case AvatarResponse.ChangeFireState_Stop(weaponGuid) - if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { msg => msg.visible || msg.shooting.nonEmpty } => - sendResponse(ChangeFireStateMessage_Stop(weaponGuid)) - val entry = ops.lastSeenStreamMessage(guid.guid) - ops.lastSeenStreamMessage.put(guid.guid, entry.copy(shooting = None)) + case AvatarAction.LoadCreatedPlayer(pkt) + if TestFilter(NotSameTargetTest) => + sendResponse(pkt) - case AvatarResponse.ChangeFireState_Stop(weaponGuid) - if isNotSameTarget => - sendResponse(ChangeFireStateMessage_Stop(weaponGuid)) + case AvatarAction.EquipmentCreatedInHand(pkt) + if TestFilter(NotSameTargetTest) => + sendResponse(pkt) - case AvatarResponse.LoadPlayer(pkt) if isNotSameTarget => - sendResponse(pkt) + case AvatarAction.Destroy(victim, killer, weapon, pos) => + // guid = victim // killer = killer + sendResponse(DestroyMessage(victim, killer, weapon, pos)) - case AvatarResponse.EquipmentInHand(pkt) if isNotSameTarget => - sendResponse(pkt) + case AvatarAction.DestroyDisplay(killer, victim, method, unk) => + sendResponse(ops.destroyDisplayMessage(killer, victim, method, unk)) - case AvatarResponse.PlanetsideAttribute(attributeType, attributeValue) if isNotSameTarget => - sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue)) + case AvatarAction.TerminalOrderResult(terminalGuid, action, result) + if TestFilter(() => { result && (action == TransactionType.Buy || action == TransactionType.Loadout) }) => + sendResponse(ItemTransactionResultMessage(terminalGuid, action, result)) + sessionLogic.terminals.lastTerminalOrderFulfillment = true + AvatarActor.savePlayerData(player) - case AvatarResponse.PlanetsideAttributeToAll(attributeType, attributeValue) => - sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue)) + case AvatarAction.TerminalOrderResult(terminalGuid, action, result) => + sendResponse(ItemTransactionResultMessage(terminalGuid, action, result)) + sessionLogic.terminals.lastTerminalOrderFulfillment = true - case AvatarResponse.PlanetsideAttributeSelf(attributeType, attributeValue) if isSameTarget => - sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue)) - - case AvatarResponse.GenericObjectAction(objectGuid, actionCode) if isNotSameTarget => - sendResponse(GenericObjectActionMessage(objectGuid, actionCode)) - - case AvatarResponse.HitHint(sourceGuid) if player.isAlive => - sendResponse(HitHint(sourceGuid, guid)) - sessionLogic.zoning.CancelZoningProcess() - - case AvatarResponse.Destroy(victim, killer, weapon, pos) => - // guid = victim // killer = killer - sendResponse(DestroyMessage(victim, killer, weapon, pos)) - - case AvatarResponse.DestroyDisplay(killer, victim, method, unk) => - sendResponse(ops.destroyDisplayMessage(killer, victim, method, unk)) - - case AvatarResponse.TerminalOrderResult(terminalGuid, action, result) - if result && (action == TransactionType.Buy || action == TransactionType.Loadout) => - sendResponse(ItemTransactionResultMessage(terminalGuid, action, result)) - sessionLogic.terminals.lastTerminalOrderFulfillment = true - AvatarActor.savePlayerData(player) - - case AvatarResponse.TerminalOrderResult(terminalGuid, action, result) => - sendResponse(ItemTransactionResultMessage(terminalGuid, action, result)) - sessionLogic.terminals.lastTerminalOrderFulfillment = true - - case AvatarResponse.ChangeExosuit( - target, - armor, - exosuit, - subtype, - _, - maxhand, - oldHolsters, - holsters, - oldInventory, - inventory, - drop, - delete - ) if resolvedPlayerGuid == target => - sendResponse(ArmorChangedMessage(target, exosuit, subtype)) - sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) - //happening to this player - //cleanup - sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=false)) - (oldHolsters ++ oldInventory ++ delete).foreach { - case (_, dguid) => sendResponse(ObjectDeleteMessage(dguid, unk1=0)) - } - //functionally delete - delete.foreach { case (obj, _) => TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) } - //redraw - if (maxhand) { - TaskWorkflow.execute(HoldNewEquipmentUp(player)( - Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)), - 0 - )) - } - //draw free hand - player.FreeHand.Equipment.foreach { obj => + case AvatarAction.ChangeExosuit( + target, + armor, + exosuit, + subtype, + _, + maxhand, + oldHolsters, + holsters, + oldInventory, + inventory, + drop, + delete + ) if TestFilter(() => { ResolvedGuid == target }) => + sendResponse(ArmorChangedMessage(target, exosuit, subtype)) + sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) + //happening to this player + //cleanup + sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=false)) + (oldHolsters ++ oldInventory ++ delete).foreach { + case (_, dguid) => sendResponse(ObjectDeleteMessage(dguid, unk1=0)) + } + //functionally delete + delete.foreach { case (obj, _) => TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) } + //redraw + if (maxhand) { + TaskWorkflow.execute(HoldNewEquipmentUp(player)( + Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)), + 0 + )) + } + //draw free hand + player.FreeHand.Equipment.foreach { obj => + val definition = obj.Definition + sendResponse( + ObjectCreateDetailedMessage( + definition.ObjectId, + obj.GUID, + ObjectCreateMessageParent(target, Player.FreeHandSlot), + definition.Packet.DetailedConstructorData(obj).get + ) + ) + } + //draw holsters and inventory + (holsters ++ inventory).foreach { + case InventoryItem(obj, index) => val definition = obj.Definition sendResponse( ObjectCreateDetailedMessage( definition.ObjectId, obj.GUID, - ObjectCreateMessageParent(target, Player.FreeHandSlot), + ObjectCreateMessageParent(target, index), definition.Packet.DetailedConstructorData(obj).get ) ) - } - //draw holsters and inventory - (holsters ++ inventory).foreach { - case InventoryItem(obj, index) => - val definition = obj.Definition - sendResponse( - ObjectCreateDetailedMessage( - definition.ObjectId, - obj.GUID, - ObjectCreateMessageParent(target, index), - definition.Packet.DetailedConstructorData(obj).get - ) + } + DropLeftovers(player)(drop) + + case AvatarAction.ChangeExosuit(target, armor, exosuit, subtype, slot, _, oldHolsters, holsters, _, _, _, delete) => + sendResponse(ArmorChangedMessage(target, exosuit, subtype)) + sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) + //happening to some other player + sendResponse(ObjectHeldMessage(target, slot, unk1 = false)) + //cleanup + (oldHolsters ++ delete).foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) } + //draw holsters + holsters.foreach { + case InventoryItem(obj, index) => + val definition = obj.Definition + sendResponse( + ObjectCreateMessage( + definition.ObjectId, + obj.GUID, + ObjectCreateMessageParent(target, index), + definition.Packet.ConstructorData(obj).get ) - } - DropLeftovers(player)(drop) - - case AvatarResponse.ChangeExosuit(target, armor, exosuit, subtype, slot, _, oldHolsters, holsters, _, _, _, delete) => - sendResponse(ArmorChangedMessage(target, exosuit, subtype)) - sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) - //happening to some other player - sendResponse(ObjectHeldMessage(target, slot, unk1 = false)) - //cleanup - (oldHolsters ++ delete).foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) } - //draw holsters - holsters.foreach { - case InventoryItem(obj, index) => - val definition = obj.Definition - sendResponse( - ObjectCreateMessage( - definition.ObjectId, - obj.GUID, - ObjectCreateMessageParent(target, index), - definition.Packet.ConstructorData(obj).get - ) - ) - } - - case AvatarResponse.ChangeLoadout( - target, - armor, - exosuit, - subtype, - _, - maxhand, - oldHolsters, - holsters, - oldInventory, - inventory, - drops - ) if resolvedPlayerGuid == target => - sendResponse(ArmorChangedMessage(target, exosuit, subtype)) - sendResponse(PlanetsideAttributeMessage(target, attribute_type = 4, armor)) - //happening to this player - sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=true)) - //cleanup - (oldHolsters ++ oldInventory).foreach { - case (obj, objGuid) => - sendResponse(ObjectDeleteMessage(objGuid, unk1=0)) - TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) - } - drops.foreach(item => sendResponse(ObjectDeleteMessage(item.obj.GUID, unk1=0))) - //redraw - if (maxhand) { - TaskWorkflow.execute(HoldNewEquipmentUp(player)( - Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)), - slot = 0 - )) - } - sessionLogic.general.applyPurchaseTimersBeforePackingLoadout(player, player, holsters ++ inventory) - DropLeftovers(player)(drops) - - case AvatarResponse.ChangeLoadout(target, armor, exosuit, subtype, slot, _, oldHolsters, _, _, _, _) => - //redraw handled by callbacks - sendResponse(ArmorChangedMessage(target, exosuit, subtype)) - sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) - //happening to some other player - sendResponse(ObjectHeldMessage(target, slot, unk1=false)) - //cleanup - oldHolsters.foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) } - - case AvatarResponse.UseKit(kguid, kObjId) => - sendResponse( - UseItemMessage( - resolvedPlayerGuid, - kguid, - resolvedPlayerGuid, - unk2 = 4294967295L, - unk3 = false, - unk4 = Vector3.Zero, - unk5 = Vector3.Zero, - unk6 = 126, - unk7 = 0, //sequence time? - unk8 = 137, - kObjId ) - ) - sendResponse(ObjectDeleteMessage(kguid, unk1=0)) + } - case AvatarResponse.KitNotUsed(_, "") => - sessionLogic.general.kitToBeUsed = None - - case AvatarResponse.KitNotUsed(_, msg) => - sessionLogic.general.kitToBeUsed = None - sendResponse(ChatMsg(ChatMessageType.UNK_225, msg)) - - case AvatarResponse.UpdateKillsDeathsAssists(_, kda: Kill) if kda.experienceEarned > 0 => - continent.actor ! ZoneActor.RewardOurSupporters( - PlayerSource(player), - Players.produceContributionTranscriptFromKill(continent, player, kda), - kda, - kda.experienceEarned - ) - - case AvatarResponse.AwardBep(charId, bep, expType) => - //if the target player, always award (some) BEP - if (charId == player.CharId) { - avatarActor ! AvatarActor.AwardBep(bep, expType) - } - - case AvatarResponse.AwardCep(charId, cep) => - //if the target player, always award (some) CEP - if (charId == player.CharId) { - avatarActor ! AvatarActor.AwardCep(cep) - } - - case AvatarResponse.FacilityCaptureRewards(buildingId, zoneNumber, cep) => - ops.facilityCaptureRewards(buildingId, zoneNumber, cep) - - case AvatarResponse.SendResponse(pkt: AvatarImplantMessage) - if pkt.player_guid == player.GUID && pkt.action == ImplantAction.Initialization => - //special spectator implants stay initialized and do not deinitialize - () - - case AvatarResponse.SendResponse(msg) => - sendResponse(msg) - - case AvatarResponse.SendResponseTargeted(targetGuid, msg) if resolvedPlayerGuid == targetGuid => - sendResponse(msg) - - /* common messages (maybe once every respawn) */ - case AvatarResponse.Reload(itemGuid) - if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { _.visible } => - sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0)) - - case AvatarResponse.Killed(_, mount) => - //log and chat messages - val cause = player.LastDamage.flatMap { damage => - val interaction = damage.interaction - val reason = interaction.cause - val adversarial = interaction.adversarial.map { _.attacker } - reason match { - case r: ExplodingEntityReason if r.entity.isInstanceOf[VehicleSpawnPad] => - //also, @SVCP_Killed_TooCloseToPadOnCreate^n~ or "... within n meters of pad ..." - sendResponse(ChatMsg(ChatMessageType.UNK_227, "@SVCP_Killed_OnPadOnCreate")) - case _ => () - } - adversarial.map {_.Name }.orElse { Some(s"a ${reason.getClass.getSimpleName}") } - }.getOrElse { s"an unfortunate circumstance (probably ${player.Sex.pronounObject} own fault)" } - log.info(s"${player.Name} has died, killed by $cause") - if (sessionLogic.shooting.shotsWhileDead > 0) { - log.warn( - s"SHOTS_WHILE_DEAD: client of ${avatar.name} fired ${sessionLogic.shooting.shotsWhileDead} rounds while character was dead on server" - ) - sessionLogic.shooting.shotsWhileDead = 0 - } - sessionLogic.zoning.CancelZoningProcess() - - //player state changes - sessionLogic.zoning.spawn.avatarActive = false - AvatarActor.updateToolDischargeFor(avatar) - player.FreeHand.Equipment.foreach { item => - DropEquipmentFromInventory(player)(item) - } - sessionLogic.general.dropSpecialSlotItem() - sessionLogic.general.toggleMaxSpecialState(enable = false) - sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive - sessionLogic.zoning.zoningStatus = Zoning.Status.None - sessionLogic.zoning.spawn.deadState = DeadState.Dead - continent.GUID(mount).collect { case obj: Vehicle => - sessionLogic.vehicles.ConditionalDriverVehicleControl(obj) - sessionLogic.general.unaccessContainer(obj) - } - sessionLogic.actionsToCancel() - sessionLogic.terminals.CancelAllProximityUnits() - AvatarActor.savePlayerLocation(player) - sessionLogic.zoning.spawn.ShiftPosition = Some(player.Position) - - //respawn - sessionLogic.zoning.spawn.reviveTimer.cancel() - if (player.death_by == 0) { - sessionLogic.zoning.spawn.randomRespawn(300.seconds) - } else { - sessionLogic.zoning.spawn.HandleReleaseAvatar(player, continent) - } - - case AvatarResponse.Release(tplayer) if isNotSameTarget => - sessionLogic.zoning.spawn.DepictPlayerAsCorpse(tplayer) - - case AvatarResponse.Revive(revivalTargetGuid) if resolvedPlayerGuid == revivalTargetGuid => - log.info(s"No time for rest, ${player.Name}. Back on your feet!") - sessionLogic.zoning.spawn.reviveTimer.cancel() - sessionLogic.zoning.spawn.deadState = DeadState.Alive - player.Revive - val health = player.Health - sendResponse(PlanetsideAttributeMessage(revivalTargetGuid, attribute_type=0, health)) - sendResponse(AvatarDeadStateMessage(DeadState.Alive, timer_max=0, timer=0, player.Position, player.Faction, unk5=true)) - continent.AvatarEvents ! AvatarServiceMessage( - continent.id, - AvatarAction.PlanetsideAttributeToAll(revivalTargetGuid, attribute_type=0, health) - ) - - /* uncommon messages (utility, or once in a while) */ - case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) - if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { _.visible } => - ops.changeAmmoProcedures(weapon_guid, previous_guid, ammo_id, ammo_guid, weapon_slot, ammo_data) - sendResponse(ChangeAmmoMessage(weapon_guid, 1)) - - case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) - if isNotSameTarget => - ops.changeAmmoProcedures(weapon_guid, previous_guid, ammo_id, ammo_guid, weapon_slot, ammo_data) - - case AvatarResponse.ChangeFireMode(itemGuid, mode) if isNotSameTarget => - sendResponse(ChangeFireModeMessage(itemGuid, mode)) - - case AvatarResponse.ConcealPlayer() => - sendResponse(GenericObjectActionMessage(guid, code=9)) - - case AvatarResponse.EnvironmentalDamage(_, _, _) => - //TODO damage marker? - sessionLogic.zoning.CancelZoningProcess() - - case AvatarResponse.DropItem(pkt) if isNotSameTarget => - sendResponse(pkt) - - case AvatarResponse.ObjectDelete(itemGuid, unk) if isNotSameTarget => - sendResponse(ObjectDeleteMessage(itemGuid, unk)) - - /* rare messages */ - case AvatarResponse.SetEmpire(objectGuid, faction) if isNotSameTarget => - sendResponse(SetEmpireMessage(objectGuid, faction)) - - case AvatarResponse.DropSpecialItem() => - sessionLogic.general.dropSpecialSlotItem() - - case AvatarResponse.OxygenState(player, vehicle) => - sendResponse(OxygenStateMessage( - DrowningTarget(player.guid, player.progress, player.state), - vehicle.flatMap { vinfo => Some(DrowningTarget(vinfo.guid, vinfo.progress, vinfo.state)) } + case AvatarAction.ChangeLoadout( + target, + armor, + exosuit, + subtype, + _, + maxhand, + oldHolsters, + holsters, + oldInventory, + inventory, + drops + ) if TestFilter(() => { ResolvedGuid == target }) => + sendResponse(ArmorChangedMessage(target, exosuit, subtype)) + sendResponse(PlanetsideAttributeMessage(target, attribute_type = 4, armor)) + //happening to this player + sendResponse(ObjectHeldMessage(target, Player.HandsDownSlot, unk1=true)) + //cleanup + (oldHolsters ++ oldInventory).foreach { + case (obj, objGuid) => + sendResponse(ObjectDeleteMessage(objGuid, unk1=0)) + TaskWorkflow.execute(GUIDTask.unregisterEquipment(continent.GUID, obj)) + } + drops.foreach(item => sendResponse(ObjectDeleteMessage(item.obj.GUID, unk1=0))) + //redraw + if (maxhand) { + TaskWorkflow.execute(HoldNewEquipmentUp(player)( + Tool(GlobalDefinitions.MAXArms(subtype, player.Faction)), + slot = 0 )) + } + sessionLogic.general.applyPurchaseTimersBeforePackingLoadout(player, player, holsters ++ inventory) + DropLeftovers(player)(drops) - case AvatarResponse.LoadProjectile(pkt) if isNotSameTarget => - sendResponse(pkt) + case AvatarAction.ChangeLoadout(target, armor, exosuit, subtype, slot, _, oldHolsters, _, _, _, _) => + //redraw handled by callbacks + sendResponse(ArmorChangedMessage(target, exosuit, subtype)) + sendResponse(PlanetsideAttributeMessage(target, attribute_type=4, armor)) + //happening to some other player + sendResponse(ObjectHeldMessage(target, slot, unk1=false)) + //cleanup + oldHolsters.foreach { case (_, guid) => sendResponse(ObjectDeleteMessage(guid, unk1=0)) } - case AvatarResponse.ProjectileState(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid) if isNotSameTarget => - sendResponse(ProjectileStateMessage(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid)) - - case AvatarResponse.ProjectileExplodes(projectileGuid, projectile) => - sendResponse( - ProjectileStateMessage( - projectileGuid, - projectile.Position, - shot_vel = Vector3.Zero, - projectile.Orientation, - sequence_num=0, - end=true, - hit_target_guid=PlanetSideGUID(0) - ) + case AvatarAction.UseKit(kguid, kObjId) => + sendResponse( + UseItemMessage( + ResolvedGuid, + kguid, + ResolvedGuid, + unk2 = 4294967295L, + unk3 = false, + unk4 = Vector3.Zero, + unk5 = Vector3.Zero, + unk6 = 126, + unk7 = 0, //sequence time? + unk8 = 137, + kObjId ) - sendResponse(ObjectDeleteMessage(projectileGuid, unk1=2)) + ) + sendResponse(ObjectDeleteMessage(kguid, unk1=0)) - case AvatarResponse.ProjectileAutoLockAwareness(mode) => - sendResponse(GenericActionMessage(mode)) + case AvatarAction.KitNotUsed(_, "") => + sessionLogic.general.kitToBeUsed = None - case AvatarResponse.PutDownFDU(target) if isNotSameTarget => - sendResponse(GenericObjectActionMessage(target, code=53)) + case AvatarAction.KitNotUsed(_, msg) => + sessionLogic.general.kitToBeUsed = None + sendResponse(ChatMsg(ChatMessageType.UNK_225, msg)) - case AvatarResponse.StowEquipment(target, slot, item) if isNotSameTarget => - val definition = item.Definition - sendResponse( - ObjectCreateDetailedMessage( - definition.ObjectId, - item.GUID, - ObjectCreateMessageParent(target, slot), - definition.Packet.DetailedConstructorData(item).get - ) - ) + case AvatarAction.UpdateKillsDeathsAssists(_, kda: Kill) + if TestFilter(() => kda.experienceEarned > 0) => + continent.actor ! ZoneActor.RewardOurSupporters( + PlayerSource(player), + Players.produceContributionTranscriptFromKill(continent, player, kda), + kda, + kda.experienceEarned + ) - case AvatarResponse.WeaponDryFire(weaponGuid) - if isNotSameTarget && ops.lastSeenStreamMessage.get(guid.guid).exists { _.visible } => - continent.GUID(weaponGuid).collect { - case tool: Tool if tool.Magazine == 0 => - // check that the magazine is still empty before sending WeaponDryFireMessage - // if it has been reloaded since then, other clients will not see it firing - sendResponse(WeaponDryFireMessage(weaponGuid)) + case AvatarAction.AwardBep(charId, bep, expType) => + //if the target player, always award (some) BEP + if (charId == player.CharId) { + avatarActor ! AvatarActor.AwardBep(bep, expType) + } + + case AvatarAction.AwardCep(charId, cep) => + //if the target player, always award (some) CEP + if (charId == player.CharId) { + avatarActor ! AvatarActor.AwardCep(cep) + } + + case AvatarAction.FacilityCaptureRewards(buildingId, zoneNumber, cep) => + ops.facilityCaptureRewards(buildingId, zoneNumber, cep) + + case SendResponse(msgs) => + msgs.foreach { + case pkt: AvatarImplantMessage + if pkt.player_guid == player.GUID && pkt.action == ImplantAction.Initialization => + //special spectator implants stay initialized and do not deinitialize + () + case msg => + sendResponse(msg) + } + + /* common messages (maybe once every respawn) */ + case ReloadTool(itemGuid) + if TestFilter(() => { NotSameTarget && ops.lastSeenStreamMessage.get(FilterGuid.guid).exists { _.visible }}) => + sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0)) + + case AvatarAction.Killed(_, mount) => + //log and chat messages + val cause = player.LastDamage.flatMap { damage => + val interaction = damage.interaction + val reason = interaction.cause + val adversarial = interaction.adversarial.map { _.attacker } + reason match { + case r: ExplodingEntityReason if r.entity.isInstanceOf[VehicleSpawnPad] => + //also, @SVCP_Killed_TooCloseToPadOnCreate^n~ or "... within n meters of pad ..." + sendResponse(ChatMsg(ChatMessageType.UNK_227, "@SVCP_Killed_OnPadOnCreate")) + case _ => () } + adversarial.map {_.Name }.orElse { Some(s"a ${reason.getClass.getSimpleName}") } + }.getOrElse { s"an unfortunate circumstance (probably ${player.Sex.pronounObject} own fault)" } + log.info(s"${player.Name} has died, killed by $cause") + if (sessionLogic.shooting.shotsWhileDead > 0) { + log.warn( + s"SHOTS_WHILE_DEAD: client of ${avatar.name} fired ${sessionLogic.shooting.shotsWhileDead} rounds while character was dead on server" + ) + sessionLogic.shooting.shotsWhileDead = 0 + } + sessionLogic.zoning.CancelZoningProcess() - case _ => () - } + //player state changes + sessionLogic.zoning.spawn.avatarActive = false + AvatarActor.updateToolDischargeFor(avatar) + player.FreeHand.Equipment.foreach { item => + DropEquipmentFromInventory(player)(item) + } + sessionLogic.general.dropSpecialSlotItem() + sessionLogic.general.toggleMaxSpecialState(enable = false) + sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive + sessionLogic.zoning.zoningStatus = Zoning.Status.None + sessionLogic.zoning.spawn.deadState = DeadState.Dead + continent.GUID(mount).collect { case obj: Vehicle => + sessionLogic.vehicles.ConditionalDriverVehicleControl(obj) + sessionLogic.general.unaccessContainer(obj) + } + sessionLogic.actionsToCancel() + sessionLogic.terminals.CancelAllProximityUnits() + AvatarActor.savePlayerLocation(player) + sessionLogic.zoning.spawn.ShiftPosition = Some(player.Position) + + //respawn + sessionLogic.zoning.spawn.reviveTimer.cancel() + if (player.death_by == 0) { + sessionLogic.zoning.spawn.randomRespawn(300.seconds) + } else { + sessionLogic.zoning.spawn.HandleReleaseAvatar(player, continent) + } + + case AvatarAction.ReleasePlayer(tplayer) + if TestFilter(NotSameTargetTest) => + sessionLogic.zoning.spawn.DepictPlayerAsCorpse(tplayer) + + case AvatarAction.Revive(revivalTargetGuid) + if TestFilter(() => { ResolvedGuid == revivalTargetGuid }) => + log.info(s"No time for rest, ${player.Name}. Back on your feet!") + sessionLogic.zoning.spawn.reviveTimer.cancel() + sessionLogic.zoning.spawn.deadState = DeadState.Alive + player.Revive + val health = player.Health + sendResponse(PlanetsideAttributeMessage(revivalTargetGuid, attribute_type=0, health)) + sendResponse(AvatarDeadStateMessage(DeadState.Alive, timer_max=0, timer=0, player.Position, player.Faction, unk5=true)) + continent.AvatarEvents ! MessageEnvelope( + continent.id, + PlanetsideAttribute(revivalTargetGuid, attribute_type=0, health) + ) + + /* uncommon messages (utility, or once in a while) */ + case ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) + if TestFilter(NotSameTargetTest) => + ops.changeAmmoProcedure(weapon_guid, previous_guid, ammo_id, ammo_guid, weapon_slot, ammo_data) + sendResponse(ChangeAmmoMessage(weapon_guid, 1)) + + case AvatarAction.ChangeFireMode(itemGuid, mode) + if TestFilter(NotSameTargetTest) => + sendResponse(ChangeFireModeMessage(itemGuid, mode)) + + case AvatarAction.EnvironmentalDamage(_, _, _) => + //TODO damage marker? + sessionLogic.zoning.CancelZoningProcess() + + case AvatarAction.DropCreatedItem(pkt) + if TestFilter(NotSameTargetTest) => + sendResponse(pkt) + + /* rare messages */ + case AvatarAction.DropSpecialItem() => + sessionLogic.general.dropSpecialSlotItem() + + case AvatarAction.OxygenState(player, vehicle) => + sendResponse(OxygenStateMessage( + DrowningTarget(player.guid, player.progress, player.state), + vehicle.flatMap { vinfo => Some(DrowningTarget(vinfo.guid, vinfo.progress, vinfo.state)) } + )) + + case AvatarAction.LoadCreatedProjectile(pkt) + if TestFilter(NotSameTargetTest) => + sendResponse(pkt) + + case AvatarAction.ProjectileState(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid) + if TestFilter(NotSameTargetTest) => + sendResponse(ProjectileStateMessage(projectileGuid, shotPos, shotVel, shotOrient, seq, end, targetGuid)) + + case AvatarAction.ProjectileExplodes(projectileGuid, projectile) => + sendResponse( + ProjectileStateMessage( + projectileGuid, + projectile.Position, + shot_vel = Vector3.Zero, + projectile.Orientation, + sequence_num=0, + end=true, + hit_target_guid=PlanetSideGUID(0) + ) + ) + sendResponse(ObjectDeleteMessage(projectileGuid, unk1=2)) + + case AvatarAction.ProjectileAutoLockAwareness(mode) => + sendResponse(GenericActionMessage(mode)) + + case AvatarAction.PutDownFDU(target) + if TestFilter(NotSameTargetTest) => + sendResponse(GenericObjectActionMessage(target, code=53)) + + case AvatarAction.StowEquipment(target, slot, item) + if TestFilter(NotSameTargetTest) => + val definition = item.Definition + sendResponse( + ObjectCreateDetailedMessage( + definition.ObjectId, + item.GUID, + ObjectCreateMessageParent(target, slot), + definition.Packet.DetailedConstructorData(item).get + ) + ) + + case WeaponDryFire(weaponGuid) + if TestFilter(() => { NotSameTarget && ops.lastSeenStreamMessage.get(FilterGuid.guid).exists { _.visible } }) => + continent.GUID(weaponGuid).collect { + case tool: Tool if tool.Magazine == 0 => + sendResponse(WeaponDryFireMessage(weaponGuid)) + } } } 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 0987bfd1c..ebcf68ba3 100644 --- a/src/main/scala/net/psforever/actors/session/spectator/GeneralLogic.scala +++ b/src/main/scala/net/psforever/actors/session/spectator/GeneralLogic.scala @@ -16,7 +16,10 @@ import net.psforever.objects.zones.ZoneProjectile 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.avatar.AvatarAction +import net.psforever.services.base.CachedEnvelope +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.PlanetsideAttribute import net.psforever.types.{ExoSuitType, Vector3} import scala.concurrent.duration.DurationInt @@ -74,10 +77,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex player.Crouching = isCrouching player.Jumping = isJumping player.Cloaked = player.ExoSuit == ExoSuitType.Infiltration && isCloaking - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! CachedEnvelope( "spectator", + avatarGuid, AvatarAction.PlayerState( - avatarGuid, player.Position, player.Velocity, yaw, @@ -106,8 +109,7 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex } def handleEmote(pkt: EmoteMsg): Unit = { - val EmoteMsg(avatarGuid, emote) = pkt - sendResponse(EmoteMsg(avatarGuid, emote)) + sendResponse(pkt) } def handleDropItem(pkt: DropItemMessage): Unit = { /* intentionally blank */ } @@ -228,9 +230,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex case GenericAction.MaxAnchorsExtend_RCV => log.info(s"${player.Name} has anchored ${player.Sex.pronounObject}self to the ground") player.UsingSpecial = SpecialExoSuitDefinition.Mode.Anchored - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( continent.id, - AvatarAction.PlanetsideAttribute(player.GUID, 19, 1) + player.GUID, + PlanetsideAttribute(player.GUID, 19, 1) ) definition match { case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.trhev_burster => @@ -249,9 +252,10 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex case GenericAction.MaxAnchorsRelease_RCV => log.info(s"${player.Name} has released the anchors") player.UsingSpecial = SpecialExoSuitDefinition.Mode.Normal - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( continent.id, - AvatarAction.PlanetsideAttribute(player.GUID, 19, 0) + player.GUID, + PlanetsideAttribute(player.GUID, 19, 0) ) definition match { case GlobalDefinitions.trhev_dualcycler | GlobalDefinitions.trhev_burster => diff --git a/src/main/scala/net/psforever/actors/session/spectator/MountHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/spectator/MountHandlerLogic.scala index 42214bbd4..c56699848 100644 --- a/src/main/scala/net/psforever/actors/session/spectator/MountHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/spectator/MountHandlerLogic.scala @@ -12,9 +12,9 @@ import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech import net.psforever.objects.vital.InGameHistory import net.psforever.packet.game.{DelayedPathMountMsg, DismountVehicleCargoMsg, DismountVehicleMsg, GenericObjectActionMessage, MountVehicleCargoMsg, MountVehicleMsg, ObjectDetachMessage, PlayerStasisMessage, PlayerStateShiftMessage, ShiftState} -import net.psforever.services.Service -import net.psforever.services.local.{LocalAction, LocalServiceMessage} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.SendResponse +import net.psforever.services.vehicle.VehicleAction object MountHandlerLogic { def apply(ops: SessionMountHandlers): MountHandlerLogic = { @@ -61,9 +61,9 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act val (pos, zang) = Vehicles.dismountShuttle(obj, mountPoint) tplayer.Position = pos sendResponse(DelayedPathMountMsg(pguid, sguid, u1=60, u2=true)) - continent.LocalEvents ! LocalServiceMessage( + continent.LocalEvents ! MessageEnvelope( continent.id, - LocalAction.SendResponse(ObjectDetachMessage(sguid, pguid, pos, roll=0, pitch=0, zang)) + SendResponse(ObjectDetachMessage(sguid, pguid, pos, roll=0, pitch=0, zang)) ) obj.Zone.actor ! ZoneActor.RemoveFromBlockMap(player) sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive @@ -76,25 +76,21 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act ops.DismountAction(tplayer, obj, seatNum) continent.actor ! ZoneActor.RemoveFromBlockMap(player) //character doesn't need it //DismountAction(...) uses vehicle service, so use that service to coordinate the remainder of the messages - events ! VehicleServiceMessage( - player.Name, - VehicleAction.SendResponse(Service.defaultPlayerGUID, PlayerStasisMessage(pguid)) //the stasis message - ) //when the player dismounts, they will be positioned where the shuttle was when it disappeared in the sky //the player will fall to the ground and is perfectly vulnerable in this state //additionally, our player must exist in the current zone //having no in-game avatar target will throw us out of the map screen when deploying and cause softlock - events ! VehicleServiceMessage( - player.Name, - VehicleAction.SendResponse( - Service.defaultPlayerGUID, - PlayerStateShiftMessage(ShiftState(unk=0, obj.Position, obj.Orientation.z, vel=None)) //cower in the shuttle bay + events ! BundledEnvelope( + MessageEnvelope(player.Name, + SendResponse(Seq( + PlayerStasisMessage(pguid), + PlayerStateShiftMessage(ShiftState(unk=0, obj.Position, obj.Orientation.z, vel=None)) + )) + ), + MessageEnvelope(continent.id, pguid, + SendResponse(GenericObjectActionMessage(pguid, code=9)) /* conceal the player */ ) ) - events ! VehicleServiceMessage( - continent.id, - VehicleAction.SendResponse(pguid, GenericObjectActionMessage(pguid, code=9)) //conceal the player - ) context.self ! SessionActor.SetMode(NormalMode) sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive @@ -111,9 +107,10 @@ class MountHandlerLogic(val ops: SessionMountHandlers, implicit val context: Act ops.DismountVehicleAction(tplayer, obj, seatNum) case Mountable.CanDismount(obj: Vehicle, seat_num, _) => - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.KickPassenger(tplayer.GUID, seat_num, unk2=true, obj.GUID) + tplayer.GUID, + VehicleAction.KickPassenger(seat_num, unk2=true, obj.GUID) ) case Mountable.CanDismount(obj: PlanetSideGameObject with PlanetSideGameObject with Mountable with FactionAffinity with InGameHistory, seatNum, _) => diff --git a/src/main/scala/net/psforever/actors/session/spectator/SpectatorMode.scala b/src/main/scala/net/psforever/actors/session/spectator/SpectatorMode.scala index 483f13a31..59d438d3b 100644 --- a/src/main/scala/net/psforever/actors/session/spectator/SpectatorMode.scala +++ b/src/main/scala/net/psforever/actors/session/spectator/SpectatorMode.scala @@ -6,12 +6,12 @@ import net.psforever.actors.zone.ZoneActor import net.psforever.objects.avatar.{BattleRank, CommandRank, DeployableToolbox, FirstTimeEvents, Implant, ProgressDecoration, Shortcut => AvatarShortcut} import net.psforever.objects.ce.Deployable import net.psforever.objects.serverobject.ServerObject -import net.psforever.objects.{GlobalDefinitions, Player, Session, SimpleItem, Vehicle} +import net.psforever.objects.{Default, GlobalDefinitions, Player, Session, SimpleItem, Vehicle} import net.psforever.packet.PlanetSidePacket import net.psforever.packet.game.{DeployableInfo, DeployableObjectsInfoMessage, DeploymentAction, ObjectCreateDetailedMessage, ObjectDeleteMessage} import net.psforever.packet.game.objectcreate.{ObjectClass, ObjectCreateMessageParent, RibbonBars} -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.ObjectDelete import net.psforever.services.chat.SpectatorChannel import net.psforever.services.teamwork.{SquadAction, SquadServiceMessage} import net.psforever.types.{CapacitorStateType, ChatMessageType, ExoSuitType, MeritCommendation, SquadRequestType} @@ -68,7 +68,7 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic { player.Inventory.Items .foreach { entry => sendResponse(ObjectDeleteMessage(entry.GUID, 0)) } sendResponse(ObjectDeleteMessage(player.avatar.locker.GUID, 0)) - continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.ObjectDelete(pguid, pguid)) + continent.AvatarEvents ! MessageEnvelope(continent.id, pguid, ObjectDelete(pguid)) player.Holsters() .collect { case slot if slot.Equipment.nonEmpty => sendResponse(ObjectDeleteMessage(slot.Equipment.get.GUID, 0)) } val vehicleAndSeat = data.vehicles.GetMountableAndSeat(None, player, continent) match { @@ -111,7 +111,7 @@ class SpectatorModeLogic(data: SessionData) extends ModeLogic { .foreach { obj => sendResponse(DeployableObjectsInfoMessage( DeploymentAction.Dismiss, - DeployableInfo(obj.GUID, Deployable.Icon.apply(obj.Definition.Item), obj.Position, Service.defaultPlayerGUID) + DeployableInfo(obj.GUID, Deployable.Icon.apply(obj.Definition.Item), obj.Position, Default.GUID0) )) } if (player.silenced) { diff --git a/src/main/scala/net/psforever/actors/session/spectator/SquadHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/spectator/SquadHandlerLogic.scala index b73217603..b25d823f8 100644 --- a/src/main/scala/net/psforever/actors/session/spectator/SquadHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/spectator/SquadHandlerLogic.scala @@ -10,7 +10,7 @@ import net.psforever.objects.avatar.Avatar import net.psforever.packet.game.{CharacterKnowledgeInfo, CharacterKnowledgeMessage, ChatMsg, PlanetsideAttributeMessage, ReplicationStreamMessage, SquadAction, SquadDefinitionActionMessage, SquadDetailDefinitionUpdateMessage, SquadListing, SquadMemberEvent, SquadMembershipRequest, SquadMembershipResponse, SquadState, SquadStateInfo, SquadWaypointEvent, SquadWaypointRequest, WaypointEventAction} import net.psforever.services.chat.SquadChannel import net.psforever.services.teamwork.SquadResponse -import net.psforever.types.{ChatMessageType, PlanetSideGUID, SquadListDecoration, SquadResponseType} +import net.psforever.types.{PlanetSideGUID, SquadListDecoration, SquadResponseType} object SquadHandlerLogic { def apply(ops: SessionSquadHandlers): SquadHandlerLogic = { diff --git a/src/main/scala/net/psforever/actors/session/spectator/VehicleHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/spectator/VehicleHandlerLogic.scala index d68308a68..313254d87 100644 --- a/src/main/scala/net/psforever/actors/session/spectator/VehicleHandlerLogic.scala +++ b/src/main/scala/net/psforever/actors/session/spectator/VehicleHandlerLogic.scala @@ -1,14 +1,16 @@ // Copyright (c) 2024 PSForever package net.psforever.actors.session.spectator +import akka.actor.Actor.Receive import akka.actor.ActorContext import net.psforever.actors.session.support.{SessionData, SessionVehicleHandlers, VehicleHandlerFunctions} -import net.psforever.objects.{Tool, Vehicle, Vehicles} +import net.psforever.objects.{Vehicle, Vehicles} import net.psforever.objects.equipment.Equipment import net.psforever.objects.serverobject.pad.VehicleSpawnPad import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent -import net.psforever.packet.game.{ChangeAmmoMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChildObjectStateMessage, DeadState, DeployRequestMessage, DismountVehicleMsg, FrameVehicleStateMessage, GenericObjectActionMessage, HitHint, InventoryStateMessage, ObjectAttachMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, PlanetsideAttributeMessage, ReloadMessage, ServerVehicleOverrideMsg, VehicleStateMessage, WeaponDryFireMessage} -import net.psforever.services.vehicle.{VehicleResponse, VehicleServiceResponse} +import net.psforever.packet.game.{ChildObjectStateMessage, DeadState, DeployRequestMessage, DismountVehicleMsg, FrameVehicleStateMessage, GenericObjectActionMessage, InventoryStateMessage, ObjectAttachMessage, ObjectCreateDetailedMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, PlanetsideAttributeMessage, ServerVehicleOverrideMsg, VehicleStateMessage} +import net.psforever.services.base.envelope.GenericResponseEnvelope +import net.psforever.services.vehicle.{VehicleAction, VehicleStamp} import net.psforever.types.{BailType, PlanetSideGUID, Vector3} object VehicleHandlerLogic { @@ -24,230 +26,184 @@ class VehicleHandlerLogic(val ops: SessionVehicleHandlers, implicit val context: //private val galaxyService: ActorRef = ops.galaxyService - /** - * na - * - * @param toChannel na - * @param guid na - * @param reply na - */ - def handle(toChannel: String, guid: PlanetSideGUID, reply: VehicleResponse.Response): Unit = { - val resolvedPlayerGuid = if (player.HasGUID) { - player.GUID - } else { - PlanetSideGUID(-1) - } - val isNotSameTarget = resolvedPlayerGuid != guid - reply match { - case VehicleResponse.VehicleState( - vehicleGuid, - unk1, - pos, - orient, - vel, - unk2, - unk3, - unk4, - wheelDirection, - unk5, - unk6 - ) if isNotSameTarget && player.VehicleSeated.contains(vehicleGuid) => - //player who is also in the vehicle (not driver) - sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, orient, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6)) - player.Position = pos - player.Orientation = orient - player.Velocity = vel - sessionLogic.updateLocalBlockMap(pos) + def receive: Receive = { + case VehicleAction.VehicleState( + vehicleGuid, + unk1, + pos, + orient, + vel, + unk2, + unk3, + unk4, + wheelDirection, + unk5, + unk6 + ) if TestFilter(() => { NotSameTarget && player.VehicleSeated.contains(vehicleGuid) }) => + //player who is also in the vehicle (not driver) + sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, orient, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6)) + player.Position = pos + player.Orientation = orient + player.Velocity = vel + sessionLogic.updateLocalBlockMap(pos) - case VehicleResponse.VehicleState( - vehicleGuid, - unk1, - pos, - ang, - vel, - unk2, - unk3, - unk4, - wheelDirection, - unk5, - unk6 - ) if isNotSameTarget => - //player who is watching the vehicle from the outside - sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, ang, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6)) + case VehicleAction.VehicleState( + vehicleGuid, + unk1, + pos, + ang, + vel, + unk2, + unk3, + unk4, + wheelDirection, + unk5, + unk6 + ) if TestFilter(NotSameTargetTest) => + //player who is watching the vehicle from the outside + sendResponse(VehicleStateMessage(vehicleGuid, unk1, pos, ang, vel, unk2, unk3, unk4, wheelDirection, unk5, unk6)) - case VehicleResponse.ChildObjectState(objectGuid, pitch, yaw) if isNotSameTarget => - sendResponse(ChildObjectStateMessage(objectGuid, pitch, yaw)) + case VehicleAction.ChildObjectState(objectGuid, pitch, yaw) + if TestFilter(NotSameTargetTest) => + sendResponse(ChildObjectStateMessage(objectGuid, pitch, yaw)) - case VehicleResponse.FrameVehicleState(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA) - if isNotSameTarget => - sendResponse(FrameVehicleStateMessage(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA)) + case VehicleAction.FrameVehicleState(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA) + if TestFilter(NotSameTargetTest) => + sendResponse(FrameVehicleStateMessage(vguid, u1, pos, oient, vel, u2, u3, u4, is_crouched, u6, u7, u8, u9, uA)) - case VehicleResponse.ChangeFireState_Start(weaponGuid) if isNotSameTarget => - sendResponse(ChangeFireStateMessage_Start(weaponGuid)) + case VehicleAction.DismountVehicle(bailType, wasKickedByDriver) + if TestFilter(NotSameTargetTest) => + sendResponse(DismountVehicleMsg(FilterGuid, bailType, wasKickedByDriver)) - case VehicleResponse.ChangeFireState_Stop(weaponGuid) if isNotSameTarget => - sendResponse(ChangeFireStateMessage_Stop(weaponGuid)) + case VehicleAction.MountVehicle(vehicleGuid, seat) + if TestFilter(NotSameTargetTest) => + sendResponse(ObjectAttachMessage(vehicleGuid, FilterGuid, seat)) - case VehicleResponse.Reload(itemGuid) if isNotSameTarget => - sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0)) + case VehicleAction.DeployRequest(objectGuid, state, unk1, unk2, pos) + if TestFilter(NotSameTargetTest) => + sendResponse(DeployRequestMessage(FilterGuid, objectGuid, state, unk1, unk2, pos)) - case VehicleResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) if isNotSameTarget => - sendResponse(ObjectDetachMessage(weapon_guid, previous_guid, Vector3.Zero, 0)) - //TODO? sendResponse(ObjectDeleteMessage(previousAmmoGuid, 0)) - sendResponse( - ObjectCreateMessage( - ammo_id, - ammo_guid, - ObjectCreateMessageParent(weapon_guid, weapon_slot), - ammo_data - ) + case VehicleAction.EquipmentCreatedInSlot(pkt) + if TestFilter(NotSameTargetTest) => + sendResponse(pkt) + + case VehicleAction.InventoryState(obj, parentGuid, start, conData) + if TestFilter(NotSameTargetTest) => + //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly? + val objGuid = obj.GUID + sendResponse(ObjectDeleteMessage(objGuid, unk1=0)) + sendResponse(ObjectCreateDetailedMessage( + obj.Definition.ObjectId, + objGuid, + ObjectCreateMessageParent(parentGuid, start), + conData + )) + + case VehicleAction.KickPassenger(_, wasKickedByDriver, vehicleGuid) + if TestFilter(NotSameTargetTest) => + //seat number (first field) seems to be correct if passenger is kicked manually by driver + //but always seems to return 4 if user is kicked by mount permissions changing + sendResponse(DismountVehicleMsg(FilterGuid, BailType.Kicked, wasKickedByDriver)) + continent.GUID(vehicleGuid) match { + case Some(obj: Vehicle) => + sessionLogic.general.unaccessContainer(obj) + case _ => () + } + + case VehicleAction.KickPassenger(_, wasKickedByDriver, _) => + //seat number (first field) seems to be correct if passenger is kicked manually by driver + //but always seems to return 4 if user is kicked by mount permissions changing + sendResponse(DismountVehicleMsg(FilterGuid, BailType.Kicked, wasKickedByDriver)) + + case VehicleAction.InventoryState2(objGuid, parentGuid, value) + if TestFilter(NotSameTargetTest) => + sendResponse(InventoryStateMessage(objGuid, unk=0, parentGuid, value)) + + case VehicleAction.LoadVehicle(vehicle, vtype, vguid, vdata) + if TestFilter(NotSameTargetTest) => + //this is not be suitable for vehicles with people who are seated in it before it spawns (if that is possible) + sendResponse(ObjectCreateMessage(vtype, vguid, vdata)) + Vehicles.ReloadAccessPermissions(vehicle, player.Name) + + case VehicleAction.SeatPermissions(vehicleGuid, seatGroup, permission) + if TestFilter(NotSameTargetTest) => + sendResponse(PlanetsideAttributeMessage(vehicleGuid, seatGroup, permission)) + + case VehicleAction.UnloadVehicle(_, vehicleGuid) => + sendResponse(ObjectDeleteMessage(vehicleGuid, unk1=1)) + + case VehicleAction.UnstowEquipment(itemGuid) + if TestFilter(NotSameTargetTest) => + //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly? + sendResponse(ObjectDeleteMessage(itemGuid, unk1=0)) + + case VehicleAction.UpdateAmsSpawnList(list) => + sessionLogic.zoning.spawn.amsSpawnPoints = list.filter(tube => tube.Faction == player.Faction) + sessionLogic.zoning.spawn.DrawCurrentAmsSpawnPoint() + + case VehicleAction.KickCargo(vehicle, speed, delay) + if TestFilter(() => { player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive && speed > 0 }) => + val strafe = 1 + Vehicles.CargoOrientation(vehicle) + val reverseSpeed = if (strafe > 1) { 0 } else { speed } + //strafe or reverse, not both + sessionLogic.vehicles.ServerVehicleOverrideWithPacket( + vehicle, + ServerVehicleOverrideMsg( + lock_accelerator=true, + lock_wheel=true, + reverse=true, + unk4=false, + lock_vthrust=0, + strafe, + reverseSpeed, + unk8=Some(0) ) - sendResponse(ChangeAmmoMessage(weapon_guid, 1)) + ) + import scala.concurrent.ExecutionContext.Implicits.global + import scala.concurrent.duration._ + val resp = GenericResponseEnvelope( + VehicleStamp, + "", + PlanetSideGUID(0), + VehicleAction.KickCargo(vehicle, speed=0, delay) + ) + context.system.scheduler.scheduleOnce(delay milliseconds, context.self, resp) - case VehicleResponse.WeaponDryFire(weaponGuid) if isNotSameTarget => - continent.GUID(weaponGuid).collect { - case tool: Tool if tool.Magazine == 0 => - // check that the magazine is still empty before sending WeaponDryFireMessage - // if it has been reloaded since then, other clients will not see it firing - sendResponse(WeaponDryFireMessage(weaponGuid)) - } + case VehicleAction.KickCargo(cargo, _, _) + if TestFilter(() => { player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive }) => + sessionLogic.vehicles.TotalDriverVehicleControl(cargo) - case VehicleResponse.DismountVehicle(bailType, wasKickedByDriver) if isNotSameTarget => - sendResponse(DismountVehicleMsg(guid, bailType, wasKickedByDriver)) + case VehicleSpawnPad.AttachToRails(vehicle, pad) => + sendResponse(ObjectAttachMessage(pad.GUID, vehicle.GUID, slot=3)) - case VehicleResponse.MountVehicle(vehicleGuid, seat) if isNotSameTarget => - sendResponse(ObjectAttachMessage(vehicleGuid, guid, seat)) + case VehicleSpawnPad.ConcealPlayer(playerGuid) => + sendResponse(GenericObjectActionMessage(playerGuid, code=9)) - case VehicleResponse.DeployRequest(objectGuid, state, unk1, unk2, pos) if isNotSameTarget => - sendResponse(DeployRequestMessage(guid, objectGuid, state, unk1, unk2, pos)) - - case VehicleResponse.SendResponse(msg) => - sendResponse(msg) - - case VehicleResponse.AttachToRails(vehicleGuid, padGuid) => - sendResponse(ObjectAttachMessage(padGuid, vehicleGuid, slot=3)) - - case VehicleResponse.ConcealPlayer(playerGuid) => - sendResponse(GenericObjectActionMessage(playerGuid, code=9)) - - case VehicleResponse.DetachFromRails(vehicleGuid, padGuid, padPosition, padOrientationZ) => - val pad = continent.GUID(padGuid).get.asInstanceOf[VehicleSpawnPad].Definition - sendResponse( - ObjectDetachMessage( - padGuid, - vehicleGuid, - padPosition + Vector3.z(pad.VehicleCreationZOffset), - padOrientationZ + pad.VehicleCreationZOrientOffset - ) + case VehicleSpawnPad.DetachFromRails(vehicle, pad) => + val padDefinition = pad.Definition + sendResponse( + ObjectDetachMessage( + pad.GUID, + vehicle.GUID, + pad.Position + Vector3.z(padDefinition.VehicleCreationZOffset), + pad.Orientation.z + padDefinition.VehicleCreationZOrientOffset ) + ) - case VehicleResponse.EquipmentInSlot(pkt) if isNotSameTarget => - sendResponse(pkt) + case VehicleSpawnPad.ResetSpawnPad(pad) => + sendResponse(GenericObjectActionMessage(pad.GUID, code=23)) - case VehicleResponse.GenericObjectAction(objectGuid, action) if isNotSameTarget => - sendResponse(GenericObjectActionMessage(objectGuid, action)) + case VehicleSpawnPad.RevealPlayer(playerGuid) => + sendResponse(GenericObjectActionMessage(playerGuid, code=10)) - case VehicleResponse.InventoryState(obj, parentGuid, start, conData) if isNotSameTarget => - //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly? - val objGuid = obj.GUID - sendResponse(ObjectDeleteMessage(objGuid, unk1=0)) - sendResponse(ObjectCreateDetailedMessage( - obj.Definition.ObjectId, - objGuid, - ObjectCreateMessageParent(parentGuid, start), - conData - )) + case VehicleSpawnPad.ServerVehicleOverrideEnd(vehicle, _) => + sessionLogic.vehicles.ServerVehicleOverrideStop(vehicle) - case VehicleResponse.KickPassenger(_, wasKickedByDriver, vehicleGuid) if resolvedPlayerGuid == guid => - //seat number (first field) seems to be correct if passenger is kicked manually by driver - //but always seems to return 4 if user is kicked by mount permissions changing - sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver)) - continent.GUID(vehicleGuid) match { - case Some(obj: Vehicle) => - sessionLogic.general.unaccessContainer(obj) - case _ => () - } - - case VehicleResponse.KickPassenger(_, wasKickedByDriver, _) => - //seat number (first field) seems to be correct if passenger is kicked manually by driver - //but always seems to return 4 if user is kicked by mount permissions changing - sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver)) - - case VehicleResponse.InventoryState2(objGuid, parentGuid, value) if isNotSameTarget => - sendResponse(InventoryStateMessage(objGuid, unk=0, parentGuid, value)) - - case VehicleResponse.LoadVehicle(vehicle, vtype, vguid, vdata) if isNotSameTarget => - //this is not be suitable for vehicles with people who are seated in it before it spawns (if that is possible) - sendResponse(ObjectCreateMessage(vtype, vguid, vdata)) - Vehicles.ReloadAccessPermissions(vehicle, player.Name) - - case VehicleResponse.ObjectDelete(itemGuid) if isNotSameTarget => - sendResponse(ObjectDeleteMessage(itemGuid, unk1=0)) - - case VehicleResponse.PlanetsideAttribute(vehicleGuid, attributeType, attributeValue) if isNotSameTarget => - sendResponse(PlanetsideAttributeMessage(vehicleGuid, attributeType, attributeValue)) - - case VehicleResponse.ResetSpawnPad(padGuid) => - sendResponse(GenericObjectActionMessage(padGuid, code=23)) - - case VehicleResponse.RevealPlayer(playerGuid) => - sendResponse(GenericObjectActionMessage(playerGuid, code=10)) - - case VehicleResponse.SeatPermissions(vehicleGuid, seatGroup, permission) if isNotSameTarget => - sendResponse(PlanetsideAttributeMessage(vehicleGuid, seatGroup, permission)) - - case VehicleResponse.UnloadVehicle(_, vehicleGuid) => - sendResponse(ObjectDeleteMessage(vehicleGuid, unk1=1)) - - case VehicleResponse.UnstowEquipment(itemGuid) if isNotSameTarget => - //TODO prefer ObjectDetachMessage, but how to force ammo pools to update properly? - sendResponse(ObjectDeleteMessage(itemGuid, unk1=0)) - - case VehicleResponse.UpdateAmsSpawnPoint(list) => - sessionLogic.zoning.spawn.amsSpawnPoints = list.filter(tube => tube.Faction == player.Faction) - sessionLogic.zoning.spawn.DrawCurrentAmsSpawnPoint() - - case VehicleResponse.KickCargo(vehicle, speed, delay) - if player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive && speed > 0 => - val strafe = 1 + Vehicles.CargoOrientation(vehicle) - val reverseSpeed = if (strafe > 1) { 0 } else { speed } - //strafe or reverse, not both - sessionLogic.vehicles.ServerVehicleOverrideWithPacket( - vehicle, - ServerVehicleOverrideMsg( - lock_accelerator=true, - lock_wheel=true, - reverse=true, - unk4=false, - lock_vthrust=0, - strafe, - reverseSpeed, - unk8=Some(0) - ) - ) - import scala.concurrent.ExecutionContext.Implicits.global - import scala.concurrent.duration._ - context.system.scheduler.scheduleOnce( - delay milliseconds, - context.self, - VehicleServiceResponse(toChannel, PlanetSideGUID(0), VehicleResponse.KickCargo(vehicle, speed=0, delay)) - ) - - case VehicleResponse.KickCargo(cargo, _, _) - if player.VehicleSeated.nonEmpty && sessionLogic.zoning.spawn.deadState == DeadState.Alive => - sessionLogic.vehicles.TotalDriverVehicleControl(cargo) - - case VehicleResponse.ServerVehicleOverrideEnd(vehicle, _) => - sessionLogic.vehicles.ServerVehicleOverrideStop(vehicle) - - case VehicleResponse.ChangeLoadout(target, oldWeapons, _, oldInventory, _) => - //TODO when vehicle weapons can be changed without visual glitches, rewrite this - continent.GUID(target).collect { case vehicle: Vehicle => - changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory) - } - - case _ => () - } + case VehicleAction.ChangeLoadout(target, oldWeapons, _, oldInventory, _) => + //TODO when vehicle weapons can be changed without visual glitches, rewrite this + continent.GUID(target).collect { case vehicle: Vehicle => + changeLoadoutDeleteOldEquipment(vehicle, oldWeapons, oldInventory) + } } private def changeLoadoutDeleteOldEquipment( 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 c61499dd8..d20548bd3 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,9 @@ 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.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.CachedEnvelope +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.vehicle.VehicleAction import net.psforever.types.{DriveState, Vector3} object VehicleLogic { @@ -39,22 +41,10 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex obj.Velocity = vel sessionLogic.updateBlockMap(obj, pos) obj.zoneInteractions() - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! CachedEnvelope( continent.id, - VehicleAction.VehicleState( - player.GUID, - vehicle_guid, - unk1, - pos, - ang, - obj.Velocity, - obj.Flying, - 0, - 0, - 15, - unk5 = false, - obj.Cloaked - ) + player.GUID, + VehicleAction.VehicleState(vehicle_guid, unk1, pos, ang, obj.Velocity, obj.Flying, 0, 0, 15, unk5 = false, obj.Cloaked) ) case _ => () } @@ -92,9 +82,10 @@ class VehicleLogic(val ops: VehicleOperations, implicit val context: ActorContex val mobileShift: String = if (obj.DeploymentState != DriveState.Mobile) { obj.DeploymentState = DriveState.Mobile sendResponse(DeployRequestMessage(player.GUID, obj.GUID, DriveState.Mobile, 0, unk3=false, Vector3.Zero)) - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.DeployRequest(player.GUID, obj.GUID, DriveState.Mobile, 0, unk2=false, Vector3.Zero) + player.GUID, + VehicleAction.DeployRequest(obj.GUID, DriveState.Mobile, 0, unk2=false, Vector3.Zero) ) "; enforcing Mobile deployment state" } else { 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 b721628b3..0294b7531 100644 --- a/src/main/scala/net/psforever/actors/session/support/ChatOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/ChatOperations.scala @@ -17,10 +17,10 @@ import net.psforever.objects.sourcing.PlayerSource import net.psforever.objects.zones.{Zone, ZoneInfo} import net.psforever.packet.game.TimeOfDayMessage.GetTimeOfDayValue import net.psforever.packet.game.{SetChatFilterMessage, TimeOfDayMessage} -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse import net.psforever.services.chat.{DefaultChannel, OutfitChannel, SquadChannel} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +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 @@ -405,10 +405,7 @@ class ChatOperations( } else { if (building.CaptureTerminalIsHacked) { - zone.LocalEvents ! LocalServiceMessage( - zone.id, - LocalAction.ResecureCaptureTerminal(terminal, PlayerSource.Nobody) - ) + zone.LocalEvents ! CaptureEnvelope(HackCaptureActor.ResecureCaptureTerminal(terminal, zone, PlayerSource.Nobody)) } building.Actor ! BuildingActor.SetFaction(faction) building.Actor ! BuildingActor.AmenityStateChange(terminal, Some(false)) @@ -1444,10 +1441,7 @@ class ChatOperations( val msg = TimeOfDayMessage(zone.GetTimeOfDay(), zone.GetTimeOfDaySpeed()) // update players in zone - zone.AvatarEvents ! AvatarServiceMessage( - zone.id, - AvatarAction.SendResponse(Service.defaultPlayerGUID, msg) - ) + zone.AvatarEvents ! MessageEnvelope(zone.id, SendResponse(msg)) sendResponse(ChatMsg(messageType = UNK_227, contents = f"@CMT_SETTIME_OK^$hh~^$mm%02d~")) case _ => @@ -1482,10 +1476,7 @@ class ChatOperations( val msg = TimeOfDayMessage(zone.GetTimeOfDay(), zone.GetTimeOfDaySpeed()) // update players in zone - zone.AvatarEvents ! AvatarServiceMessage( - zone.id, - AvatarAction.SendResponse(Service.defaultPlayerGUID, msg) - ) + zone.AvatarEvents ! MessageEnvelope(zone.id, SendResponse(msg)) sendResponse(ChatMsg(messageType = UNK_227, contents = s"@CMT_SETTIMESPEED_OK^$timeSpeed~")) case _ => diff --git a/src/main/scala/net/psforever/actors/session/support/CommonHandlerFunctions.scala b/src/main/scala/net/psforever/actors/session/support/CommonHandlerFunctions.scala new file mode 100644 index 000000000..488406c7f --- /dev/null +++ b/src/main/scala/net/psforever/actors/session/support/CommonHandlerFunctions.scala @@ -0,0 +1,100 @@ +// Copyright (c) 2026 PSForever +package net.psforever.actors.session.support + +import akka.actor.Actor.Receive +import net.psforever.objects.{Default, Player} +import net.psforever.services.base.message.EventResponse +import net.psforever.types.PlanetSideGUID + +/** + * A shared filter container utilized by all response handlers connected by a certain mode. + * It is expected that the filters are treated in a true-oriented context - + * always check `isNotSameTarget` and not `!isSameTarget`, etc.. + */ +protected[support] class CommonHandlerFilters { + var resolvedGuid: PlanetSideGUID = Default.GUID0 + var filterGuid: PlanetSideGUID = Default.GUID0 + var isNotSameTarget: Boolean = false + var isSameTarget: Boolean = false + + def Configure(player: Player, guid: PlanetSideGUID): Unit = { + filterGuid = guid + if (player != null && player.HasGUID) { + resolvedGuid = player.GUID + isNotSameTarget = resolvedGuid != filterGuid + isSameTarget = resolvedGuid == filterGuid + } else { + resolvedGuid = Default.GUID0 + isNotSameTarget = false + isSameTarget = false + } + } +} + +trait CommonHandlerFunctions { + _: CommonSessionInterfacingFunctionality => + final def ResolvedGuid: PlanetSideGUID = sessionLogic.handlerFilters.resolvedGuid + final def FilterGuid: PlanetSideGUID = sessionLogic.handlerFilters.filterGuid + final def NotSameTarget: Boolean = sessionLogic.handlerFilters.isNotSameTarget + final def SameTarget: Boolean = sessionLogic.handlerFilters.isSameTarget + + val NotSameTargetTest: () => Boolean = () => NotSameTarget + + val SameTargetTest: () => Boolean = () => SameTarget + + private var ignoreFilter: Boolean = false + + final def IgnoreFilter: Boolean = ignoreFilter + + final def IgnoreFilter_=(state: Boolean): Boolean = { + ignoreFilter = state + IgnoreFilter + } + + /** + * Process the output of a received response envelope. + * Sets the response handler filter. + * @param toChannel set of subscribers on an event system bus the envelope should reach + * @param guid a specific subscriber endpoint to be excluded + * @param reply output payload transported by this envelope + * @return `true`, if the response was processed; `false`, otherwise + */ + final def handle(toChannel: String, guid: PlanetSideGUID, reply: EventResponse): Boolean = { + sessionLogic.handlerFilters.Configure(player, guid) + tryToHandle(reply) + } + + /** + * Can the response handler process this message (with the guard boolean permissions set as they currently are). + * @see `Actor.isDefinedAt` + * @param x payload for processing + * @return `true`, if the payload was processed; `false`, otherwise + */ + def isDefinedAt(x: Any): Boolean = receive.isDefinedAt(x) + + /** + * Process the output. + * @param x payload for processing + * @return `true`, if the payload was processed; `false`, otherwise + */ + final def tryToHandle(x: Any): Boolean = { + var passed = true + receive.applyOrElse(x, (_: Any) => { passed = false }) + passed + } + + /** + * If ignoring guard booleans (filters), always pass. + * If not, test filters using the provided function. + * @param filter contained guard booleans + * @return `true`. if ignoring filter tests or the filter test passed; `false`, otherwise + */ + final def TestFilter(filter: () => Boolean): Boolean = { + ignoreFilter || filter() + } + + /** + * @see `Actor.receive` + */ + def receive: Receive +} diff --git a/src/main/scala/net/psforever/actors/session/support/CommonHandlerLogic.scala b/src/main/scala/net/psforever/actors/session/support/CommonHandlerLogic.scala new file mode 100644 index 000000000..c66fff0f8 --- /dev/null +++ b/src/main/scala/net/psforever/actors/session/support/CommonHandlerLogic.scala @@ -0,0 +1,65 @@ +// Copyright (c) 2026 PSForever +package net.psforever.actors.session.support + +import akka.actor.Actor.Receive +import akka.actor.ActorContext +import net.psforever.objects.Tool +import net.psforever.packet.game.{ChangeAmmoMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, GenericObjectActionMessage, HitHint, ObjectDeleteMessage, PlanetsideAttributeMessage, ReloadMessage, SetEmpireMessage, WeaponDryFireMessage} +import net.psforever.services.base.message.{ChangeAmmo, ChangeFireState_Start, ChangeFireState_Stop, ConcealPlayer, GenericObjectAction, HintsAtAttacker, ObjectDelete, PlanetsideAttribute, ReloadTool, SendResponse, SetEmpire, WeaponDryFire} + +class CommonHandlerLogic(val sessionLogic: SessionData, implicit val context: ActorContext) + extends CommonSessionInterfacingFunctionality with CommonHandlerFunctions { + + def receive: Receive = { + case PlanetsideAttribute(target_guid, attributeType, attributeValue) + if TestFilter(NotSameTargetTest) => + sendResponse(PlanetsideAttributeMessage(target_guid, attributeType, attributeValue)) + + case GenericObjectAction(objectGuid, actionCode) + if TestFilter(NotSameTargetTest) => + sendResponse(GenericObjectActionMessage(objectGuid, actionCode)) + + case ObjectDelete(itemGuid, unk) + if TestFilter(NotSameTargetTest) => + sendResponse(ObjectDeleteMessage(itemGuid, unk)) + + case ChangeFireState_Start(weaponGuid) + if TestFilter(NotSameTargetTest) => + sendResponse(ChangeFireStateMessage_Start(weaponGuid)) + + case ChangeFireState_Stop(weaponGuid) + if TestFilter(NotSameTargetTest) => + sendResponse(ChangeFireStateMessage_Stop(weaponGuid)) + + case ReloadTool(itemGuid) + if TestFilter(NotSameTargetTest) => + sendResponse(ReloadMessage(itemGuid, ammo_clip=1, unk1=0)) + + case ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) + if TestFilter(NotSameTargetTest) => + sessionLogic.avatarResponse.changeAmmoProcedure(weapon_guid, previous_guid, ammo_id, ammo_guid, weapon_slot, ammo_data) + sendResponse(ChangeAmmoMessage(weapon_guid, 1)) + + case WeaponDryFire(weaponGuid) + if TestFilter(NotSameTargetTest) => + continent.GUID(weaponGuid).collect { + case tool: Tool if tool.Magazine == 0 => + sendResponse(WeaponDryFireMessage(weaponGuid)) + } + + case HintsAtAttacker(sourceGuid) + if TestFilter(() => { player.isAlive }) => + sendResponse(HitHint(sourceGuid, FilterGuid)) + sessionLogic.zoning.CancelZoningProcessWithDescriptiveReason("cancel_dmg") + + case SetEmpire(objectGuid, faction) + if TestFilter(NotSameTargetTest) => + sendResponse(SetEmpireMessage(objectGuid, faction)) + + case ConcealPlayer(_) => + sendResponse(GenericObjectActionMessage(FilterGuid, code=9)) + + case SendResponse(msgs) => + msgs.foreach(sendResponse) + } +} 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 9899a1b2c..3d34e5071 100644 --- a/src/main/scala/net/psforever/actors/session/support/GeneralOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/GeneralOperations.scala @@ -17,8 +17,12 @@ import net.psforever.objects.sourcing.{DeployableSource, PlayerSource, VehicleSo 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.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.avatar.support.GroundEnvelope +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.{ObjectDelete, PlanetsideAttribute, SendResponse} +import net.psforever.services.base.support.RemoverActor +import net.psforever.services.local.support.{CaptureEnvelope, HackCaptureActor} +import net.psforever.services.local.LocalAction import scala.collection.mutable import scala.concurrent.ExecutionContext.Implicits.global @@ -48,9 +52,9 @@ import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum import net.psforever.packet.game.objectcreate._ import net.psforever.packet.game._ import net.psforever.services.account.AccountPersistenceService -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction import net.psforever.services.local.support.CaptureFlagManager -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.vehicle.VehicleAction import net.psforever.services.Service import net.psforever.types._ import net.psforever.util.Config @@ -193,6 +197,32 @@ class GeneralOperations( private[session] var progressBarUpdate: Cancellable = Default.Cancellable private var charSavedTimer: Cancellable = Default.Cancellable + def handleEmote(pkt: EmoteMsg): Unit = { + val guid = player.GUID + val zone = player.Zone + val events = zone.LocalEvents + //todo better way to collect csr players while utilizing the aforementioned benefit of localSector + val position = player.Position + val rangeSq = { + val range = math.sqrt(2 * math.pow(sessionLogic.localSector.rangeX.toDouble, 2)) + range * range + } + val msg = SendResponse(pkt) + val (localRecipients, localRecipientMessages) = sessionLogic + .localSector + .livePlayerList + .filter(_.GUID != guid) + .map { p => (p.Name, MessageEnvelope(p.Name, msg)) } + .unzip + val otherRecipientMessages = zone + .AllPlayers + .filter { p => + !p.allowInteraction && p.GUID != guid && !localRecipients.contains(p.Name) && Vector3.DistanceSquared(p.Position, position) < rangeSq + } + .map(p => MessageEnvelope(p.Name, msg)) + events ! BundledEnvelope(localRecipientMessages ++ otherRecipientMessages) + } + def handleDropItem(pkt: DropItemMessage): GeneralOperations.ItemDropState.Behavior = { val DropItemMessage(itemGuid) = pkt (sessionLogic.validObject(itemGuid, decorator = "DropItem"), player.FreeHand.Equipment) match { @@ -376,7 +406,7 @@ class GeneralOperations( val detectedTargets = sessionLogic.shooting.FindDetectedProjectileTargets(targets) val mode = 7 + (if (weapon.Projectile == GlobalDefinitions.wasp_rocket_projectile) 1 else 0) detectedTargets.foreach { target => - continent.AvatarEvents ! AvatarServiceMessage(target, AvatarAction.ProjectileAutoLockAwareness(mode)) + continent.AvatarEvents ! MessageEnvelope(target, AvatarAction.ProjectileAutoLockAwareness(mode)) } case _ => () } @@ -456,7 +486,7 @@ class GeneralOperations( case attacker if attacker.Faction != player.Faction && System.currentTimeMillis() - llu.LastCollectionTime >= Config.app.game.experience.cep.lluSlayerCreditDuration.toMillis => - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( attacker.Name, AvatarAction.AwardCep(attacker.CharId, Config.app.game.experience.cep.lluSlayerCredit) ) @@ -714,7 +744,7 @@ class GeneralOperations( * @param channel the channel name */ private def unaccessContainerChannel(events: ActorRef, channel: String): Unit = { - events ! Service.Leave(Some(channel)) + events ! Service.Leave(channel) } /** @@ -851,7 +881,7 @@ class GeneralOperations( case _ if continent.EquipmentOnGround.contains(obj) => obj.Position = Vector3.Zero continent.Ground ! Zone.Ground.RemoveItem(objectGuid) - continent.AvatarEvents ! AvatarServiceMessage.Ground(RemoverActor.ClearSpecific(List(obj), continent)) + continent.AvatarEvents ! GroundEnvelope(RemoverActor.ClearSpecific(List(obj), continent)) true case _ => Zone.EquipmentIs.Where(obj, objectGuid, continent) match { @@ -876,9 +906,22 @@ class GeneralOperations( * @param unk2 na */ def hackObject(targetGuid: PlanetSideGUID, unk1: Long, unk2: HackState7): Unit = { - sendResponse(HackMessage(HackState1.Unk0, targetGuid, player_guid=Service.defaultPlayerGUID, progress=100, unk1.toFloat, HackState.Hacked, unk2)) + sendResponse(HackMessage(HackState1.Unk0, targetGuid, player_guid=Default.GUID0, progress=100, unk1.toFloat, HackState.Hacked, unk2)) } + /** + * Send a PlanetsideAttributeMessage packet to the client + * @param targetGuid The target of the attribute + * @param attribute The attribute + * @param attributeValue The attribute value + */ + def sendPlanetsideAttributeMessage( + targetGuid: PlanetSideGUID, + attribute: PlanetsideAttributeEnum, + attributeValue: Long + ): Unit = { + sendPlanetsideAttributeMessage(targetGuid, attribute.id, attributeValue) + } /** * Send a PlanetsideAttributeMessage packet to the client * @param targetGuid The target of the attribute @@ -887,7 +930,7 @@ class GeneralOperations( */ def sendPlanetsideAttributeMessage( targetGuid: PlanetSideGUID, - attributeNumber: PlanetsideAttributeEnum, + attributeNumber: Int, attributeValue: Long ): Unit = { sendResponse(PlanetsideAttributeMessage(targetGuid, attributeNumber, attributeValue)) @@ -986,18 +1029,18 @@ class GeneralOperations( sendResponse(ChatMsg(ChatMessageType.UNK_227, "@ArmorShieldOff")) } player.UsingSpecial = SpecialExoSuitDefinition.Mode.Normal - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( continent.id, - AvatarAction.PlanetsideAttributeToAll(player.GUID, 8, 0) + PlanetsideAttribute(player.GUID, 8, 0) ) } } } private def activateMaxSpecialStateMessage(): Unit = { - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( continent.id, - AvatarAction.PlanetsideAttributeToAll(player.GUID, 8, 1) + PlanetsideAttribute(player.GUID, 8, 1) ) } @@ -1010,9 +1053,10 @@ class GeneralOperations( case (Some(obj), Some(seatNum)) => tplayer.VehicleSeated = None obj.Seats(seatNum).unmount(tplayer) - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.KickPassenger(tplayer.GUID, seatNum, unk2=false, obj.GUID) + tplayer.GUID, + VehicleAction.KickPassenger(seatNum, unk2=false, obj.GUID) ) case _ => () } @@ -1249,7 +1293,7 @@ class GeneralOperations( continent.GUID(specialItemSlotGuid) match { case Some(llu: CaptureFlag) => if (llu.Target.GUID == captureTerminal.Owner.GUID) { - continent.LocalEvents ! LocalServiceMessage(continent.id, LocalAction.LluCaptured(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}" @@ -1432,23 +1476,21 @@ class GeneralOperations( val events = continent.AvatarEvents val zoneid = continent.id val destinationPosition = dest.Position - events ! AvatarServiceMessage(zoneid, AvatarAction.ObjectDelete(pguid, pguid)) - events ! AvatarServiceMessage(player.Name, - AvatarAction.SendResponse(PlanetSideGUID(0), PlayerStateShiftMessage(ShiftState(0, destinationPosition, player.Orientation.z))) - ) player.Position = destinationPosition - events ! AvatarServiceMessage(zoneid, AvatarAction.LoadPlayer( - pguid, - player.Definition.ObjectId, - pguid, - player.Definition.Packet.ConstructorData(player).get, - None - )) - useRouterTelepadEffect(pguid, sguid, dguid) - continent.LocalEvents ! LocalServiceMessage( - continent.id, - LocalAction.RouterTelepadTransport(pguid, pguid, sguid, dguid) + events ! BundledEnvelope( + MessageEnvelope(zoneid, pguid, ObjectDelete(pguid)), + MessageEnvelope(player.Name, + SendResponse(PlayerStateShiftMessage(ShiftState(0, destinationPosition, player.Orientation.z))) + ), + MessageEnvelope(zoneid, pguid, AvatarAction.LoadPlayer( + player.Definition.ObjectId, + pguid, + player.Definition.Packet.ConstructorData(player).get, + None + )), + MessageEnvelope(zoneid, pguid, LocalAction.RouterTelepadTransport(pguid, sguid, dguid)) ) + useRouterTelepadEffect(pguid, sguid, dguid) sessionLogic.zoning.spawn.ShiftPosition = destinationPosition player.LogActivity(TelepadUseActivity(VehicleSource(router), DeployableSource(remoteTelepad), PlayerSource(player))) } else { diff --git a/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala index 7c7002e51..2ac39d632 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala @@ -7,8 +7,9 @@ import net.psforever.objects.{Default, PlanetSideGameObject, Player, Vehicle} import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, UniquePlayer} import net.psforever.packet.game.objectcreate.ConstructorData import net.psforever.objects.zones.exp -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage, AvatarServiceResponse} +import net.psforever.services.avatar.{AvatarAction, AvatarStamp} +import net.psforever.services.base.envelope.{GenericResponseEnvelope, MessageEnvelope} +import net.psforever.services.base.message.SendResponse import net.psforever.services.chat.OutfitChannel import scala.collection.mutable @@ -16,14 +17,11 @@ import scala.collection.mutable import net.psforever.actors.session.AvatarActor import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent import net.psforever.packet.game._ -import net.psforever.services.avatar.AvatarResponse import net.psforever.types._ import net.psforever.util.Config -trait AvatarHandlerFunctions extends CommonSessionInterfacingFunctionality { +trait AvatarHandlerFunctions extends CommonSessionInterfacingFunctionality with CommonHandlerFunctions { val ops: SessionAvatarHandlers - - def handle(toChannel: String, guid: PlanetSideGUID, reply: AvatarResponse.Response): Unit } class SessionAvatarHandlers( @@ -36,7 +34,7 @@ class SessionAvatarHandlers( mutable.LongMap[SessionAvatarHandlers.LastUpstream]() private[session] val hidingPlayerRandomizer = new scala.util.Random - def changeAmmoProcedures( + def changeAmmoProcedure( weaponGuid: PlanetSideGUID, previousAmmoGuid: PlanetSideGUID, ammoTypeId: Int, @@ -45,7 +43,7 @@ class SessionAvatarHandlers( ammoData: ConstructorData ): Unit = { sendResponse(ObjectDetachMessage(weaponGuid, previousAmmoGuid, Vector3.Zero, 0)) - //TODO? sendResponse(ObjectDeleteMessage(previousAmmoGuid, 0)) + sendResponse(ObjectDeleteMessage(previousAmmoGuid, 0)) sendResponse( ObjectCreateMessage( ammoTypeId, @@ -140,7 +138,7 @@ class SessionAvatarHandlers( val playersInZone = killer.Zone.Players.map { avatar => (avatar.id, avatar.basic.name) } val squadMembersHere = playersInZone.filter(member => squadMembers.contains(member._2)) squadMembersHere.foreach { member => - killer.Zone.AvatarEvents ! AvatarServiceMessage( + killer.Zone.AvatarEvents ! MessageEnvelope( member._2, AvatarAction.AwardBep(member._1, expSplit, ExperienceType.Normal)) } @@ -155,7 +153,7 @@ class SessionAvatarHandlers( val playersInZone = vehicle.Zone.Players.map { avatar => (avatar.id, avatar.basic.name) } val squadMembersHere = playersInZone.filter(member => squadMembers.contains(member._2)) squadMembersHere.foreach { member => - vehicle.Zone.AvatarEvents ! AvatarServiceMessage( + vehicle.Zone.AvatarEvents ! MessageEnvelope( member._2, AvatarAction.AwardBep(member._1, exp, ExperienceType.Normal)) } @@ -212,12 +210,11 @@ class SessionAvatarHandlers( def killedWhileMounted(obj: PlanetSideGameObject with Mountable, playerGuid: PlanetSideGUID): Unit = { val playerName = player.Name //boot cadaver from mount on client - context.self ! AvatarServiceResponse( + context.self ! GenericResponseEnvelope( + AvatarStamp, playerName, - Service.defaultPlayerGUID, - AvatarResponse.SendResponse( - ObjectDetachMessage(obj.GUID, playerGuid, player.Position, Vector3.Zero) - ) + Default.GUID0, + SendResponse(ObjectDetachMessage(obj.GUID, playerGuid, player.Position, Vector3.Zero)) ) //player no longer seated obj.PassengerInSeat(player).foreach { seatNumber => @@ -239,7 +236,7 @@ class SessionAvatarHandlers( object SessionAvatarHandlers { private[session] case class LastUpstream( - msg: Option[AvatarResponse.PlayerState], + msg: Option[AvatarAction.PlayerState], visible: Boolean, shooting: Option[PlanetSideGUID], time: Long diff --git a/src/main/scala/net/psforever/actors/session/support/SessionData.scala b/src/main/scala/net/psforever/actors/session/support/SessionData.scala index 33b838102..7368c358f 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionData.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionData.scala @@ -4,6 +4,7 @@ package net.psforever.actors.session.support import akka.actor.typed.receptionist.Receptionist import akka.actor.typed.scaladsl.adapter._ import akka.actor.{ActorContext, ActorRef, typed} +import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.services.chat.ChatService import scala.collection.mutable @@ -31,7 +32,7 @@ import net.psforever.packet._ import net.psforever.packet.game._ import net.psforever.services.account.AccountPersistenceService import net.psforever.services.ServiceManager.LookupResult -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.vehicle.VehicleAction import net.psforever.services.{Service, InterstellarClusterService => ICS} import net.psforever.types._ import net.psforever.util.Config @@ -119,6 +120,8 @@ class SessionData( def zoning: ZoningOperations = zoningOpt.orNull def chat: ChatOperations = chatOpt.orNull + val handlerFilters: CommonHandlerFilters = new CommonHandlerFilters() + ServiceManager.serviceManager ! Lookup("accountIntermediary") ServiceManager.serviceManager ! Lookup("accountPersistence") ServiceManager.serviceManager ! Lookup("galaxy") @@ -567,9 +570,10 @@ class SessionData( case (Some(obj), Some(seatNum)) => tplayer.VehicleSeated = None obj.Seats(seatNum).unmount(tplayer) - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.KickPassenger(tplayer.GUID, seatNum, unk2=false, obj.GUID) + tplayer.GUID, + VehicleAction.KickPassenger(seatNum, unk2=false, obj.GUID) ) case _ => () } @@ -615,12 +619,12 @@ class SessionData( squadResponseOpt.foreach(_.stop()) zoningOpt.foreach(_.stop()) chatOpt.foreach(_.stop()) - continent.AvatarEvents ! Service.Leave() - continent.LocalEvents ! Service.Leave() - continent.VehicleEvents ! Service.Leave() - galaxyService ! Service.Leave() + continent.AvatarEvents ! Service.LeaveAll + continent.LocalEvents ! Service.LeaveAll + continent.VehicleEvents ! Service.LeaveAll + galaxyService ! Service.LeaveAll if (avatar != null && squadService != Default.Actor) { - squadService ! Service.Leave() + squadService ! Service.LeaveAll } } } diff --git a/src/main/scala/net/psforever/actors/session/support/SessionGalaxyHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionGalaxyHandlers.scala index ad7614296..419c74c0d 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionGalaxyHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionGalaxyHandlers.scala @@ -3,16 +3,13 @@ package net.psforever.actors.session.support import akka.actor.{ActorContext, ActorRef, typed} import net.psforever.packet.game.FriendsResponse -// -import net.psforever.actors.session.AvatarActor -import net.psforever.services.galaxy.GalaxyResponse -trait GalaxyHandlerFunctions extends CommonSessionInterfacingFunctionality { +import net.psforever.actors.session.AvatarActor + +trait GalaxyHandlerFunctions extends CommonSessionInterfacingFunctionality with CommonHandlerFunctions { def ops: SessionGalaxyHandlers def handleUpdateIgnoredPlayers(pkt: FriendsResponse): Unit - - def handle(reply: GalaxyResponse.Response): Unit } class SessionGalaxyHandlers( diff --git a/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala index 8480a1d55..583a83f87 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionLocalHandlers.scala @@ -7,17 +7,14 @@ import net.psforever.objects.ce.Deployable import net.psforever.objects.guid.{GUIDTask, TaskWorkflow} import net.psforever.objects.serverobject.interior.Sidedness import net.psforever.packet.game.{GenericObjectActionMessage, ObjectDeleteMessage, PlanetsideAttributeMessage, TriggerEffectMessage} -import net.psforever.services.local.LocalResponse import net.psforever.types.{PlanetSideGUID, Vector3} -trait LocalHandlerFunctions extends CommonSessionInterfacingFunctionality { +trait LocalHandlerFunctions extends CommonSessionInterfacingFunctionality with CommonHandlerFunctions { def ops: SessionLocalHandlers def handleTurretDeployableIsDismissed(obj: TurretDeployable): Unit def handleDeployableIsDismissed(obj: Deployable): Unit - - def handle(toChannel: String, guid: PlanetSideGUID, reply: LocalResponse.Response): Unit } class SessionLocalHandlers( 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 7f5004529..67c410492 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionMountHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionMountHandlers.scala @@ -5,11 +5,14 @@ import akka.actor.{ActorContext, typed} import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.interior.Sidedness.OutsideOf import net.psforever.objects.{PlanetSideGameObject, Tool, Vehicle} -import net.psforever.objects.vehicles.{CargoBehavior, MountableWeapons} +import net.psforever.objects.vehicles.MountableWeapons +import net.psforever.objects.vehicles.control.CargoBehavior import net.psforever.objects.vital.InGameHistory import net.psforever.packet.game.{DismountVehicleCargoMsg, GenericObjectActionMessage, InventoryStateMessage, MountVehicleCargoMsg, MountVehicleMsg, ObjectAttachMessage, ObjectDetachMessage, PlanetsideAttributeMessage} -import net.psforever.services.Service -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.CachedEnvelope +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse +import net.psforever.services.vehicle.VehicleAction import net.psforever.types.{BailType, PlanetSideGUID, Vector3} // import net.psforever.actors.session.AvatarActor @@ -185,9 +188,10 @@ class SessionMountHandlers( avatarActor ! AvatarActor.DeactivateActiveImplants avatarActor ! AvatarActor.SuspendStaminaRegeneration(3.seconds) sendResponse(ObjectAttachMessage(objGuid, playerGuid, seatNum)) - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.MountVehicle(playerGuid, objGuid, seatNum) + playerGuid, + VehicleAction.MountVehicle(objGuid, seatNum) ) } @@ -202,13 +206,12 @@ class SessionMountHandlers( if (tplayer.BailProtection) { tplayer.ContributionFrom(obj) sessionLogic.keepAliveFunc = sessionLogic.zoning.NormalKeepAlive - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.SendResponse(Service.defaultPlayerGUID, PlanetsideAttributeMessage(obj.GUID, 81, 1)) - ) - continent.VehicleEvents ! VehicleServiceMessage( - continent.id, - VehicleAction.SendResponse(Service.defaultPlayerGUID, ObjectDetachMessage(obj.GUID, tplayer.GUID, tplayer.Position, obj.Orientation)) + SendResponse( + PlanetsideAttributeMessage(obj.GUID, 81, 1), + ObjectDetachMessage(obj.GUID, tplayer.GUID, tplayer.Position, obj.Orientation) + ) ) } else { @@ -223,22 +226,10 @@ class SessionMountHandlers( sessionLogic.vehicles.ServerVehicleOverrideStop(v) }*/ v.Velocity = Vector3.Zero - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! CachedEnvelope( continent.id, - VehicleAction.VehicleState( - tplayer.GUID, - v.GUID, - unk1 = 0, - tplayer.Position, - v.Orientation, - v.Velocity, - v.Flying, - unk3 = 0, - unk4 = 0, - wheel_direction = 15, - unk5 = false, - unk6 = v.Cloaked - ) + 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) ) case _ => () } @@ -260,9 +251,10 @@ class SessionMountHandlers( BailType.Normal } sendResponse(DismountVehicleMsg(playerGuid, bailType, wasKickedByDriver = false)) - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.DismountVehicle(playerGuid, bailType, unk2 = false) + playerGuid, + VehicleAction.DismountVehicle(bailType, unk2 = false) ) } diff --git a/src/main/scala/net/psforever/actors/session/support/SessionOutfitHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionOutfitHandlers.scala index 80bebf9c9..8fc619912 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionOutfitHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionOutfitHandlers.scala @@ -8,7 +8,9 @@ import net.psforever.objects.Player import net.psforever.packet.game.OutfitEventAction.{Initial, Leaving, OutfitInfo, OutfitRankNames, Unk1, Update, UpdateMemberCount} import net.psforever.packet.game.OutfitMembershipResponse.PacketType.CreateResponse import net.psforever.packet.game._ -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} import net.psforever.services.chat.OutfitChannel import net.psforever.types.ChatMessageType import net.psforever.util.Config @@ -48,58 +50,62 @@ object SessionOutfitHandlers { import scala.concurrent.Future def HandleOutfitForm(outfitName: String, player: Player, session: SessionData): Unit = { + val zone = player.Zone + val zoneid = zone.id + val charid = player.CharId + val pname = player.Name val cleanedName = sanitizeOutfitName(outfitName) cleanedName match { case Some(validName) => ctx.run(findOutfitByName(validName)).flatMap { case existing if existing.nonEmpty => - PlayerControl.sendResponse(player.Zone, player.Name, - ChatMsg(ChatMessageType.UNK_227, "@OutfitErrorNameAlreadyTaken")) + PlayerControl.sendResponse(zone, pname, + ChatMsg(ChatMessageType.UNK_227, "@OutfitErrorNameAlreadyTaken") + ) Future.successful(()) case _ => - createNewOutfit(validName, player.Faction.id, player.CharId).map { outfit => - val seconds: Long = - outfit.created.atZone(ZoneId.systemDefault()).toInstant.toEpochMilli / 1000 - - PlayerControl.sendResponse(player.Zone, player.Name, - OutfitEvent(outfit.id, Update( - OutfitInfo( - outfit.name, 0, 0, 1, - OutfitRankNames("", "", "", "", "", "", "", ""), - "", - 14, unk11 = true, 0, seconds, 0, 0, 0)))) - - PlayerControl.sendResponse(player.Zone, player.Name, - OutfitMemberUpdate(outfit.id, player.CharId, 7, flag = true)) - - PlayerControl.sendResponse(player.Zone, player.Name, - ChatMsg(ChatMessageType.UNK_227, "@OutfitCreateSuccess")) - - PlayerControl.sendResponse(player.Zone, player.Name, - OutfitMembershipResponse(CreateResponse, 0, 0, player.CharId, 0, "", "", flag = true)) - - player.outfit_id = outfit.id - player.outfit_name = outfit.name - - player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.id, - AvatarAction.PlanetsideAttributeToAll(player.GUID, 39, outfit.id)) - - player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.id, - AvatarAction.PlanetsideStringAttribute(player.GUID, 0, outfit.name)) - - session.chat.JoinChannel(OutfitChannel(player.outfit_id)) + createNewOutfit(validName, player.Faction.id, charid).map { outfit => + val outfitId = outfit.id + val outfitName = outfit.name + val seconds: Long = outfit.created.atZone(ZoneId.systemDefault()).toInstant.toEpochMilli / 1000 + player.outfit_id = outfitId + player.outfit_name = outfitName + zone.AvatarEvents ! BundledEnvelope( + MessageEnvelope(pname, SendResponse( + OutfitEvent(outfitId, Update( + OutfitInfo( + outfitName, 0, 0, 1, + OutfitRankNames("", "", "", "", "", "", "", ""), + "", + 14, unk11 = true, 0, seconds, 0, 0, 0) + )), + OutfitMemberUpdate(outfitId, charid, 7, flag = true), + ChatMsg(ChatMessageType.UNK_227, "@OutfitCreateSuccess"), + OutfitMembershipResponse(CreateResponse, 0, 0, charid, 0, "", "", flag = true), + )), + MessageEnvelope( + zoneid, + PlanetsideAttribute(player.GUID, 39, outfitId) + ), + MessageEnvelope( + zoneid, + player.GUID, + AvatarAction.PlanetsideStringAttribute(0, outfitName) + ) + ) + session.chat.JoinChannel(OutfitChannel(outfitId)) } .recover { case e => e.printStackTrace() - PlayerControl.sendResponse(player.Zone, player.Name, - ChatMsg(ChatMessageType.UNK_227, "@OutfitCreateFailure")) + PlayerControl.sendResponse(zone, pname, + ChatMsg(ChatMessageType.UNK_227, "@OutfitCreateFailure") + ) } } case None => - PlayerControl.sendResponse(player.Zone, player.Name, - ChatMsg(ChatMessageType.UNK_227, "@OutfitCreateFailure")) + PlayerControl.sendResponse(zone, pname, ChatMsg(ChatMessageType.UNK_227, "@OutfitCreateFailure")) } } @@ -120,69 +126,80 @@ object SessionOutfitHandlers { } def HandleOutfitInviteAccept(invited: Player, session: SessionData): Unit = { - OutfitInviteManager.getOutfitInvite(invited.CharId) match { + val toName = invited.Name + val toCharId = invited.CharId + val toZone = invited.Zone + val toZoneId = toZone.id + val toGuid = invited.GUID + OutfitInviteManager.getOutfitInvite(toCharId) match { case Some(outfitInvite) => + val fromName = outfitInvite.sentFrom.Name + val fromCharId = outfitInvite.sentFrom.CharId + val fromZone = outfitInvite.sentFrom.Zone val outfitId = outfitInvite.sentFrom.outfit_id - (for { - _ <- addMemberToOutfit(outfitId, invited.CharId) + _ <- addMemberToOutfit(outfitId, toCharId) outfitOpt <- ctx.run(getOutfitById(outfitId)).map(_.headOption) memberCount <- ctx.run(getOutfitMemberCount(outfitId)) points <- ctx.run(getOutfitPoints(outfitId)).map(_.headOption.map(_.points).getOrElse(0L)) } yield (outfitOpt, memberCount, points)) .map { case (Some(outfit), memberCount, points) => - - PlayerControl.sendResponse(outfitInvite.sentFrom.Zone, outfitInvite.sentFrom.Name, - OutfitMembershipResponse( - OutfitMembershipResponse.PacketType.InviteAccepted, 0, 0, - invited.CharId, outfitInvite.sentFrom.CharId, invited.Name, outfit.name, flag = false)) - - PlayerControl.sendResponse(invited.Zone, invited.Name, - OutfitMembershipResponse( - OutfitMembershipResponse.PacketType.InviteAccepted, 0, 0, - invited.CharId, outfitInvite.sentFrom.CharId, invited.Name, outfit.name, flag = true)) - - PlayerControl.sendResponse(outfitInvite.sentFrom.Zone, outfitInvite.sentFrom.Name, - OutfitEvent(outfitId, UpdateMemberCount(memberCount))) - - PlayerControl.sendResponse(outfitInvite.sentFrom.Zone, outfitInvite.sentFrom.Name, - OutfitMemberEvent(outfitId, invited.CharId, - OutfitMemberEventAction.Update(invited.Name, 0, 0, 0, - OutfitMemberEventAction.PacketType.Padding, 0))) - + val outfitName = outfit.name val seconds: Long = outfit.created.atZone(ZoneId.systemDefault()).toInstant.toEpochMilli / 1000 - PlayerControl.sendResponse(invited.Zone, invited.Name, - OutfitEvent(outfitId, Initial(OutfitInfo( - outfit.name, points, points, memberCount, - OutfitRankNames("", "", "", "", "", "", "", ""), - outfit.motd.getOrElse(""), - 14, unk11 = true, 0, seconds, 0, 0, 0)))) - - PlayerControl.sendResponse(invited.Zone, invited.Name, - OutfitMemberUpdate(outfit.id, invited.CharId, 0, flag=true)) - - OutfitInviteManager.removeOutfitInvite(invited.CharId) - - session.chat.JoinChannel(OutfitChannel(outfit.id)) - invited.outfit_id = outfit.id - invited.outfit_name = outfit.name - - invited.Zone.AvatarEvents ! AvatarServiceMessage(invited.Zone.id, - AvatarAction.PlanetsideAttributeToAll(invited.GUID, 39, outfit.id)) - - invited.Zone.AvatarEvents ! AvatarServiceMessage(invited.Zone.id, - AvatarAction.PlanetsideStringAttribute(invited.GUID, 0, outfit.name)) - case (None, _, _) => - - PlayerControl.sendResponse(invited.Zone, invited.Name, - ChatMsg(ChatMessageType.UNK_227, "Failed to join outfit")) - } - .recover { case _ => - PlayerControl.sendResponse(invited.Zone, invited.Name, - ChatMsg(ChatMessageType.UNK_227, "Failed to join outfit")) + fromZone.AvatarEvents ! BundledEnvelope( + MessageEnvelope(fromName, SendResponse( + OutfitMembershipResponse( + OutfitMembershipResponse.PacketType.InviteAccepted, 0, 0, + toCharId, fromCharId, toName, outfitName, flag = false + ), + OutfitEvent(outfitId, UpdateMemberCount(memberCount)), + OutfitMemberEvent(outfitId, toCharId, + OutfitMemberEventAction.Update(toName, 0, 0, 0, + OutfitMemberEventAction.PacketType.Padding, 0 + ) + ) + )) + ) + toZone.AvatarEvents ! BundledEnvelope( + MessageEnvelope(toName, SendResponse( + OutfitMembershipResponse( + OutfitMembershipResponse.PacketType.InviteAccepted, 0, 0, + toCharId, fromCharId, toName, outfitName, flag = true + ), + OutfitEvent(outfitId, Initial(OutfitInfo( + outfitName, points, points, memberCount, + OutfitRankNames("", "", "", "", "", "", "", ""), + outfit.motd.getOrElse(""), + 14, unk11 = true, 0, seconds, 0, 0, 0 + ))), + OutfitMemberUpdate(outfitId, toCharId, 0, flag=true) + )), + MessageEnvelope( + toZoneId, + PlanetsideAttribute(toGuid, 39, outfitId) + ), + MessageEnvelope( + toZoneId, + toGuid, + AvatarAction.PlanetsideStringAttribute(0, outfitName) + ) + ) + OutfitInviteManager.removeOutfitInvite(toCharId) + session.chat.JoinChannel(OutfitChannel(outfitId)) + invited.outfit_id = outfitId + invited.outfit_name = outfitName + case (None, _, _) => () + PlayerControl.sendResponse(toZone, toName, + ChatMsg(ChatMessageType.UNK_227, "Failed to join outfit") + ) } - case None => + .recover { case _ => + PlayerControl.sendResponse(toZone, toName, + ChatMsg(ChatMessageType.UNK_227, "Failed to join outfit") + ) + } + case None => () } } @@ -206,72 +223,76 @@ object SessionOutfitHandlers { } def HandleOutfitKick(zones: Seq[Zone], kickedId: Long, kickedBy: Player, session: SessionData): Unit = { + val outfit_id = kickedBy.outfit_id // if same id, player has left the outfit by their own choice if (kickedId == kickedBy.CharId) { - // store outfit_id since it will be nulled soon - val outfit_id = kickedBy.outfit_id - removeMemberFromOutfit(outfit_id, kickedId).map { case (deleted, _) => if (deleted > 0) { - - PlayerControl.sendResponse(kickedBy.Zone, kickedBy.Name, - OutfitEvent(outfit_id, Leaving()) + kickedBy.Zone.AvatarEvents ! BundledEnvelope( + MessageEnvelope(kickedBy.Name, SendResponse(OutfitEvent(outfit_id, Leaving()))), + MessageEnvelope( + kickedBy.Zone.id, + PlanetsideAttribute(kickedBy.GUID, 39, 0) + ), + MessageEnvelope( + kickedBy.Zone.id, + kickedBy.GUID, + AvatarAction.PlanetsideStringAttribute(0, "") + ) ) - session.chat.LeaveChannel(OutfitChannel(outfit_id)) kickedBy.outfit_name = "" kickedBy.outfit_id = 0 - zones.filter(z => z.AllPlayers.nonEmpty).flatMap(_.AllPlayers) - .filter(p => p.outfit_id == outfit_id).foreach(outfitMember => - PlayerControl.sendResponse(outfitMember.Zone, outfitMember.Name, - OutfitMemberEvent(outfit_id, kickedId, OutfitMemberEventAction.Kicked())) - ) - - kickedBy.Zone.AvatarEvents ! AvatarServiceMessage(kickedBy.Zone.id, - AvatarAction.PlanetsideAttributeToAll(kickedBy.GUID, 39, 0)) - - kickedBy.Zone.AvatarEvents ! AvatarServiceMessage(kickedBy.Zone.id, - AvatarAction.PlanetsideStringAttribute(kickedBy.GUID, 0, "")) + zones + .filter(z => z.AllPlayers.nonEmpty) + .flatMap(_.AllPlayers) + .filter(p => p.outfit_id == outfit_id) + .foreach(outfitMember => + PlayerControl.sendResponse(outfitMember.Zone, outfitMember.Name, + OutfitMemberEvent(outfit_id, kickedId, OutfitMemberEventAction.Kicked()) + ) + ) } }.recover { case e => e.printStackTrace() } } else { - removeMemberFromOutfit(kickedBy.outfit_id, kickedId).map { + removeMemberFromOutfit(outfit_id, kickedId).map { case (deleted, _) => if (deleted > 0) { findPlayerByIdForOutfitAction(zones, kickedId, kickedBy).foreach { kicked => - - PlayerControl.sendResponse(kicked.Zone, kicked.Name, - OutfitEvent(kickedBy.outfit_id, Leaving()) + kicked.Zone.AvatarEvents ! BundledEnvelope( + MessageEnvelope(kicked.Name, SendResponse( + OutfitEvent(outfit_id, Leaving()), + OutfitMembershipResponse( + OutfitMembershipResponse.PacketType.YouGotKicked, 0, 1, + kickedBy.CharId, kicked.CharId, kickedBy.Name, kicked.Name, flag = false + ) + )), + MessageEnvelope(kicked.Zone.id, + PlanetsideAttribute(kicked.GUID, 39, 0) + ), + MessageEnvelope( + kicked.Zone.id, + kicked.GUID, + AvatarAction.PlanetsideStringAttribute(0, "") + ), + MessageEnvelope(kicked.Name, + AvatarAction.RemoveFromOutfitChat(outfit_id) + ), + MessageEnvelope(kicked.Name, SendResponse( + OutfitMemberEvent(outfit_id, kickedId, OutfitMemberEventAction.Kicked()) + )) ) - - PlayerControl.sendResponse(kicked.Zone, kicked.Name, - OutfitMembershipResponse(OutfitMembershipResponse.PacketType.YouGotKicked, 0, 1, - kickedBy.CharId, kicked.CharId, kickedBy.Name, kicked.Name, flag = false)) - - kicked.Zone.AvatarEvents ! AvatarServiceMessage(kicked.Zone.id, - AvatarAction.PlanetsideAttributeToAll(kicked.GUID, 39, 0)) - - kicked.Zone.AvatarEvents ! AvatarServiceMessage(kicked.Zone.id, - AvatarAction.PlanetsideStringAttribute(kicked.GUID, 0, "")) - - kicked.Zone.AvatarEvents ! AvatarServiceMessage( - kicked.Name, AvatarAction.RemoveFromOutfitChat(kickedBy.outfit_id)) - kicked.outfit_id = 0 kicked.outfit_name = "" - PlayerControl.sendResponse(kicked.Zone, kicked.Name, - OutfitMemberEvent(kickedBy.outfit_id, kickedId, OutfitMemberEventAction.Kicked())) } val avatarName: Future[Option[String]] = - ctx.run( - quote { query[Avatar].filter(_.id == lift(kickedId)).map(_.name) } - ).map(_.headOption) + ctx.run(quote { query[Avatar].filter(_.id == lift(kickedId)).map(_.name) }).map(_.headOption) avatarName.foreach { case Some(name) => PlayerControl.sendResponse(kickedBy.Zone, kickedBy.Name, @@ -280,10 +301,14 @@ object SessionOutfitHandlers { case None => PlayerControl.sendResponse(kickedBy.Zone, kickedBy.Name, OutfitMembershipResponse(OutfitMembershipResponse.PacketType.YouKicked, 0, 1, kickedBy.CharId, kickedId, "NameNotFound", "", flag = true)) } - zones.filter(z => z.AllPlayers.nonEmpty).flatMap(_.AllPlayers) - .filter(p => p.outfit_id == kickedBy.outfit_id).foreach(outfitMember => - PlayerControl.sendResponse(outfitMember.Zone, outfitMember.Name, - OutfitMemberEvent(kickedBy.outfit_id, kickedId, OutfitMemberEventAction.Kicked())) + zones + .filter(z => z.AllPlayers.nonEmpty) + .flatMap(_.AllPlayers) + .filter(p => p.outfit_id == kickedBy.outfit_id) + .foreach(outfitMember => + PlayerControl.sendResponse(outfitMember.Zone, outfitMember.Name, + OutfitMemberEvent(kickedBy.outfit_id, kickedId, OutfitMemberEventAction.Kicked()) + ) ) // this needs to be the kicked player // session.chat.LeaveChannel(OutfitChannel(kickedBy.outfit_id)) @@ -297,18 +322,13 @@ object SessionOutfitHandlers { } def HandleOutfitPromote(zones: Seq[Zone], promotedId: Long, newRank: Int, promoter: Player): Unit = { - val outfit_id = promoter.outfit_id - findPlayerByIdForOutfitAction(zones, promotedId, promoter).foreach { promoted => - if (newRank == 7) { - // demote owner to rank 6 // promote promoted to rank 7 // update outfit updateOutfitOwner(outfit_id, promoter.avatar.id, promoted.avatar.id) - // TODO: does every member get the notification like this? getOutfitMemberPoints(outfit_id, promoter.avatar.id).map { owner_points => @@ -324,7 +344,6 @@ object SessionOutfitHandlers { }) }) } - // update promoter rank PlayerControl.sendResponse( promoter.Zone, promoter.Name, @@ -338,17 +357,19 @@ object SessionOutfitHandlers { // TODO: does every member get the notification like this? getOutfitMemberPoints(outfit_id, promoted.avatar.id).map { member_points => - // tell everyone about the new rank of the promoted member - zones.foreach(zone => { - zone.AllPlayers + zones.foreach { zone => + // tell everyone about the new rank of the promoted member + val messages = zone.AllPlayers .filter(_.outfit_id == outfit_id) - .foreach(player => { - PlayerControl.sendResponse( - zone, player.Name, + .map { player => + MessageEnvelope(player.Name, SendResponse( OutfitMemberEvent(outfit_id, promoted.avatar.id, - OutfitMemberEventAction.Update(promoted.Name, newRank, member_points, 0, OutfitMemberEventAction.PacketType.Padding, 0))) - }) - }) + OutfitMemberEventAction.Update(promoted.Name, newRank, member_points, 0, OutfitMemberEventAction.PacketType.Padding, 0) + ) + )) + } + zone.AvatarEvents ! BundledEnvelope(messages) + } } // update promoted rank @@ -364,7 +385,6 @@ object SessionOutfitHandlers { memberCount <- ctx.run(query[Outfitmember].filter(_.outfit_id == lift(outfitId)).size) pointsTotal <- ctx.run(querySchema[OutfitpointMv]("outfitpoint_mv").filter(_.outfit_id == lift(outfitId))) } yield (outfitOpt, memberCount, pointsTotal.headOption.map(_.points).getOrElse(0L)) - val membersF = ctx.run(getOutfitMembersWithDetails(outfitId)) for { @@ -373,43 +393,42 @@ object SessionOutfitHandlers { } yield { outfitOpt.foreach { outfit => val seconds: Long = outfit.created.atZone(ZoneId.systemDefault()).toInstant.toEpochMilli / 1000 - - PlayerControl.sendResponse(player.Zone, player.Name, - OutfitEvent(outfit.id, Initial(OutfitInfo( - outfit.name, - totalPoints, - totalPoints, - memberCount, - OutfitRankNames( - outfit.rank0.getOrElse(""), - outfit.rank1.getOrElse(""), - outfit.rank2.getOrElse(""), - outfit.rank3.getOrElse(""), - outfit.rank4.getOrElse(""), - outfit.rank5.getOrElse(""), - outfit.rank6.getOrElse(""), - outfit.rank7.getOrElse(""), - ), - outfit.motd.getOrElse(""), - 14, unk11 = true, 0, seconds, 0, 0, 0)))) - - members.foreach { case (avatarId, avatarName, points, rank, login) => + val outfitEventInitial = OutfitEvent(outfit.id, Initial(OutfitInfo( + outfit.name, + totalPoints, + totalPoints, + memberCount, + OutfitRankNames( + outfit.rank0.getOrElse(""), + outfit.rank1.getOrElse(""), + outfit.rank2.getOrElse(""), + outfit.rank3.getOrElse(""), + outfit.rank4.getOrElse(""), + outfit.rank5.getOrElse(""), + outfit.rank6.getOrElse(""), + outfit.rank7.getOrElse(""), + ), + outfit.motd.getOrElse(""), + 14, unk11 = true, 0, seconds, 0, 0, 0)) + ) + val memberEventList = members.map { case (avatarId, avatarName, points, rank, login) => val lastLogin = findPlayerByIdForOutfitAction(zones, avatarId, player) match { case Some(_) => 0L case None if player.Name == avatarName => 0L case None => (System.currentTimeMillis() - login.atZone(ZoneId.systemDefault()).toInstant.toEpochMilli) / 1000 } - PlayerControl.sendResponse(player.Zone, player.Name, - OutfitMemberEvent(outfit.id, avatarId, - OutfitMemberEventAction.Update( - avatarName, - rank, - points, - lastLogin, - OutfitMemberEventAction.PacketType.Padding, 0))) + OutfitMemberEvent(outfit.id, avatarId, + OutfitMemberEventAction.Update( + avatarName, + rank, + points, + lastLogin, + OutfitMemberEventAction.PacketType.Padding, 0) + ) } - PlayerControl.sendResponse(player.Zone, player.Name, - OutfitEvent(outfit.id, Unk1())) + player.Zone.AvatarEvents ! MessageEnvelope(player.Name, SendResponse( + outfitEventInitial +: memberEventList :+ OutfitEvent(outfit.id, Unk1()) + )) } } } @@ -420,16 +439,13 @@ object SessionOutfitHandlers { futureResult.onComplete { case Success(rows) => - rows.foreach { case (outfitId, points, name, leaderName, memberCount) => - PlayerControl.sendResponse(player.Zone, player.Name, + player.Zone.AvatarEvents ! MessageEnvelope(player.Name, SendResponse( + rows.map { case (outfitId, points, name, leaderName, memberCount) => OutfitListEvent( - OutfitListEventAction.ListElementOutfit( - outfitId, - points, - memberCount, - name, - leaderName))) - } + OutfitListEventAction.ListElementOutfit(outfitId, points, memberCount, name, leaderName) + ) + } + )) case Failure(_) => PlayerControl.sendResponse(player.Zone, player.Name, @@ -439,9 +455,7 @@ object SessionOutfitHandlers { } def HandleOutfitMotd(zones: Seq[Zone], message: String, player: Player): Unit = { - val outfit_id = player.outfit_id - val outfitDetails = for { _ <- updateOutfitMotd(outfit_id, message) outfitOpt <- ctx.run(getOutfitById(outfit_id)).map(_.headOption) @@ -453,9 +467,8 @@ object SessionOutfitHandlers { (outfitOpt, memberCount, totalPoints) <- outfitDetails } yield { outfitOpt.foreach { outfit => - // send to all online players in outfit - val outfit_event = OutfitEvent( + val outfit_event = SendResponse(OutfitEvent( outfit_id, Update( OutfitInfo( @@ -483,19 +496,15 @@ object SessionOutfitHandlers { unk25 = 0 ) ) - ) + )) - zones.foreach(zone => { - zone.AllPlayers - .filter(_.outfit_id == outfit_id) - .filter(_.outfit_window_open) - .foreach(player => { - PlayerControl.sendResponse( - zone, player.Name, - outfit_event - ) - }) - }) + zones.foreach { zone => + zone.AvatarEvents ! BundledEnvelope( + zone.AllPlayers + .filter { p => p.outfit_id == outfit_id && p.outfit_window_open } + .map(p => MessageEnvelope(p.Name, outfit_event)) + ) + } } } @@ -504,9 +513,7 @@ object SessionOutfitHandlers { } def HandleOutfitRank(zones: Seq[Zone], list: List[Option[String]], player: Player): Unit = { - val outfit_id = player.outfit_id - val outfitDetails = for { _ <- updateOutfitRanks(outfit_id, list) outfitOpt <- ctx.run(getOutfitById(outfit_id)).map(_.headOption) @@ -518,9 +525,8 @@ object SessionOutfitHandlers { (outfitOpt, memberCount, totalPoints) <- outfitDetails } yield { outfitOpt.foreach { outfit => - // send to all online players in outfit with window open - val outfit_event = OutfitEvent( + val outfit_event = SendResponse(OutfitEvent( outfit_id, Update( OutfitInfo( @@ -548,19 +554,15 @@ object SessionOutfitHandlers { unk25 = 0 ) ) - ) + )) - zones.foreach(zone => { - zone.AllPlayers - .filter(_.outfit_id == outfit_id) - .filter(_.outfit_window_open) - .foreach(player => { - PlayerControl.sendResponse( - zone, player.Name, - outfit_event - ) - }) - }) + zones.foreach { zone => + zone.AvatarEvents ! BundledEnvelope( + zone.AllPlayers + .filter { p => p.outfit_id == outfit_id && p.outfit_window_open } + .map(p => MessageEnvelope(p.Name, outfit_event)) + ) + } } } } @@ -596,29 +598,34 @@ object SessionOutfitHandlers { .map { case (Some(outfit), memberCount, points) => val seconds: Long = outfit.created.atZone(ZoneId.systemDefault()).toInstant.toEpochMilli / 1000 - - PlayerControl.sendResponse(player.Zone, player.Name, - OutfitEvent(outfitId, Update(OutfitInfo( - outfit.name, points, points, memberCount, - OutfitRankNames(outfit.rank0.getOrElse(""), outfit.rank1.getOrElse(""), outfit.rank2.getOrElse(""), - outfit.rank3.getOrElse(""), outfit.rank4.getOrElse(""), outfit.rank5.getOrElse(""), - outfit.rank6.getOrElse(""), outfit.rank7.getOrElse("")), - outfit.motd.getOrElse(""), - 14, unk11 = true, 0, seconds, 0, 0, 0)))) - - PlayerControl.sendResponse(player.Zone, player.Name, - OutfitMemberUpdate(outfit.id, player.CharId, membership.rank, flag = true)) - + player.Zone.AvatarEvents ! BundledEnvelope( + MessageEnvelope(player.Name, SendResponse( + OutfitEvent(outfitId, Update(OutfitInfo( + outfit.name, points, points, memberCount, + OutfitRankNames( + outfit.rank0.getOrElse(""), outfit.rank1.getOrElse(""), outfit.rank2.getOrElse(""), + outfit.rank3.getOrElse(""), outfit.rank4.getOrElse(""), outfit.rank5.getOrElse(""), + outfit.rank6.getOrElse(""), outfit.rank7.getOrElse("") + ), + outfit.motd.getOrElse(""), + 14, unk11 = true, 0, seconds, 0, 0, 0 + ))), + OutfitMemberUpdate(outfit.id, player.CharId, membership.rank, flag = true) + )), + MessageEnvelope( + player.Zone.id, + PlanetsideAttribute(player.GUID, 39, outfit.id) + ), + MessageEnvelope( + player.Zone.id, + player.GUID, + AvatarAction.PlanetsideStringAttribute(0, outfit.name) + ) + ) session.chat.JoinChannel(OutfitChannel(outfit.id)) player.outfit_id = outfit.id player.outfit_name = outfit.name - player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.id, - AvatarAction.PlanetsideAttributeToAll(player.GUID, 39, outfit.id)) - - player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.id, - AvatarAction.PlanetsideStringAttribute(player.GUID, 0, outfit.name)) - case (None, _, _) => PlayerControl.sendResponse(player.Zone, player.Name, ChatMsg(ChatMessageType.UNK_227, "Failed to load outfit")) @@ -849,7 +856,7 @@ object SessionOutfitHandlers { query[Outfit] .filter(_.id == lift(outfit_id)) .update( - _.rank0 -> lift(colorized(0)), + _.rank0 -> lift(colorized.head), _.rank1 -> lift(colorized(1)), _.rank2 -> lift(colorized(2)), _.rank3 -> lift(colorized(3)), diff --git a/src/main/scala/net/psforever/actors/session/support/SessionSquadHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionSquadHandlers.scala index 8f5e0e5b2..fa9da51e7 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionSquadHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionSquadHandlers.scala @@ -2,6 +2,8 @@ package net.psforever.actors.session.support import akka.actor.{ActorContext, ActorRef, typed} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.PlanetsideAttribute import net.psforever.services.teamwork.SquadServiceResponse import scala.collection.mutable @@ -10,7 +12,6 @@ import net.psforever.actors.session.AvatarActor import net.psforever.objects.teamwork.Squad import net.psforever.objects.{Default, Player} import net.psforever.packet.game._ -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.services.teamwork.{SquadResponse, SquadServiceMessage, SquadAction => SquadServiceAction} import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3} @@ -105,9 +106,10 @@ class SessionSquadHandlers( squadUI.get(player.CharId) match { case Some(elem) => sendResponse(PlanetsideAttributeMessage(player.GUID, 31, squad_supplement_id)) - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( s"${player.Faction}", - AvatarAction.PlanetsideAttribute(player.GUID, 31, squad_supplement_id) + player.GUID, + PlanetsideAttribute(player.GUID, 31, squad_supplement_id) ) sendResponse(PlanetsideAttributeMessage(player.GUID, 32, elem.index)) case _ => @@ -286,7 +288,7 @@ class SessionSquadHandlers( * @param value value to associate the player */ def GiveSquadColorsForOthers(guid: PlanetSideGUID, factionChannel: String, value: Long): Unit = { - continent.AvatarEvents ! AvatarServiceMessage(factionChannel, AvatarAction.PlanetsideAttribute(guid, 31, value)) + continent.AvatarEvents ! MessageEnvelope(factionChannel, guid, PlanetsideAttribute(guid, 31, value)) } /** diff --git a/src/main/scala/net/psforever/actors/session/support/SessionVehicleHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionVehicleHandlers.scala index b493b1ff2..3b46b874f 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionVehicleHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionVehicleHandlers.scala @@ -5,13 +5,10 @@ import akka.actor.{ActorContext, ActorRef, typed} import net.psforever.actors.session.AvatarActor import net.psforever.objects.Vehicle import net.psforever.packet.game.ChatMsg -import net.psforever.services.vehicle.VehicleResponse import net.psforever.types.{ChatMessageType, DriveState, PlanetSideGUID} -trait VehicleHandlerFunctions extends CommonSessionInterfacingFunctionality { +trait VehicleHandlerFunctions extends CommonSessionInterfacingFunctionality with CommonHandlerFunctions { def ops: SessionVehicleHandlers - - def handle(toChannel: String, guid: PlanetSideGUID, reply: VehicleResponse.Response): Unit } class SessionVehicleHandlers( 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 790343fc8..93dc18e4d 100644 --- a/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/WeaponAndProjectileOperations.scala @@ -27,7 +27,9 @@ import net.psforever.objects.vital.interaction.DamageInteraction import net.psforever.objects.vital.projectile.ProjectileReason import net.psforever.objects.zones.exp.ToDatabase import net.psforever.packet.game.UplinkRequest -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.CachedEnvelope +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{ChangeAmmo, ChangeFireState_Start, ChangeFireState_Stop, ReloadTool, SendResponse, WeaponDryFire} import net.psforever.types.{ChatMessageType, PlanetSideEmpire, ValidPlanetSideGUID, Vector3} import net.psforever.util.Config @@ -45,8 +47,8 @@ import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.turret.FacilityTurret import net.psforever.objects._ import net.psforever.packet.game._ -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.vehicle.VehicleAction import net.psforever.types.{ExoSuitType, PlanetSideGUID} trait WeaponAndProjectileFunctions extends CommonSessionInterfacingFunctionality { @@ -281,14 +283,12 @@ class WeaponAndProjectileOperations( .orElse { continent.GUID(weapon_guid) } .collect { case _: Equipment if containerOpt.exists(_.isInstanceOf[Player]) => - continent.AvatarEvents ! AvatarServiceMessage( - continent.id, - AvatarAction.WeaponDryFire(player.GUID, weapon_guid) - ) + continent.AvatarEvents ! MessageEnvelope(continent.id, player.GUID, WeaponDryFire(weapon_guid)) case _: Equipment => - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.WeaponDryFire(player.GUID, weapon_guid) + player.GUID, + WeaponDryFire(weapon_guid) ) } .orElse { @@ -348,14 +348,21 @@ class WeaponAndProjectileOperations( sendResponse(UplinkResponse(code.value, 0)) sendResponse(PlanetsideAttributeMessage(player.GUID, 59, 1200000)) avatarActor ! AvatarActor.UpdateCUDTime("emp_blast") - player.Zone.LocalEvents ! LocalServiceMessage(s"${player.Zone.id}", - LocalAction.SendPacket(TriggerEffectMessage(ValidPlanetSideGUID(0), empColor, None, Some(TriggeredEffectLocation(player.Position, Vector3(0, 0, 90)))))) + player.Zone.LocalEvents ! MessageEnvelope( + s"${player.Zone.id}", + PlanetSideGUID(-1), + SendResponse(TriggerEffectMessage(Default.GUID0, empColor, None, Some(TriggeredEffectLocation(player.Position, Vector3(0, 0, 90))))) + ) context.system.scheduler.scheduleOnce(delay = 1 seconds) { Zone.serverSideDamage(player.Zone, player, empSize, SpecialEmp.createEmpInteraction(empSize, player.Position), ExplosiveDeployableControl.detectionForExplosiveSource(player), Zone.findAllTargets) } case UplinkRequestType.OrbitalStrike => - player.Zone.LocalEvents ! LocalServiceMessage(s"$playerFaction", LocalAction.SendPacket(OrbitalStrikeWaypointMessage(player.GUID, pos.get.x, pos.get.y))) + player.Zone.LocalEvents ! MessageEnvelope( + s"$playerFaction", + PlanetSideGUID(-1), + SendResponse(OrbitalStrikeWaypointMessage(player.GUID, pos.get.x, pos.get.y)) + ) sendResponse(UplinkResponse(code.value, 0)) orbitalStrikePos = pos case UplinkRequestType.Unknown5 => @@ -380,9 +387,16 @@ class WeaponAndProjectileOperations( sendResponse(PlanetsideAttributeMessage(player.GUID, 60, 10800000)) avatarActor ! AvatarActor.UpdateCUDTime("orbital_strike") context.system.scheduler.scheduleOnce(delay = 5 seconds) { - player.Zone.LocalEvents ! LocalServiceMessage(s"${player.Zone.id}", - LocalAction.SendPacket(TriggerEffectMessage(ValidPlanetSideGUID(0), strikeType, None, Some(TriggeredEffectLocation(orbitalStrikePos.get, Vector3(0, 0, 90)))))) - player.Zone.LocalEvents ! LocalServiceMessage(s"$playerFaction", LocalAction.SendPacket(OrbitalStrikeWaypointMessage(player.GUID, None))) + player.Zone.LocalEvents ! MessageEnvelope( + s"${player.Zone.id}", + PlanetSideGUID(-1), + SendResponse(TriggerEffectMessage(ValidPlanetSideGUID(0), strikeType, None, Some(TriggeredEffectLocation(orbitalStrikePos.get, Vector3(0, 0, 90))))) + ) + player.Zone.LocalEvents ! MessageEnvelope( + s"$playerFaction", + PlanetSideGUID(-1), + SendResponse(OrbitalStrikeWaypointMessage(player.GUID, None)) + ) context.system.scheduler.scheduleOnce(delay = 5 seconds) { val sectorTargets = Zone.findOrbitalStrikeTargets(player.Zone, orbitalStrikePos.get, osSize.DamageRadius, Zone.getOrbitbalStrikeTargets) val withinRange = sectorTargets.filter { target => Zone.orbitalStrikeDistanceCheck(orbitalStrikePos.get, target.Position, osSize.DamageRadius) } @@ -457,9 +471,10 @@ class WeaponAndProjectileOperations( log.info(s"${player.Name} changed ${player.Sex.possessive} ${obj.Definition.Name}'s fire mode to #$modeIndex") } sendResponse(ChangeFireModeMessage(item_guid, modeIndex)) - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( sessionLogic.zoning.zoneChannel, - AvatarAction.ChangeFireMode(player.GUID, item_guid, modeIndex) + player.GUID, + AvatarAction.ChangeFireMode(item_guid, modeIndex) ) } case Some(_) => @@ -478,10 +493,10 @@ class WeaponAndProjectileOperations( projectile.Position = shot_pos projectile.Orientation = shot_orient projectile.Velocity = shot_vel - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! CachedEnvelope( continent.id, + player.GUID, AvatarAction.ProjectileState( - player.GUID, projectileGlobalUID, shot_pos, shot_vel, @@ -742,12 +757,9 @@ class WeaponAndProjectileOperations( (target.GUID, (target, proxyCopy, proxyCopy.shot_origin, target.Position)) }.unzip //chain lash effect - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( continent.id, - AvatarAction.SendResponse( - PlanetSideGUID(0), - ChainLashMessage(hitPos, projectile.profile.ObjectId, guidRefs.toList) - ) + SendResponse(ChainLashMessage(hitPos, projectile.profile.ObjectId, guidRefs.toList)) ) //chain lash target output outputRefs.toList @@ -931,12 +943,9 @@ class WeaponAndProjectileOperations( tool.Magazine = 0 sendResponse(InventoryStateMessage(tool.AmmoSlot.Box.GUID, weapon_guid, 0)) sendResponse(ChangeFireStateMessage_Stop(weapon_guid)) - continent.AvatarEvents ! AvatarServiceMessage( - continent.id, - AvatarAction.ChangeFireState_Stop(player.GUID, weapon_guid) - ) + continent.AvatarEvents ! MessageEnvelope(continent.id, player.GUID, ChangeFireState_Stop(weapon_guid)) sendResponse(WeaponDryFireMessage(weapon_guid)) - continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.WeaponDryFire(player.GUID, weapon_guid)) + continent.AvatarEvents ! MessageEnvelope(continent.id, player.GUID, WeaponDryFire(weapon_guid)) } /** @@ -952,7 +961,7 @@ class WeaponAndProjectileOperations( .map { sessionLogic.validObject(_, decorator="FindDetectedProjectileTargets") } .flatMap { case Some(obj: Vehicle) if !obj.Cloaked => - //TODO hint: vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.ProjectileAutoLockAwareness(mode)) + //TODO hint: vehicleService ! MessageEnvelope(s"${obj.Actor}", VehicleAction.ProjectileAutoLockAwareness(mode)) obj.Seats.values.flatMap { seat => seat.occupants.map(_.Name) } case Some(obj: Mountable) => obj.Seats.values.flatMap { seat => seat.occupants.map(_.Name) } @@ -1077,9 +1086,10 @@ class WeaponAndProjectileOperations( } def fireStateStartPlayerMessages(itemGuid: PlanetSideGUID): Unit = { - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( sessionLogic.zoning.zoneChannel, - AvatarAction.ChangeFireState_Start(player.GUID, itemGuid) + player.GUID, + ChangeFireState_Start(itemGuid) ) } @@ -1088,9 +1098,10 @@ class WeaponAndProjectileOperations( case turret: FacilityTurret if continent.map.cavern => turret.Actor ! VanuSentry.ChangeFireStart } - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.ChangeFireState_Start(player.GUID, itemGuid) + player.GUID, + ChangeFireState_Start(itemGuid) ) } @@ -1116,9 +1127,10 @@ class WeaponAndProjectileOperations( } def fireStateStopPlayerMessages(itemGuid: PlanetSideGUID): Unit = { - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( sessionLogic.zoning.zoneChannel, - AvatarAction.ChangeFireState_Stop(player.GUID, itemGuid) + player.GUID, + ChangeFireState_Stop(itemGuid) ) } @@ -1127,9 +1139,10 @@ class WeaponAndProjectileOperations( case turret: FacilityTurret if continent.map.cavern => turret.Actor ! VanuSentry.ChangeFireStop } - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.ChangeFireState_Stop(player.GUID, itemGuid) + player.GUID, + ChangeFireState_Stop(itemGuid) ) } @@ -1186,16 +1199,14 @@ class WeaponAndProjectileOperations( used by ReloadMessage handling */ def reloadPlayerMessages(itemGuid: PlanetSideGUID): Unit = { - continent.AvatarEvents ! AvatarServiceMessage( - sessionLogic.zoning.zoneChannel, - AvatarAction.Reload(player.GUID, itemGuid) - ) + continent.AvatarEvents ! MessageEnvelope(sessionLogic.zoning.zoneChannel, player.GUID, ReloadTool(itemGuid)) } def reloadVehicleMessages(itemGuid: PlanetSideGUID): Unit = { - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.Reload(player.GUID, itemGuid) + player.GUID, + ReloadTool(itemGuid) ) } @@ -1369,10 +1380,10 @@ class WeaponAndProjectileOperations( val previous_box_guid = previousBox.GUID val boxDef = box.Definition sendResponse(ChangeAmmoMessage(tool_guid, box.Capacity)) - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( sessionLogic.zoning.zoneChannel, - AvatarAction.ChangeAmmo( - player.GUID, + player.GUID, + ChangeAmmo( tool_guid, ammoSlotIndex, previous_box_guid, @@ -1470,15 +1481,10 @@ class WeaponAndProjectileOperations( def modifyAmmunitionInMountable(obj: PlanetSideServerObject with Container)(box: AmmoBox, reloadValue: Int): Unit = { modifyAmmunition(obj)(box, reloadValue) obj.Find(box).collect { index => - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( s"${obj.Actor}", - VehicleAction.InventoryState( - player.GUID, - box, - obj.GUID, - index, - box.Definition.Packet.DetailedConstructorData(box).get - ) + player.GUID, + VehicleAction.InventoryState(box, obj.GUID, index, box.Definition.Packet.DetailedConstructorData(box).get) ) } } @@ -1582,10 +1588,7 @@ class WeaponAndProjectileOperations( shootingStop.clear() (prefire ++ shooting).foreach { guid => sendResponse(ChangeFireStateMessage_Stop(guid)) - continent.AvatarEvents ! AvatarServiceMessage( - continent.id, - AvatarAction.ChangeFireState_Stop(player.GUID, guid) - ) + continent.AvatarEvents ! MessageEnvelope(continent.id, player.GUID, ChangeFireState_Stop(guid)) } prefire.clear() shooting.clear() 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 a69a4a724..6ffe99ebb 100644 --- a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala @@ -18,10 +18,14 @@ import net.psforever.objects.serverobject.mount.Seat import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.objects.serverobject.turret.auto.AutomatedTurret import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource} +import net.psforever.objects.vehicles.control.{CargoBehavior, CarrierBehavior} import net.psforever.objects.vital.{InGameHistory, IncarnationActivity, ReconstructionActivity, SpawningActivity} 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.packet.game.{CampaignStatistic, ChangeFireStateMessage_Start, CloudInfo, GenericActionMessage, GenericObjectActionEnum, HackState7, MailMessage, ObjectDetectedMessage, SessionStatistic, StormInfo, TrainingZoneMessage, TriggeredSound, WeatherMessage} +import net.psforever.services.avatar.support.{CorpseEnvelope, ReleaseEnvelope} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.{GenericObjectAction, ObjectDelete, PlanetsideAttribute, SendResponse} import net.psforever.services.chat.DefaultChannel import scala.concurrent.duration._ @@ -62,16 +66,16 @@ import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectCreateMess import net.psforever.packet.game.objectcreate.ObjectClass import net.psforever.packet.{PlanetSideGamePacket, game} import net.psforever.persistence.Savedplayer -import net.psforever.services.RemoverActor import net.psforever.services.ServiceManager.{Lookup, LookupResult} import net.psforever.services.account.{AccountPersistenceService, PlayerToken} -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.support.RemoverActor +import net.psforever.services.galaxy.GalaxyAction import net.psforever.services.hart.HartTimer import net.psforever.services.local.support.HackCaptureActor -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.local.LocalAction import net.psforever.services.properties.PropertyOverrideManager -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.vehicle.VehicleAction import net.psforever.services.{CavernRotationService, Service, ServiceManager, InterstellarClusterService => ICS} import net.psforever.types._ import net.psforever.util.{Config, DefinitionUtil} @@ -118,7 +122,7 @@ object ZoningOperations { additionalChannels: List[String] ): Unit = { val events = zone.LocalEvents - val effectMessage = LocalAction.TriggerEffectLocation(Service.defaultPlayerGUID, s"respawn_$faction", position, orientation) + val effectMessage = LocalAction.TriggerEffectLocation(s"respawn_$faction", position, orientation) (zone .blockMap .sector(position, range = 100f) @@ -126,7 +130,7 @@ object ZoningOperations { .filter(p => Sidedness.equals(side, p.WhichSide)) .map(_.Name) ++ additionalChannels) .foreach { target => - events ! LocalServiceMessage(target, effectMessage) + events ! MessageEnvelope(target, effectMessage) } } @@ -152,13 +156,13 @@ object ZoningOperations { Vector3.DistanceSquared(t.Position.xy, posxy) < 2500f && /* literal 50m */ heightDiff < 5f && heightDiff > -1f } - val effectMessage = LocalAction.TriggerEffectLocation(Service.defaultPlayerGUID, s"respawn_$faction", position, orientation) + val effectMessage = LocalAction.TriggerEffectLocation(s"respawn_$faction", position, orientation) (effectTargets.map(_.Name) ++ additionalChannels).foreach { target => - events ! LocalServiceMessage(target, effectMessage) + events ! MessageEnvelope(target, effectMessage) } - val soundMessage = LocalAction.TriggerSound(Service.defaultPlayerGUID, TriggeredSound.SpawnInTube, position, 50, 0.69803923f) + val soundMessage = LocalAction.TriggerSound(TriggeredSound.SpawnInTube, position, 50, 0.69803923f) (soundTargets.map(_.Name) ++ additionalChannels).foreach { target => - events ! LocalServiceMessage(target, soundMessage) + events ! MessageEnvelope(target, soundMessage) } } @@ -547,8 +551,8 @@ class ZoningOperations( sendResponse(OCM.apply(projectile)) } //spawn point update request - continent.VehicleEvents ! VehicleServiceMessage( - continent.id, + continent.VehicleEvents ! MessageEnvelope( + player.Name, VehicleAction.UpdateAmsSpawnPoint(continent) ) spawn.upstreamMessageCount = 0 @@ -595,14 +599,14 @@ class ZoningOperations( context.self ) LivePlayerList.Add(avatar.id, avatar) - galaxyService.tell(GalaxyServiceMessage(GalaxyAction.LogStatusChange(avatar.name)), context.parent) + galaxyService.tell(MessageEnvelope("", GalaxyAction.LogStatusChange(avatar.name)), context.parent) //PropertyOverrideMessage ServiceManager.serviceManager ! Lookup("propertyOverrideManager") sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 0)) // disable festive backpacks sendResponse(ReplicationStreamMessage(5, Some(6), Vector.empty)) //clear squad list spawn.initializeFriendsAndIgnoredLists() //the following subscriptions last until character switch/logout - galaxyService ! Service.Join("galaxy") //for galaxy-wide messages + galaxyService ! Service.Join("") //for galaxy-wide messages galaxyService ! Service.Join(s"${avatar.faction}") //for hotspots, etc. sessionLogic.squadService ! Service.Join(s"${avatar.faction}") //channel will be player.Faction sessionLogic.squadService ! Service.Join(s"${avatar.id}") //channel will be player.CharId (in order to work with packets) @@ -618,9 +622,9 @@ class ZoningOperations( //the only zone-level event system subscription necessary before BeginZoningMessage (for persistence purposes) zone.AvatarEvents ! Service.Join(player.Name) sessionLogic.persist() - oldZone.AvatarEvents ! Service.Leave() - oldZone.LocalEvents ! Service.Leave() - oldZone.VehicleEvents ! Service.Leave() + oldZone.AvatarEvents ! Service.LeaveAll + oldZone.LocalEvents ! Service.LeaveAll + oldZone.VehicleEvents ! Service.LeaveAll if (player.isAlive && zoningType != Zoning.Method.Reset) { if (player.HasGUID) { @@ -649,9 +653,9 @@ class ZoningOperations( val oldZone = session.zone session = session.copy(zone = foundZone) sessionLogic.persist() - oldZone.AvatarEvents ! Service.Leave() - oldZone.LocalEvents ! Service.Leave() - oldZone.VehicleEvents ! Service.Leave() + oldZone.AvatarEvents ! Service.LeaveAll + oldZone.LocalEvents ! Service.LeaveAll + oldZone.VehicleEvents ! Service.LeaveAll //the only zone-level event system subscription necessary before BeginZoningMessage (for persistence purposes) foundZone.AvatarEvents ! Service.Join(player.Name) foundZone.Population ! Zone.Population.Join(avatar) @@ -760,7 +764,7 @@ class ZoningOperations( case _ => interstellarFerry match { case None => - galaxyService ! Service.Leave(Some(temp_channel)) //no longer being transferred between zones + galaxyService ! Service.Leave(temp_channel) //no longer being transferred between zones interstellarFerryTopLevelGUID = None case Some(_) => () //wait patiently } @@ -768,7 +772,7 @@ class ZoningOperations( } private def handleTransferPassengerVehicle(vehicle: Vehicle, temporaryChannel: String): Unit = { - galaxyService ! Service.Leave(Some(temporaryChannel)) //temporary vehicle-specific channel (see above) + galaxyService ! Service.Leave(temporaryChannel) //temporary vehicle-specific channel (see above) spawn.deadState = DeadState.Release sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, player.Faction, unk5=true)) interstellarFerry = Some(vehicle) //on the other continent and registered to that continent's GUID system @@ -851,7 +855,7 @@ class ZoningOperations( case None => spawn.deadState = DeadState.Release // cancel movement updates player.Position = position - // continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player.GUID, player.GUID)) + // continent.AvatarEvents ! MessageEnvelope(continent.Id, ObjectDelete(player.GUID, player.GUID)) spawn.LoadZonePhysicalSpawnPoint(zoneId, position, Vector3.Zero, 0 seconds, None) case _ => // seated in something that is not a vehicle or the vehicle is cargo, in which case we can't move } @@ -1121,7 +1125,7 @@ class ZoningOperations( case obj if obj.Destroyed => configAmenityAsDestroyed(obj) case obj => configAmenityAsWorking(obj) } - //sendResponse(HackMessage(HackState1.Unk3, guid, Service.defaultPlayerGUID, progress=0, -1f, HackState.HackCleared, HackState7.Unk8)) + //sendResponse(HackMessage(HackState1.Unk3, guid, Default.GUID0, progress=0, -1f, HackState.HackCleared, HackState7.Unk8)) } } @@ -1205,9 +1209,10 @@ class ZoningOperations( sendResponse(OCM.apply(llu)) // Attach it to a player if it has a carrier if (llu.Carrier.nonEmpty) { - continent.LocalEvents ! LocalServiceMessage( + continent.LocalEvents ! MessageEnvelope( continent.id, - LocalAction.SendPacket(ObjectAttachMessage(llu.Carrier.get.GUID, llu.GUID, 252)) + PlanetSideGUID(-1), + SendResponse(ObjectAttachMessage(llu.Carrier.get.GUID, llu.GUID, 252)) ) } case _ => () @@ -1349,9 +1354,10 @@ class ZoningOperations( val pguid = player.GUID val toChannel = manifest.file val topLevel = interstellarFerryTopLevelGUID.getOrElse(vehicle.GUID) - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( s"${vehicle.Actor}", - VehicleAction.TransferPassengerChannel(pguid, s"${vehicle.Actor}", toChannel, vehicle, topLevel) + pguid, + VehicleAction.TransferPassengerChannel(s"${vehicle.Actor}", toChannel, vehicle, topLevel) ) manifest.cargo.foreach { case ManifestPassengerEntry("MISSING_DRIVER", index) => @@ -1362,9 +1368,10 @@ class ZoningOperations( cargo.Actor ! CargoBehavior.StartCargoDismounting(bailed = false) case entry => val cargo = vehicle.CargoHolds(entry.mount).occupant.get - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( entry.name, - VehicleAction.TransferPassengerChannel(pguid, s"${cargo.Actor}", toChannel, cargo, topLevel) + pguid, + VehicleAction.TransferPassengerChannel(s"${cargo.Actor}", toChannel, cargo, topLevel) ) } // @@ -1393,9 +1400,10 @@ class ZoningOperations( interstellarFerryTopLevelGUID = if (manifest.passengers.isEmpty && manifest.cargo.count { !_.name.equals("MISSING_DRIVER") } == 0) { //do not delete if vehicle has passengers or cargo - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( continent.id, - VehicleAction.UnloadVehicle(pguid, vehicle, topLevel) + pguid, + VehicleAction.UnloadVehicle(vehicle, topLevel) ) None } else { @@ -1470,17 +1478,14 @@ class ZoningOperations( case Some(manifest) => val toChannel = manifest.file val topLevel = interstellarFerryTopLevelGUID.getOrElse(vehicle.GUID) - galaxyService ! GalaxyServiceMessage( - toChannel, - GalaxyAction.TransferPassenger(player_guid, toChannel, vehicle, topLevel, manifest) - ) + galaxyService ! MessageEnvelope(toChannel, GalaxyAction.TransferPassenger(player_guid, toChannel, vehicle, topLevel, manifest)) vehicle.CargoHolds.values .collect { case hold if hold.isOccupied => val cargo = hold.occupant.get cargo.Continent = toZoneId //point to the cargo vehicle to instigate cargo vehicle driver transportation - // galaxyService ! GalaxyServiceMessage( + // galaxyService ! MesdsageEnvelope( // toChannel, // GalaxyAction.TransferPassenger(player_guid, toChannel, vehicle, topLevel, manifest) // ) @@ -1972,7 +1977,7 @@ class ZoningOperations( guid, Deployable.Icon(obj.Definition.Item), obj.Position, - obj.OwnerGuid.getOrElse(Service.defaultPlayerGUID) + obj.OwnerGuid.getOrElse(Default.GUID0) ))) } } @@ -2191,7 +2196,7 @@ class ZoningOperations( log.info(s"RestoreInfo: player $name is already logged in zone ${inZone.id}; rejoining that character") sessionLogic.persistFunc = UpdatePersistence(from) //tell the old WorldSessionActor to kill itself by using its own subscriptions against itself - inZone.AvatarEvents ! AvatarServiceMessage(name, AvatarAction.TeardownConnection()) + inZone.AvatarEvents ! MessageEnvelope(name, AvatarAction.TeardownConnection) spawn.switchAvatarStatisticsFieldToRefreshAfterRespawn() //find and reload previous player ( @@ -2594,9 +2599,10 @@ class ZoningOperations( vehicle.CargoHolds.values .collect { case hold if hold.isOccupied => hold.occupant.get } .foreach { _.MountedIn = vguid } - events ! VehicleServiceMessage( + events ! MessageEnvelope( zoneid, - VehicleAction.LoadVehicle(player.GUID, vehicle, vObjectId, vguid, data) + player.GUID, + VehicleAction.LoadVehicle(vehicle, vObjectId, vguid, data) ) carrierInfo match { case (Some(carrier), Some((index, _))) => @@ -2621,9 +2627,10 @@ class ZoningOperations( //do not dispatch delete action if any hierarchical occupant has not gotten this far through the summoning process val vehicleToDelete = interstellarFerryTopLevelGUID.orElse(originalSeated).getOrElse(PlanetSideGUID(0)) val zone = vehicle.PreviousGatingManifest().get.origin - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.UnloadVehicle(player.GUID, vehicle, vehicleToDelete) + player.GUID, + VehicleAction.UnloadVehicle(vehicle, vehicleToDelete) ) log.debug( s"AvatarCreate: cleaning up ghost of transitioning vehicle ${vehicle.Definition.Name}@${vehicleToDelete.guid} in zone ${zone.id}" @@ -2638,9 +2645,10 @@ class ZoningOperations( val definition = player.avatar.definition val guid = player.GUID sendResponse(OCM.detailed(player)) - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( s"spectator", - AvatarAction.LoadPlayer(guid, definition.ObjectId, guid, definition.Packet.ConstructorData(player).get, None) + guid, + AvatarAction.LoadPlayer(definition.ObjectId, guid, definition.Packet.ConstructorData(player).get, None) ) case _ => @@ -2649,9 +2657,10 @@ class ZoningOperations( val guid = player.GUID usingSpawnTubeAnimation() sendResponse(OCM.detailed(player)) - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( zoneid, - AvatarAction.LoadPlayer(guid, definition.ObjectId, guid, definition.Packet.ConstructorData(player).get, None) + guid, + AvatarAction.LoadPlayer(definition.ObjectId, guid, definition.Packet.ConstructorData(player).get, None) ) } continent.Population ! Zone.Population.Spawn(avatar, player, avatarActor) @@ -2721,10 +2730,10 @@ class ZoningOperations( interimUngunnedVehicle = Some(vguid) interimUngunnedVehicleSeat = Some(seat) } - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( continent.id, + pguid, AvatarAction.LoadPlayer( - pguid, pdef.ObjectId, pguid, pdef.Packet.ConstructorData(tplayer).get, @@ -2880,9 +2889,9 @@ class ZoningOperations( case _ => () } if (player.VisibleSlots.contains(index)) { - events ! AvatarServiceMessage( + events ! MessageEnvelope( zoneId, - AvatarAction.ObjectDelete(Service.defaultPlayerGUID, obj.GUID) + ObjectDelete(obj.GUID) ) } else { sendResponse(ObjectDeleteMessage(obj.GUID, 0)) @@ -2898,7 +2907,6 @@ class ZoningOperations( * To the game, that is a backpack (or some pastry, festive graphical modification allowing). * @see `AvatarAction.ObjectDelete` * @see `AvatarAction.Release` - * @see `AvatarServiceMessage` * @see `FriskDeadBody` * @see `GUIDTask.unregisterPlayer` * @see `ObjectDeleteMessage` @@ -2916,7 +2924,7 @@ class ZoningOperations( val pguid = tplayer.GUID zone.Population ! Zone.Population.Release(avatar) sendResponse(ObjectDeleteMessage(pguid, 0)) - zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.ObjectDelete(pguid, pguid)) + zone.AvatarEvents ! MessageEnvelope(zone.id, pguid, ObjectDelete(pguid)) TaskWorkflow.execute(GUIDTask.unregisterPlayer(zone.GUID, tplayer)) } } @@ -2926,7 +2934,6 @@ class ZoningOperations( * To the game, that is a backpack (or some pastry, festive graphical modification allowing). * A player who has been kicked may not turn into a corpse. * @see `AvatarAction.Release` - * @see `AvatarServiceMessage` * @see `CorpseConverter.converter` * @see `DepictPlayerAsCorpse` * @see `Player.Release` @@ -2939,7 +2946,7 @@ class ZoningOperations( tplayer.Release DepictPlayerAsCorpse(tplayer) zone.Population ! Zone.Corpse.Add(tplayer) - zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.Release(tplayer, zone)) + zone.AvatarEvents ! ReleaseEnvelope(zone.id, AvatarAction.Release(tplayer, zone)) } /** @@ -2978,7 +2985,7 @@ class ZoningOperations( */ def TryDisposeOfLootedCorpse(obj: Player): Boolean = { if (obj.isBackpack && WellLootedDeadBody(obj)) { - obj.Zone.AvatarEvents ! AvatarServiceMessage.Corpse(RemoverActor.HurrySpecific(List(obj), obj.Zone)) + obj.Zone.AvatarEvents ! CorpseEnvelope(RemoverActor.HurrySpecific(List(obj), obj.Zone)) true } else { false @@ -3172,9 +3179,10 @@ class ZoningOperations( // entering or exiting VR zones uses a fade-out effect for the player instead of the usual green cloud deconstruction effect val effect = if (player.IsInVRZone || zoneId.startsWith("tz")) 2 else 1 sendResponse(ObjectDeleteMessage(player_guid, unk1=effect)) - continent.AvatarEvents ! AvatarServiceMessage( + continent.AvatarEvents ! MessageEnvelope( continent.id, - AvatarAction.ObjectDelete(player_guid, player_guid, unk=effect) + player_guid, + ObjectDelete(player_guid, unk=effect) ) InGameHistory.SpawnReconstructionActivity(player, toZoneNumber, betterSpawnPoint) LoadZoneAsPlayerUsing(player, pos, ori, toSide, zoneId) @@ -3338,7 +3346,7 @@ class ZoningOperations( //looking for squad (members) if (tplayer.avatar.lookingForSquad) { sendResponse(PlanetsideAttributeMessage(guid, 53, 1)) - continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.PlanetsideAttribute(guid, 53, 1)) + continent.AvatarEvents ! MessageEnvelope(continent.id, guid, PlanetsideAttribute(guid, 53, 1)) } sendResponse(AvatarSearchCriteriaMessage(guid, List(0, 0, 0, 0, 0, 0))) //these are facilities and towers and bunkers in the zone, but not necessarily all of them for some reason @@ -3364,7 +3372,7 @@ class ZoningOperations( if (tplayer.ExoSuit == ExoSuitType.MAX) { sendResponse(PlanetsideAttributeMessage(guid, 7, tplayer.Capacitor.toLong)) sendResponse(PlanetsideAttributeMessage(guid, 4, tplayer.Armor)) - continent.AvatarEvents ! AvatarServiceMessage(continent.id, AvatarAction.PlanetsideAttributeToAll(guid, 4, tplayer.Armor)) + continent.AvatarEvents ! MessageEnvelope(continent.id, PlanetsideAttribute(guid, 4, tplayer.Armor)) } // for issue #1269 continent.AllPlayers.filter(_.ExoSuit == ExoSuitType.MAX).foreach(max => sendResponse(PlanetsideAttributeMessage(max.GUID, 4, max.Armor))) @@ -3388,9 +3396,10 @@ class ZoningOperations( continent.GUID(tplayer.avatar.vehicle) match { case Some(vehicle: Vehicle) if vehicle.OwnerName.contains(tplayer.Name) => vehicle.OwnerGuid = guid - continent.VehicleEvents ! VehicleServiceMessage( + continent.VehicleEvents ! MessageEnvelope( s"${tplayer.Faction}", - VehicleAction.Ownership(guid, vehicle.GUID) + guid, + VehicleAction.Ownership(vehicle.GUID) ) case _ => avatarActor ! AvatarActor.SetVehicle(None) @@ -3487,13 +3496,13 @@ class ZoningOperations( case Some(b: Building) if b.hasCavernLockBenefit => tplayer.MaxHealth = 120 tplayer.Health = 120 - tplayer.Zone.AvatarEvents ! AvatarServiceMessage( - tplayer.Zone.id, - AvatarAction.PlanetsideAttributeToAll(tplayer.GUID, 0, 120) - ) - tplayer.Zone.AvatarEvents ! AvatarServiceMessage( - tplayer.Zone.id, - AvatarAction.PlanetsideAttributeToAll(tplayer.GUID, 1, 120) + val guid = tplayer.GUID + val zone = tplayer.Zone + val channel = zone.id + val events = zone.AvatarEvents + events ! MessageEnvelope( + channel, + SendResponse(PlanetsideAttributeMessage(guid, 0, 120), PlanetsideAttributeMessage(guid, 1, 120)) ) case _ => () } @@ -3996,13 +4005,20 @@ class ZoningOperations( } GoToDeploymentMap() val pZone = player.Zone + val msg = GenericObjectAction(player.GUID, GenericObjectActionEnum.PlayerDeconstructs.id) sendResponse(GenericActionMessage(FirstPersonViewWithEffect)) - pZone.blockMap.sector(player).livePlayerList.collect { case t if t.GUID != player.GUID => - pZone.LocalEvents ! LocalServiceMessage(t.Name, LocalAction.SendGenericObjectActionMessage(t.GUID, player.GUID, GenericObjectActionEnum.PlayerDeconstructs)) - } - pZone.AllPlayers.collect { case t if t.GUID != player.GUID && !t.allowInteraction => - pZone.LocalEvents ! LocalServiceMessage(t.Name, LocalAction.SendGenericObjectActionMessage(t.GUID, player.GUID, GenericObjectActionEnum.PlayerDeconstructs)) - } + val (localMessageRecipients, localMesages) = pZone.blockMap.sector(player).livePlayerList + .collect { + case t if t.GUID != player.GUID => + (t.Name, MessageEnvelope(t.Name, msg)) + } + .unzip + val otherMessages: Seq[MessageEnvelope] = pZone.AllPlayers + .collect { + case t if t.GUID != player.GUID && !t.allowInteraction && !localMessageRecipients.contains(t.Name) => + MessageEnvelope(t.Name, msg) + } + pZone.LocalEvents ! BundledEnvelope(localMesages ++ otherMessages) } def stopDeconstructing(): Unit = { diff --git a/src/main/scala/net/psforever/actors/zone/BuildingActor.scala b/src/main/scala/net/psforever/actors/zone/BuildingActor.scala index 0f385dae4..3aabbf12e 100644 --- a/src/main/scala/net/psforever/actors/zone/BuildingActor.scala +++ b/src/main/scala/net/psforever/actors/zone/BuildingActor.scala @@ -11,8 +11,9 @@ import net.psforever.objects.zones.Zone import net.psforever.packet.PlanetSideGamePacket import net.psforever.packet.game.ContinentalLockUpdateMessage import net.psforever.persistence -import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.{SendResponse, SetEmpire} +import net.psforever.services.galaxy.GalaxyAction import net.psforever.services.{InterstellarClusterService, ServiceManager} import net.psforever.types.PlanetSideEmpire import net.psforever.util.Database.ctx @@ -167,7 +168,7 @@ object BuildingActor { val building = details.building val zone = building.Zone building.Faction = faction - zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SetEmpire(building.GUID, faction)) + zone.LocalEvents ! MessageEnvelope(zone.id, SetEmpire(building.GUID, faction)) } } @@ -231,8 +232,10 @@ class BuildingActor( Behaviors.same case MapUpdate() => - details.galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(details.building.infoUpdateMessage())) - details.galaxyService ! GalaxyServiceMessage(GalaxyAction.SendResponse(details.building.densityLevelUpdateMessage(building))) + details.galaxyService ! BundledEnvelope( + MessageEnvelope("", GalaxyAction.MapUpdate(details.building.infoUpdateMessage())), + MessageEnvelope("", SendResponse(details.building.densityLevelUpdateMessage(building))) + ) Behaviors.same case AmenityStateChange(amenity, data) => @@ -254,15 +257,15 @@ class BuildingActor( logic.ntu(details, msg) case DensityLevelUpdate(building) => - details.galaxyService ! GalaxyServiceMessage(GalaxyAction.SendResponse(details.building.densityLevelUpdateMessage(building))) + details.galaxyService ! MessageEnvelope("", SendResponse(details.building.densityLevelUpdateMessage(building))) Behaviors.same case ContinentalLock(zone) => - details.galaxyService ! GalaxyServiceMessage(GalaxyAction.SendResponse(ContinentalLockUpdateMessage(zone.Number, zone.lockedBy))) + details.galaxyService ! MessageEnvelope("", SendResponse(ContinentalLockUpdateMessage(zone.Number, zone.lockedBy))) Behaviors.same case HomeLockBenefits(msg) => - details.galaxyService ! GalaxyServiceMessage(GalaxyAction.SendResponse(msg)) + details.galaxyService ! MessageEnvelope("", SendResponse(msg)) Behaviors.same } } diff --git a/src/main/scala/net/psforever/actors/zone/ShootingRangeTargetSpawnerActor.scala b/src/main/scala/net/psforever/actors/zone/ShootingRangeTargetSpawnerActor.scala index 1325caf09..214d7b5be 100644 --- a/src/main/scala/net/psforever/actors/zone/ShootingRangeTargetSpawnerActor.scala +++ b/src/main/scala/net/psforever/actors/zone/ShootingRangeTargetSpawnerActor.scala @@ -6,9 +6,10 @@ import net.psforever.objects.{Default, GlobalDefinitions, Tool, Vehicle} import net.psforever.objects.avatar.{AvatarBot, AvatarBotActor} import net.psforever.objects.guid.{GUIDTask, StraightforwardTask, TaskBundle, TaskWorkflow} import net.psforever.objects.zones.Zone -import net.psforever.services.local.{LocalAction, LocalServiceMessage} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} -import net.psforever.types.{CharacterSex, CharacterVoice, ExoSuitType, PlanetSideEmpire, PlanetSideGUID, Vector3} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.local.LocalAction +import net.psforever.services.vehicle.VehicleAction +import net.psforever.types.{CharacterSex, CharacterVoice, ExoSuitType, PlanetSideEmpire, Vector3} import net.psforever.util.Config import scala.collection.mutable.ListBuffer @@ -282,7 +283,6 @@ class ShootingRangeTargetSpawnerActor(zone: Zone) extends Actor { * @param bot the bot to remove */ private def RemoveBot(bot: AvatarBot): Boolean = { - import net.psforever.services.Service activeInfantryTargets.indexOf(bot) match { case -1 => log.warn(s"Failed to remove bot with GUID ${bot.GUID} from ${zone.id}'s active targets list! This shouldn't happen... and probably just caused a leak.") @@ -290,9 +290,9 @@ class ShootingRangeTargetSpawnerActor(zone: Zone) extends Actor { case index => activeInfantryTargets.remove(index) } - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.TriggerEffectLocation(Service.defaultPlayerGUID, "bot_destroyed_effect", bot.Position, bot.Orientation) + LocalAction.TriggerEffectLocation("bot_destroyed_effect", bot.Position, bot.Orientation) ) //spawn a replacement bot context.system.scheduler.scheduleOnce( @@ -356,10 +356,9 @@ class ShootingRangeTargetSpawnerActor(zone: Zone) extends Actor { def action(): Future[Any] = { zone.Transport ! Zone.Vehicle.Spawn(localVehicle) - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, VehicleAction.LoadVehicle( - PlanetSideGUID(0), localVehicle, localVehicle.Definition.ObjectId, localVehicle.GUID, diff --git a/src/main/scala/net/psforever/actors/zone/ZoneActor.scala b/src/main/scala/net/psforever/actors/zone/ZoneActor.scala index 391b0a7c8..f781db5c5 100644 --- a/src/main/scala/net/psforever/actors/zone/ZoneActor.scala +++ b/src/main/scala/net/psforever/actors/zone/ZoneActor.scala @@ -20,7 +20,8 @@ import net.psforever.objects.zones.exp.{ExperienceCalculator, SupportExperienceC import net.psforever.packet.game.{BuildingInfoUpdateMessage, PlanetsideAttributeMessage} import net.psforever.util.Database._ import net.psforever.persistence -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse import scala.collection.mutable import scala.util.{Failure, Success} @@ -234,26 +235,26 @@ class ZoneActor( } buildingOpt.foreach { building => if (msg.generator_state == PlanetSideGeneratorState.Normal && building.hasCavernLockBenefit) { - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.SendResponse(PlanetsideAttributeMessage(building.GUID, 67, 1)) + SendResponse(PlanetsideAttributeMessage(building.GUID, 67, 1)) ) } msg.is_hacked match { case true if building.BuildingType == StructureType.Facility && !zone.map.cavern => - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.SendResponse(PlanetsideAttributeMessage(building.GUID, 67, 0)) + SendResponse(PlanetsideAttributeMessage(building.GUID, 67, 0)) ) case false if building.hasCavernLockBenefit => - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.SendResponse(PlanetsideAttributeMessage(building.GUID, 67, 1)) + SendResponse(PlanetsideAttributeMessage(building.GUID, 67, 1)) ) case false if building.BuildingType == StructureType.Facility && !zone.map.cavern && !building.hasCavernLockBenefit => - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.SendResponse(PlanetsideAttributeMessage(building.GUID, 67, 0)) + SendResponse(PlanetsideAttributeMessage(building.GUID, 67, 0)) ) case _ => } 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 ebfb38a80..b09d15dea 100644 --- a/src/main/scala/net/psforever/actors/zone/building/CavernFacilityLogic.scala +++ b/src/main/scala/net/psforever/actors/zone/building/CavernFacilityLogic.scala @@ -7,9 +7,10 @@ import net.psforever.actors.commands.NtuCommand import net.psforever.actors.zone.{BuildingActor, BuildingControlDetails, ZoneActor} 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.{LocalAction, LocalServiceMessage} -import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.galaxy.GalaxyAction +import net.psforever.services.local.support.{HackClearActor, HackClearEnvelope} +import net.psforever.types.PlanetSideEmpire /** * The logic that governs facilities and structures found in the cavern regions. @@ -52,12 +53,12 @@ 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 ! LocalServiceMessage(amenity.Zone.id,LocalAction.ClearTemporaryHack(PlanetSideGUID(0), amenity)) + building.Zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(amenity)) } }) // No map update needed - will be sent by `HackCaptureActor` when required case _ => - details.galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(details.building.infoUpdateMessage())) + details.galaxyService ! MessageEnvelope("", GalaxyAction.MapUpdate(details.building.infoUpdateMessage())) } Behaviors.same } 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 1290b9956..db12a7555 100644 --- a/src/main/scala/net/psforever/actors/zone/building/FacilityLogic.scala +++ b/src/main/scala/net/psforever/actors/zone/building/FacilityLogic.scala @@ -7,9 +7,10 @@ import net.psforever.actors.commands.NtuCommand 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.{LocalAction, LocalServiceMessage} -import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.galaxy.GalaxyAction +import net.psforever.services.local.support.{HackClearActor, HackClearEnvelope} +import net.psforever.types.PlanetSideEmpire /** * The logic that governs standard facilities and structures. @@ -52,12 +53,12 @@ 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 ! LocalServiceMessage(amenity.Zone.id, LocalAction.ClearTemporaryHack(PlanetSideGUID(0), amenity)) + building.Zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(amenity)) } }) // No map update needed - will be sent by `HackCaptureActor` when required case _ => - details.galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(details.building.infoUpdateMessage())) + details.galaxyService ! MessageEnvelope("", GalaxyAction.MapUpdate(details.building.infoUpdateMessage())) } Behaviors.same } 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 c90579c55..676838bcc 100644 --- a/src/main/scala/net/psforever/actors/zone/building/MajorFacilityLogic.scala +++ b/src/main/scala/net/psforever/actors/zone/building/MajorFacilityLogic.scala @@ -13,12 +13,13 @@ import net.psforever.objects.serverobject.resourcesilo.ResourceSiloControl import net.psforever.objects.serverobject.structures.{Amenity, Building} import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAware, CaptureTerminalAwareBehavior} import net.psforever.objects.sourcing.PlayerSource -import net.psforever.packet.game.PlanetsideAttributeMessage -import net.psforever.services.{InterstellarClusterService, Service} -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} -import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} +import net.psforever.packet.game.{GenericObjectActionMessage, PlanetsideAttributeMessage} +import net.psforever.services.InterstellarClusterService +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.{GenericObjectAction, PlanetsideAttribute, SendResponse} +import net.psforever.services.galaxy.GalaxyAction +import net.psforever.services.local.support.{CaptureEnvelope, HackCaptureActor, HackClearActor, HackClearEnvelope} +import net.psforever.types.PlanetSideEmpire /** * A package class that conveys the important information for handling facility updates. @@ -101,10 +102,7 @@ case object MajorFacilityLogic hackedAmenities } amenitiesToClear.foreach { amenity => - building.Zone.LocalEvents ! LocalServiceMessage( - amenity.Zone.id, - LocalAction.ClearTemporaryHack(PlanetSideGUID(0), amenity) - ) + building.Zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(amenity)) } // No map update needed - will be sent by `HackCaptureActor` when required case dome: ForceDomePhysics => @@ -120,7 +118,7 @@ case object MajorFacilityLogic .filter(amenity => applicable.contains(amenity.Definition)) .foreach { _.Actor ! msg } case _ => - details.galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(details.building.infoUpdateMessage())) + details.galaxyService ! MessageEnvelope("", GalaxyAction.MapUpdate(details.building.infoUpdateMessage())) } Behaviors.same } @@ -205,30 +203,24 @@ case object MajorFacilityLogic case Some(GeneratorControl.Event.UnderAttack) => val events = zone.AvatarEvents val guid = building.GUID - val msg = AvatarAction.GenericObjectAction(Service.defaultPlayerGUID, guid, 15) - building.PlayersInSOI.foreach { player => - events ! AvatarServiceMessage(player.Name, msg) - } + val msg = GenericObjectAction(guid, 15) + events ! BundledEnvelope(building.PlayersInSOI.map { player => MessageEnvelope(player.Name, msg) }) false case Some(GeneratorControl.Event.Critical) => val events = zone.AvatarEvents val guid = building.GUID - val msg = AvatarAction.PlanetsideAttributeToAll(guid, 46, 1) - building.PlayersInSOI.foreach { player => - events ! AvatarServiceMessage(player.Name, msg) - } + val msg = PlanetsideAttribute(guid, 46, 1) + events ! BundledEnvelope(building.PlayersInSOI.map { player => MessageEnvelope(player.Name, msg) }) true case Some(GeneratorControl.Event.Destabilized) => val events = zone.AvatarEvents val guid = building.GUID - val msg = AvatarAction.GenericObjectAction(Service.defaultPlayerGUID, guid, 16) - building.PlayersInSOI.foreach { player => - events ! AvatarServiceMessage(player.Name, msg) - } + val msg = GenericObjectAction(guid, 16) + events ! BundledEnvelope(building.PlayersInSOI.map { player => MessageEnvelope(player.Name, msg) }) if (building.hasCavernLockBenefit) { - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.SendResponse(PlanetsideAttributeMessage(building.GUID, 67, 0)) + SendResponse(PlanetsideAttributeMessage(building.GUID, 67, 0)) ) } false @@ -237,10 +229,9 @@ case object MajorFacilityLogic case Some(GeneratorControl.Event.Offline) => powerLost(details) val zone = building.Zone - val msg = AvatarAction.PlanetsideAttributeToAll(building.GUID, 46, 2) - building.PlayersInSOI.foreach { player => - zone.AvatarEvents ! AvatarServiceMessage(player.Name, msg) - } //??? + val events = zone.AvatarEvents + val msg = PlanetsideAttribute(building.GUID, 46, 2) + events ! BundledEnvelope(building.PlayersInSOI.map { player => MessageEnvelope(player.Name, msg) }) //??? true case Some(GeneratorControl.Event.Normal) => true @@ -249,13 +240,13 @@ case object MajorFacilityLogic powerRestored(details) val events = zone.AvatarEvents val guid = building.GUID - val msg1 = AvatarAction.PlanetsideAttributeToAll(guid, 46, 0) - val msg2 = AvatarAction.GenericObjectAction(Service.defaultPlayerGUID, guid, 17) - building.PlayersInSOI.foreach { player => - val name = player.Name - events ! AvatarServiceMessage(name, msg1) //reset ???; might be global? - events ! AvatarServiceMessage(name, msg2) //This facility's generator is back on line - } + //1. reset ???; might be global? + //2. This facility's generator is back on line + val list = SendResponse( + PlanetsideAttributeMessage(guid, 46, 0), + GenericObjectActionMessage(guid, 17) + ) + events ! BundledEnvelope(building.PlayersInSOI.map { player => MessageEnvelope(player.Name, list) }) true case _ => false @@ -283,7 +274,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 ! LocalServiceMessage("", LocalAction.ResecureCaptureTerminal(terminal, PlayerSource.Nobody)) + bldg.Zone.LocalEvents ! CaptureEnvelope(HackCaptureActor.ResecureCaptureTerminal(terminal, terminal.Zone, PlayerSource.Nobody)) case _ => () } Behaviors.same @@ -305,10 +296,12 @@ case object MajorFacilityLogic building.Amenities.foreach { amenity => amenity.Actor ! powerMsg } - //amenities disabled; red warning lights - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(guid, 48, 1)) - //disable spawn target on deployment map - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(guid, 38, 0)) + //1. amenities disabled; red warning lights + //2 .disable spawn target on deployment map + events ! MessageEnvelope( + zoneId, + SendResponse(PlanetsideAttributeMessage(guid, 48, 1), PlanetsideAttributeMessage(guid, 38, 0)) + ) Behaviors.same } @@ -328,10 +321,12 @@ case object MajorFacilityLogic building.Amenities.foreach { amenity => amenity.Actor ! powerMsg } - //amenities enabled; normal lights - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(guid, 48, 0)) - //enable spawn target on deployment map - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(guid, 38, 1)) + //1. amenities enabled; normal lights + //2. enable spawn target on deployment map + events ! MessageEnvelope( + zoneId, + SendResponse(PlanetsideAttributeMessage(guid, 48, 0), PlanetsideAttributeMessage(guid, 38, 1)) + ) Behaviors.same } diff --git a/src/main/scala/net/psforever/actors/zone/building/WarpGateLogic.scala b/src/main/scala/net/psforever/actors/zone/building/WarpGateLogic.scala index b3eb01c1f..ec60d2f8d 100644 --- a/src/main/scala/net/psforever/actors/zone/building/WarpGateLogic.scala +++ b/src/main/scala/net/psforever/actors/zone/building/WarpGateLogic.scala @@ -4,9 +4,10 @@ package net.psforever.actors.zone.building import akka.actor.typed.Behavior import akka.actor.typed.scaladsl.Behaviors import net.psforever.actors.commands.NtuCommand -import net.psforever.actors.zone.{BuildingActor, ZoneActor} +import net.psforever.actors.zone.BuildingActor import net.psforever.objects.serverobject.structures.{Amenity, Building, WarpGate} -import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.galaxy.GalaxyAction import net.psforever.types.PlanetSideEmpire import net.psforever.util.Config @@ -182,7 +183,7 @@ case object WarpGateLogic val pairedWgFaction = pairedWg.Faction if (pairedWgFaction != terminalWgFaction) { pairedWg.Faction = terminalWgFaction - details.galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(pairedWg.infoUpdateMessage())) + details.galaxyService ! MessageEnvelope("", GalaxyAction.MapUpdate(pairedWg.infoUpdateMessage())) } //the terminal warpgate can not be considered broadcast for other faction if (terminalWg.AllowBroadcastFor.contains(pairedWgFaction)) { @@ -208,9 +209,7 @@ case object WarpGateLogic warpgate.Zone.Number, warpgate.MapId, previousAllowances, setBroadcastTo ) warpgate.AllowBroadcastFor = setBroadcastTo - (setBroadcastTo ++ previousAllowances).foreach { faction => - events ! GalaxyServiceMessage(faction.toString, msg) - } + events ! BundledEnvelope((setBroadcastTo ++ previousAllowances).map { faction => MessageEnvelope(faction.toString, msg) }) } /** diff --git a/src/main/scala/net/psforever/login/WorldSession.scala b/src/main/scala/net/psforever/login/WorldSession.scala index e24360396..d89c31765 100644 --- a/src/main/scala/net/psforever/login/WorldSession.scala +++ b/src/main/scala/net/psforever/login/WorldSession.scala @@ -15,8 +15,9 @@ import net.psforever.objects.sourcing.AmenitySource import net.psforever.objects.vital.TerminalUsedActivity import net.psforever.objects.zones.Zone import net.psforever.types.{ExoSuitType, PlanetSideGUID, TransactionType, Vector3} -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.ObjectDelete import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -280,7 +281,7 @@ object WorldSession { * @throws `RuntimeException` if slot is not a player visible slot (holsters) * @see `ask` * @see `AvatarAction.ObjectDelete` - * @see `AvatarAction.SendResponse` + * @see `SendResponse` * @see `Containable.CanNotPutItemInSlot` * @see `Containable.PutItemInSlotOnly` * @see `GUIDTask.registerEquipment` @@ -289,7 +290,7 @@ object WorldSession { * @see `ObjectHeldMessage` * @see `Player.DrawnSlot` * @see `Player.LastDrawnSlot` - * @see `Service.defaultPlayerGUID` + * @see `Default.GUID0` * @see `TaskBundle` * @see `Zone.AvatarEvents` * @param player the player whose visible slot will be equipped and drawn @@ -326,10 +327,7 @@ object WorldSession { case _ => forcedTolowerRaisedArm(localPlayer, localPlayer.GUID, localZone) localPlayer.DrawnSlot = localSlot - localZone.AvatarEvents ! AvatarServiceMessage( - localZone.id, - AvatarAction.ObjectHeld(localGUID, localSlot, localSlot) - ) + localZone.AvatarEvents ! MessageEnvelope(localZone.id, localGUID, AvatarAction.ObjectHeld(localSlot, localSlot)) } Future(this) } @@ -370,10 +368,7 @@ object WorldSession { localZone.GUID(item_guid) match { case Some(_) => () case None => //acting on old data? - localZone.AvatarEvents ! AvatarServiceMessage( - localZone.id, - AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item_guid) - ) + localZone.AvatarEvents ! MessageEnvelope(localZone.id, ObjectDelete(item_guid)) } case _ => () } @@ -532,7 +527,6 @@ object WorldSession { * Failure of this process is not supported and may lead to irregular behavior. * @see `ActorRef` * @see `AvatarAction.ObjectDelete` - * @see `AvatarServiceMessage` * @see `Containable.MoveItem` * @see `Container` * @see `Equipment` @@ -597,10 +591,7 @@ object WorldSession { localGUID match { case Some(guid) => //see LockerContainerControl.RemoveItemFromSlotCallback - localSource.Zone.AvatarEvents ! AvatarServiceMessage( - localChannel, - AvatarAction.ObjectDelete(Service.defaultPlayerGUID, guid) - ) + localSource.Zone.AvatarEvents ! MessageEnvelope(localChannel, ObjectDelete(guid)) case None => () } val moveResult = ask(localDestination.Actor, Containable.PutItemInSlotOrAway(localItem, Some(localDestSlot))) @@ -625,8 +616,7 @@ object WorldSession { * Remove an item from a player's locker inventory. * Failure of this process is not supported and may lead to irregular behavior. * @see `ActorRef` - * @see `AvatarAction.ObjectDelete` - * @see `AvatarServiceMessage` + * @see `ObjectDelete` * @see `Containable.MoveItem` * @see `Container` * @see `Equipment` @@ -702,10 +692,7 @@ object WorldSession { localGUID match { case Some(guid) => //see LockerContainerControl.RemoveItemFromSlotCallback - localSource.Zone.AvatarEvents ! AvatarServiceMessage( - localChannel, - AvatarAction.ObjectDelete(Service.defaultPlayerGUID, guid) - ) + localSource.Zone.AvatarEvents ! MessageEnvelope(localChannel, ObjectDelete(guid)) case None => () } val moveResult = ask(localDestination.Actor, Containable.PutItemInSlotOrAway(localItem, Some(localDestSlot))) @@ -742,8 +729,7 @@ object WorldSession { * If the player's already-drawn hand is the same as the one that will hold the grenade (first sidearm holster), * treat it like the sidearm occupier rather than the already-drawn weapon - * the old weapon goes into the backpack or onto the ground. - * @see `AvatarAction.ObjectHeld` - * @see `AvatarServiceMessage` + * @see `ObjectHeld` * @see `Containable.RemoveItemFromSlot` * @see `countRestrictAttempts` * @see `forcedTolowerRaisedArm` @@ -795,10 +781,7 @@ object WorldSession { } //put up hand with grenade in it tplayer.DrawnSlot = slotNum - zone.AvatarEvents ! AvatarServiceMessage( - zone.id, - AvatarAction.ObjectHeld(guid, slotNum, slotNum) - ) + zone.AvatarEvents ! MessageEnvelope(zone.id, guid, AvatarAction.ObjectHeld(slotNum, slotNum)) log.info(s"${tplayer.Name} has quickly drawn a ${grenade.Definition.Name}") None case None => @@ -872,8 +855,7 @@ object WorldSession { /** * If the player has a raised arm, lower it. * Do it manually, bypassing the checks in the normal procedure. - * @see `AvatarAction.ObjectHeld` - * @see `AvatarServiceMessage` + * @see `ObjectHeld` * @see `Player.DrawnSlot` * @see `Player.HandsDownSlot` * @param tplayer the player @@ -885,10 +867,7 @@ object WorldSession { val slot = tplayer.DrawnSlot if (slot != Player.HandsDownSlot) { tplayer.DrawnSlot = Player.HandsDownSlot - zone.AvatarEvents ! AvatarServiceMessage( - zone.id, - AvatarAction.ObjectHeld(guid, Player.HandsDownSlot, slot) - ) + zone.AvatarEvents ! MessageEnvelope(zone.id, guid, AvatarAction.ObjectHeld(Player.HandsDownSlot, slot)) true } else { false @@ -951,7 +930,7 @@ object WorldSession { player.ContributionFrom(term) } } - player.Zone.AvatarEvents ! AvatarServiceMessage( + player.Zone.AvatarEvents ! MessageEnvelope( player.Name, AvatarAction.TerminalOrderResult(guid, transaction, result) ) diff --git a/src/main/scala/net/psforever/objects/BoomerDeployable.scala b/src/main/scala/net/psforever/objects/BoomerDeployable.scala index 6cf03ddb6..22ce1938d 100644 --- a/src/main/scala/net/psforever/objects/BoomerDeployable.scala +++ b/src/main/scala/net/psforever/objects/BoomerDeployable.scala @@ -11,9 +11,9 @@ import net.psforever.objects.vital.Vitality import net.psforever.objects.vital.etc.TriggerUsedReason import net.psforever.objects.vital.interaction.DamageInteraction import net.psforever.objects.zones.Zone -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.ObjectDelete +import net.psforever.services.local.LocalAction import net.psforever.types.PlanetSideEmpire import scala.annotation.unused @@ -87,9 +87,9 @@ class BoomerDeployableControl(mine: BoomerDeployable) val events = mine.Zone.LocalEvents val msg = LocalAction.DeployItem(mine) originalOwner.collect { name => - events ! LocalServiceMessage(name, msg) + events ! MessageEnvelope(name, msg) } - events ! LocalServiceMessage(player.Name, msg) + events ! MessageEnvelope(player.Name, msg) } override def dismissDeployable() : Unit = { @@ -107,10 +107,7 @@ class BoomerDeployableControl(mine: BoomerDeployable) zone.Ground ! Zone.Ground.RemoveItem(guid) case _ => () } - zone.AvatarEvents! AvatarServiceMessage( - zone.id, - AvatarAction.ObjectDelete(Service.defaultPlayerGUID, guid) - ) + zone.AvatarEvents! MessageEnvelope(zone.id, ObjectDelete(guid)) TaskWorkflow.execute(GUIDTask.unregisterObject(zone.GUID, trigger)) case None => () } diff --git a/src/main/scala/net/psforever/objects/Default.scala b/src/main/scala/net/psforever/objects/Default.scala index 09cd99b87..b94c2c8f2 100644 --- a/src/main/scala/net/psforever/objects/Default.scala +++ b/src/main/scala/net/psforever/objects/Default.scala @@ -1,7 +1,12 @@ // Copyright (c) 2017 PSForever package net.psforever.objects +import net.psforever.types.{PlanetSideGUID, ValidPlanetSideGUID} + object Default { + //'g'lobal 'u'nique 'id'entifier + final val GUID0: PlanetSideGUID = ValidPlanetSideGUID(0) + //cancellable import akka.actor.Cancellable protected class InternalCancellable extends Cancellable { diff --git a/src/main/scala/net/psforever/objects/Deployables.scala b/src/main/scala/net/psforever/objects/Deployables.scala index c75057197..6fa64bd33 100644 --- a/src/main/scala/net/psforever/objects/Deployables.scala +++ b/src/main/scala/net/psforever/objects/Deployables.scala @@ -9,8 +9,9 @@ import net.psforever.objects.ce.{Deployable, DeployedItem} import net.psforever.objects.sourcing.{PlayerSource, SourceEntry} import net.psforever.objects.zones.Zone import net.psforever.packet.game._ +import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.types.PlanetSideGUID -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.local.LocalAction object Deployables { //private val log = org.log4s.getLogger("Deployables") @@ -92,13 +93,9 @@ object Deployables { } target.AssignOwnership(None) } - events ! LocalServiceMessage( + events ! MessageEnvelope( s"${target.Faction}", - LocalAction.DeployableMapIcon( - PlanetSideGUID(0), - DeploymentAction.Dismiss, - DeployableInfo(target.GUID, Deployable.Icon(item), target.Position, PlanetSideGUID(0)) - ) + LocalAction.DeployableMapIcon(DeploymentAction.Dismiss, DeployableInfo(target.GUID, Deployable.Icon(item), target.Position, PlanetSideGUID(0))) ) } diff --git a/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala b/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala index ceea45622..075f756d6 100644 --- a/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala +++ b/src/main/scala/net/psforever/objects/ExplosiveDeployable.scala @@ -17,9 +17,9 @@ import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult} import net.psforever.objects.vital.projectile.ProjectileReason import net.psforever.objects.zones.Zone import net.psforever.types.Vector3 -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.local.LocalAction import scala.annotation.unused import scala.concurrent.duration._ @@ -182,7 +182,7 @@ object ExplosiveDeployableControl { target.Health = 1 // short-circuit logic in DestructionAwareness val zone = target.Zone zone.Activity ! Zone.HotSpot.Activity(cause) - zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.Detonate(target.GUID, target)) + zone.LocalEvents ! MessageEnvelope(zone.id, LocalAction.Detonate(target.GUID, target)) Zone.serverSideDamage( zone, target, @@ -210,14 +210,14 @@ object ExplosiveDeployableControl { Some(if (target.Jammed || target.Destroyed) 0 seconds else 500 milliseconds) ) target.Destroyed = true - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.Destroy(target.GUID, attribution, Service.defaultPlayerGUID, target.Position) + AvatarAction.Destroy(target.GUID, attribution, Default.GUID0, target.Position) ) if (target.Health == 0) { - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.TriggerEffect(Service.defaultPlayerGUID, "detonate_damaged_mine", target.GUID) + LocalAction.TriggerEffect("detonate_damaged_mine", target.GUID) ) } } diff --git a/src/main/scala/net/psforever/objects/FieldTurretDeployable.scala b/src/main/scala/net/psforever/objects/FieldTurretDeployable.scala index be51a5cba..2ebf81e88 100644 --- a/src/main/scala/net/psforever/objects/FieldTurretDeployable.scala +++ b/src/main/scala/net/psforever/objects/FieldTurretDeployable.scala @@ -12,8 +12,8 @@ import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObjec import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, TurretSource} import net.psforever.objects.vital.{DismountingActivity, InGameActivity, MountingActivity, ShieldCharge} import net.psforever.packet.game.HackState1 -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} -import net.psforever.types.PlanetSideGUID +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.PlanetsideAttribute import scala.annotation.unused @@ -98,9 +98,9 @@ class FieldTurretControl(turret: TurretDeployable) if (canChargeShields) { turret.LogActivity(ShieldCharge(amount, motivator)) turret.Shields = turret.Shields + amount - turret.Zone.VehicleEvents ! VehicleServiceMessage( + turret.Zone.VehicleEvents ! MessageEnvelope( s"${turret.Actor}", - VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), turret.GUID, turret.Definition.shieldUiAttribute, turret.Shields) + PlanetsideAttribute(turret.GUID, turret.Definition.shieldUiAttribute, turret.Shields) ) } } diff --git a/src/main/scala/net/psforever/objects/Players.scala b/src/main/scala/net/psforever/objects/Players.scala index 720a84728..7aa3fee0d 100644 --- a/src/main/scala/net/psforever/objects/Players.scala +++ b/src/main/scala/net/psforever/objects/Players.scala @@ -19,11 +19,13 @@ import net.psforever.objects.vital.{InGameActivity, InGameHistory, RevivingActiv import net.psforever.objects.zones.Zone import net.psforever.packet.game._ import net.psforever.types.{ChatMessageType, ExoSuitType, PlanetSideGUID, Vector3} -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.{ObjectDelete, SendResponse} +import net.psforever.services.local.LocalAction import scala.annotation.tailrec +import scala.collection.mutable.ArrayBuffer object Players { private val log = org.log4s.getLogger("Players") @@ -48,10 +50,7 @@ object Players { ) { val events = target.Zone.AvatarEvents val uname = user.Name - events ! AvatarServiceMessage( - uname, - AvatarAction.SendResponse(Service.defaultPlayerGUID, RepairMessage(target.GUID, progress.toInt)) - ) + events ! MessageEnvelope(uname, SendResponse(RepairMessage(target.GUID, progress.toInt))) true } else { false @@ -61,7 +60,7 @@ object Players { /** * na * @see `AvatarAction.Revive` - * @see `AvatarResponse.Revive` + * @see `AvatarAction.Revive` * @param target the player being revived * @param medic the name of the player doing the reviving * @param item the tool being used to revive the target player @@ -76,12 +75,12 @@ object Players { PlayerControl.sendResponse( target.Zone, medicName, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, + Default.GUID0, + SendResponse( InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine) ) ) - PlayerControl.sendResponse(target.Zone, name, AvatarAction.Revive(target.GUID)) + PlayerControl.sendResponse(target.Zone, name, Default.GUID0, AvatarAction.Revive(target.GUID)) } /** @@ -163,7 +162,7 @@ object Players { ExoSuitDefinition.Select(exosuit, player.Faction).Permissions match { case Nil => true - case permissions if player.IsInVRZone => + case _ if player.IsInVRZone => true case permissions if subtype != 0 => val certs = player.avatar.certifications @@ -316,7 +315,7 @@ object Players { ): Boolean = { if (player.Zone == obj.Zone && addFunc(obj)) { obj.Actor ! Deployable.Ownership(player) - player.Zone.LocalEvents ! LocalServiceMessage(player.Name, LocalAction.DeployableUIFor(obj.Definition.Item)) + player.Zone.LocalEvents ! MessageEnvelope(player.Name, LocalAction.DeployableUIFor(obj.Definition.Item)) true } else { false @@ -333,7 +332,7 @@ object Players { //sent to avatar event bus to preempt additional tool management buildCooldownReset(zone, channel, obj.GUID) //sent to local event bus to cooperate with deployable management - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( channel, LocalAction.DeployableUIFor(obj.Definition.Item) ) @@ -347,10 +346,7 @@ object Players { */ def buildCooldownReset(zone: Zone, channel: String, guid: PlanetSideGUID): Unit = { //sent to avatar event bus to preempt additional tool management - zone.AvatarEvents ! AvatarServiceMessage( - channel, - AvatarAction.SendResponse(Service.defaultPlayerGUID, GenericObjectActionMessage(guid, 21)) - ) + zone.AvatarEvents ! MessageEnvelope(channel, SendResponse(GenericObjectActionMessage(guid, 21))) } /** @@ -406,10 +402,7 @@ object Players { } }) { val zone = player.Zone - zone.AvatarEvents ! AvatarServiceMessage( - zone.id, - AvatarAction.ObjectDelete(Service.defaultPlayerGUID, tool.GUID) - ) + zone.AvatarEvents ! MessageEnvelope(zone.id, ObjectDelete(tool.GUID)) true } else { false @@ -444,27 +437,20 @@ object Players { if ((player.Slot(index).Equipment = obj).contains(obj)) { val fireMode = tool.FireModeIndex val ammoType = tool.AmmoTypeIndex + val list: ArrayBuffer[MessageEnvelope] = ArrayBuffer() player.Inventory -= x.start obj.FireModeIndex = fireMode //TODO any penalty for being handed an OCM version of the tool? - events ! AvatarServiceMessage( - zone.id, - AvatarAction.EquipmentInHand(Service.defaultPlayerGUID, pguid, index, obj) - ) + list.append(MessageEnvelope(zone.id, AvatarAction.EquipmentInHand(pguid, index, obj))) if (obj.AmmoTypeIndex != ammoType) { obj.AmmoTypeIndex = ammoType - events ! AvatarServiceMessage( - name, - AvatarAction.SendResponse(Service.defaultPlayerGUID, ChangeAmmoMessage(obj.GUID, ammoType)) - ) + list.append(MessageEnvelope(name, SendResponse(ChangeAmmoMessage(obj.GUID, ammoType)))) } if (player.DrawnSlot == Player.HandsDownSlot) { player.DrawnSlot = index - events ! AvatarServiceMessage( - zone.id, - AvatarAction.ObjectHeld(pguid, index, index) - ) + list.append(MessageEnvelope(zone.id, pguid, AvatarAction.ObjectHeld(index, index))) } + events ! BundledEnvelope(list) } case Nil => ; //no replacements found } diff --git a/src/main/scala/net/psforever/objects/SensorDeployable.scala b/src/main/scala/net/psforever/objects/SensorDeployable.scala index cd08bedb3..77e812a23 100644 --- a/src/main/scala/net/psforever/objects/SensorDeployable.scala +++ b/src/main/scala/net/psforever/objects/SensorDeployable.scala @@ -12,10 +12,10 @@ import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.repair.RepairableEntity import net.psforever.objects.vital.SimpleResolutions import net.psforever.objects.vital.interaction.DamageResult +import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.types.{PlanetSideGUID, Vector3} -import net.psforever.services.Service -import net.psforever.services.local.{LocalAction, LocalServiceMessage} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.message.PlanetsideAttribute +import net.psforever.services.local.LocalAction import scala.annotation.unused import scala.concurrent.duration._ @@ -75,9 +75,9 @@ class SensorDeployableControl(sensor: SensorDeployable) override def StartJammeredSound(target: Any, dur: Int): Unit = target match { case obj: PlanetSideServerObject if !jammedSound => - obj.Zone.VehicleEvents ! VehicleServiceMessage( + obj.Zone.VehicleEvents ! MessageEnvelope( obj.Zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 1) + PlanetsideAttribute(obj.GUID, 54, 1) ) super.StartJammeredSound(obj, dur) case _ => ; @@ -87,9 +87,9 @@ class SensorDeployableControl(sensor: SensorDeployable) target match { case obj: PlanetSideServerObject with JammableUnit => val zone = obj.Zone - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, unk1=false, 1000) + LocalAction.TriggerEffectInfo(obj.GUID, "on", unk1=false, 1000) ) super.StartJammeredStatus(obj, dur) case _ => ; @@ -99,9 +99,9 @@ class SensorDeployableControl(sensor: SensorDeployable) target match { case obj: PlanetSideServerObject if jammedSound => val zone = obj.Zone - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 54, 0) + PlanetsideAttribute(obj.GUID, 54, 0) ) case _ => ; } @@ -112,9 +112,9 @@ class SensorDeployableControl(sensor: SensorDeployable) target match { case obj: PlanetSideServerObject with JammableUnit if obj.Jammed => val zone = sensor.Zone - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", obj.GUID, unk1=true, 1000) + LocalAction.TriggerEffectInfo(obj.GUID, "on", unk1=true, 1000) ) case _ => ; } @@ -124,9 +124,9 @@ class SensorDeployableControl(sensor: SensorDeployable) override def finalizeDeployable(callback: ActorRef) : Unit = { super.finalizeDeployable(callback) val zone = sensor.Zone - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", sensor.GUID, unk1=true, 1000) + LocalAction.TriggerEffectInfo(sensor.GUID, "on", unk1=true, 1000) ) } } @@ -141,9 +141,9 @@ object SensorDeployableControl { def DestructionAwareness(target: Deployable, attribution: PlanetSideGUID): Unit = { Deployables.AnnounceDestroyDeployable(target, Some(1 seconds)) val zone = target.Zone - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.TriggerEffectInfo(Service.defaultPlayerGUID, "on", target.GUID, unk1=false, 1000) + LocalAction.TriggerEffectInfo(target.GUID, "on", unk1=false, 1000) ) //position the explosion effect near the bulky area of the sensor stalk val ang = target.Orientation @@ -157,9 +157,9 @@ object SensorDeployableControl { pos.z + math.cos(yRadians).toFloat * 0.875f ) } - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.TriggerEffectLocation(Service.defaultPlayerGUID, "motion_sensor_destroyed", explosionPos, ang) + LocalAction.TriggerEffectLocation("motion_sensor_destroyed", explosionPos, ang) ) //TODO replaced by an alternate model (charred stub)? } diff --git a/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala b/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala index 15a7ceadc..42282ca57 100644 --- a/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala +++ b/src/main/scala/net/psforever/objects/ShieldGeneratorDeployable.scala @@ -13,21 +13,21 @@ import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.repair.RepairableEntity import net.psforever.objects.vital.interaction.DamageResult import net.psforever.objects.vital.resolution.ResolutionCalculations +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.PlanetsideAttribute import net.psforever.types.PlanetSideGUID -import net.psforever.services.Service -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} class ShieldGeneratorDeployable(cdef: ShieldGeneratorDefinition) extends Deployable(cdef) with Hackable with JammableUnit -class ShieldGeneratorDefinition extends DeployableDefinition(240) +class ShieldGeneratorDefinition extends DeployableDefinition(objectId = 240) with WithShields { Packet = new ShieldGeneratorConverter DeployCategory = DeployableCategory.ShieldGenerators - override def Initialize(obj: Deployable, context: ActorContext) = { + override def Initialize(obj: Deployable, context: ActorContext): Unit = { obj.Actor = context.actorOf(Props(classOf[ShieldGeneratorControl], obj), PlanetSideServerObject.UniqueActorName(obj)) } @@ -39,10 +39,10 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable) with JammableBehavior with DamageableEntity with RepairableEntity { - def DeployableObject = gen - def JammableObject = gen - def DamageableObject = gen - def RepairableObject = gen + def DeployableObject: ShieldGeneratorDeployable = gen + def JammableObject: ShieldGeneratorDeployable = gen + def DamageableObject: ShieldGeneratorDeployable = gen + def RepairableObject: ShieldGeneratorDeployable = gen deletionType = 1 //from DeployableBehavior override def postStop(): Unit = { @@ -125,9 +125,9 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable) override def StartJammeredStatus(target: Any, dur: Int): Unit = target match { case obj: PlanetSideServerObject with JammableUnit => - obj.Zone.VehicleEvents ! VehicleServiceMessage( + obj.Zone.VehicleEvents ! MessageEnvelope( obj.Zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 1) + PlanetsideAttribute(obj.GUID, 27, 1) ) super.StartJammeredStatus(obj, dur) case _ => ; @@ -138,9 +138,9 @@ class ShieldGeneratorControl(gen: ShieldGeneratorDeployable) override def CancelJammeredStatus(target: Any): Unit = { target match { case obj: PlanetSideServerObject with JammableUnit if obj.Jammed => - obj.Zone.VehicleEvents ! VehicleServiceMessage( + obj.Zone.VehicleEvents ! MessageEnvelope( obj.Zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 0) + PlanetsideAttribute(obj.GUID, 27, 0) ) case _ => ; } @@ -160,9 +160,9 @@ object ShieldGeneratorControl { //shields if (damageToShields) { val zone = target.Zone - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 68, target.Shields) + PlanetsideAttribute(target.GUID, 68, target.Shields) ) } } diff --git a/src/main/scala/net/psforever/objects/TelepadDeployable.scala b/src/main/scala/net/psforever/objects/TelepadDeployable.scala index a0cfa954a..c524f7f24 100644 --- a/src/main/scala/net/psforever/objects/TelepadDeployable.scala +++ b/src/main/scala/net/psforever/objects/TelepadDeployable.scala @@ -11,7 +11,8 @@ import net.psforever.objects.vehicles.Utility.InternalTelepad import net.psforever.objects.vital.SimpleResolutions import net.psforever.objects.vital.interaction.DamageResult import net.psforever.objects.zones.Zone -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.local.LocalAction import scala.concurrent.duration._ @@ -133,6 +134,6 @@ object TelepadControl { } def TelepadError(zone: Zone, channel: String, msg: String): Unit = { - zone.LocalEvents ! LocalServiceMessage(channel, LocalAction.RouterTelepadMessage(msg)) + zone.LocalEvents ! MessageEnvelope(channel, LocalAction.RouterTelepadMessage(msg)) } } diff --git a/src/main/scala/net/psforever/objects/Tools.scala b/src/main/scala/net/psforever/objects/Tools.scala index 6744550b3..459e4f4ce 100644 --- a/src/main/scala/net/psforever/objects/Tools.scala +++ b/src/main/scala/net/psforever/objects/Tools.scala @@ -3,8 +3,8 @@ package net.psforever.objects import net.psforever.objects.equipment.ChargeFireModeDefinition import net.psforever.packet.game.QuantityUpdateMessage -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse object Tools { /** @@ -22,12 +22,9 @@ object Tools { tool.FireMode match { case mode: ChargeFireModeDefinition if tool.Magazine > 0 => val magazine = tool.Magazine -= mode.RoundsPerInterval - player.Zone.AvatarEvents ! AvatarServiceMessage( + player.Zone.AvatarEvents ! MessageEnvelope( player.Name, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - QuantityUpdateMessage(tool.AmmoSlot.Box.GUID, magazine) - ) + SendResponse(QuantityUpdateMessage(tool.AmmoSlot.Box.GUID, magazine)) ) player.isAlive case _ => diff --git a/src/main/scala/net/psforever/objects/TurretDeployable.scala b/src/main/scala/net/psforever/objects/TurretDeployable.scala index f21f74707..7b0cc5f3e 100644 --- a/src/main/scala/net/psforever/objects/TurretDeployable.scala +++ b/src/main/scala/net/psforever/objects/TurretDeployable.scala @@ -20,7 +20,8 @@ import net.psforever.objects.vital.resistance.StandardResistanceProfile import net.psforever.objects.vital.{SimpleResolutions, StandardVehicleResistance} import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.packet.game.TriggeredSound -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.vehicle.VehicleAction import scala.concurrent.duration.FiniteDuration @@ -109,9 +110,10 @@ abstract class TurretDeployableControl case player: Player => seat.unmount(player) player.VehicleSeated = None - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.KickPassenger(player.GUID, 4, wasKickedByDriver, TurretObject.GUID) + player.GUID, + VehicleAction.KickPassenger(4, wasKickedByDriver, TurretObject.GUID) ) } } diff --git a/src/main/scala/net/psforever/objects/Vehicles.scala b/src/main/scala/net/psforever/objects/Vehicles.scala index 019f4d7f2..fbee9477c 100644 --- a/src/main/scala/net/psforever/objects/Vehicles.scala +++ b/src/main/scala/net/psforever/objects/Vehicles.scala @@ -9,13 +9,16 @@ import net.psforever.objects.serverobject.resourcesilo.ResourceSilo import net.psforever.objects.serverobject.transfer.TransferContainer import net.psforever.objects.serverobject.structures.WarpGate import net.psforever.objects.vehicles._ +import net.psforever.objects.vehicles.control.CargoBehavior import net.psforever.objects.zones.Zone -import net.psforever.packet.game.{ChatMsg, FrameVehicleStateMessage, GenericObjectActionEnum, GenericObjectActionMessage, HackMessage, HackState, HackState1, HackState7, TriggeredSound, VehicleStateMessage} +import net.psforever.packet.game.{ChatMsg, FrameVehicleStateMessage, GenericObjectActionEnum, HackMessage, HackState, HackState1, HackState7, TriggeredSound, VehicleStateMessage} import net.psforever.types.{ChatMessageType, DriveState, PlanetSideEmpire, PlanetSideGUID, Vector3} -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.{GenericObjectAction, PlanetsideAttribute, SendResponse, SetEmpire} +import net.psforever.services.local.LocalAction +import net.psforever.services.vehicle.VehicleAction + +import scala.collection.mutable.ArrayBuffer //import scala.concurrent.duration._ @@ -45,9 +48,10 @@ object Vehicles { val locked = VehicleLockState.Locked.id Array(0, 3).foreach(group => vehicle.PermissionGroup(group, locked)) Vehicles.ReloadAccessPermissions(vehicle, tplayer.Faction.toString) - vehicle.Zone.VehicleEvents ! VehicleServiceMessage( + vehicle.Zone.VehicleEvents ! MessageEnvelope( vehicle.Zone.id, - VehicleAction.Ownership(tplayer.GUID, vehicle.GUID) + tplayer.GUID, + VehicleAction.Ownership(vehicle.GUID) ) Some(vehicle) case None => @@ -88,9 +92,9 @@ object Vehicles { val empire = VehicleLockState.Empire.id (0 to 2).foreach(group => vehicle.PermissionGroup(group, empire)) ReloadAccessPermissions(vehicle, vehicle.Faction.toString) - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.LoseOwnership(ownerGuid.getOrElse(Service.defaultPlayerGUID), guid) + VehicleAction.LoseOwnership(ownerGuid.getOrElse(Default.GUID0), guid) ) result } @@ -138,13 +142,14 @@ object Vehicles { val pguid = player.GUID if (vehicle.OwnerGuid.contains(pguid)) { vehicle.AssignOwnership(None) - //vehicle.Zone.VehicleEvents ! VehicleServiceMessage(player.Name, VehicleAction.Ownership(pguid, PlanetSideGUID(0))) + //vehicle.Zone.VehicleEvents ! MessageEnvelope(player.Name, pguid, VehicleAction.Ownership(pguid, PlanetSideGUID(0))) //val vguid = vehicle.GUID val empire = VehicleLockState.Empire.id (0 to 2).foreach(group => { vehicle.PermissionGroup(group, empire) - /*vehicle.Zone.VehicleEvents ! VehicleServiceMessage( + /*vehicle.Zone.VehicleEvents ! MessageEnvelope( s"${vehicle.Faction}", + pguid, VehicleAction.SeatPermissions(pguid, vguid, group, empire) )*/ }) @@ -167,9 +172,9 @@ object Vehicles { def ReloadAccessPermissions(vehicle: Vehicle, toChannel: String): Unit = { val vehicle_guid = vehicle.GUID (0 to 3).foreach(group => { - vehicle.Zone.AvatarEvents ! AvatarServiceMessage( + vehicle.Zone.AvatarEvents ! MessageEnvelope( toChannel, - AvatarAction.PlanetsideAttributeToAll(vehicle_guid, group + 10, vehicle.PermissionGroup(group).get.id) + PlanetsideAttribute(vehicle_guid, group + 10, vehicle.PermissionGroup(group).get.id) ) }) } @@ -246,16 +251,15 @@ object Vehicles { val hFaction = hacker.Faction val zone = target.Zone val zoneid = zone.id + val avatarEvents = zone.AvatarEvents val vehicleEvents = zone.VehicleEvents - val localEvents = zone.LocalEvents val previousOwnerName = target.OwnerName.getOrElse("") - vehicleEvents ! VehicleServiceMessage( + val occupantMessages: ArrayBuffer[MessageEnvelope] = ArrayBuffer() + val vehicleMessages: ArrayBuffer[MessageEnvelope] = ArrayBuffer() + vehicleMessages.append(MessageEnvelope( zoneid, - VehicleAction.SendResponse( - Service.defaultPlayerGUID, - HackMessage(HackState1.Unk2, tGuid, hGuid, 100, 0f, HackState.Hacked, HackState7.Unk8) - ) - ) + SendResponse(HackMessage(HackState1.Unk2, tGuid, hGuid, 100, 0f, HackState.Hacked, HackState7.Unk8)) + )) target.Actor ! CommonMessages.Hack(hacker, target) // Forcefully dismount any cargo target.CargoHolds.foreach { case (_, cargoHold) => @@ -269,24 +273,29 @@ object Vehicles { player: Player => seat.unmount(player) player.VehicleSeated = None - vehicleEvents ! VehicleServiceMessage( + occupantMessages.append(MessageEnvelope( zoneid, - VehicleAction.KickPassenger(player.GUID, 4, unk2 = false, tGuid) - ) + player.GUID, + VehicleAction.KickPassenger(4, unk2 = false, tGuid) + )) } // In case BFR is occupied and may or may not be crouched if (GlobalDefinitions.isBattleFrameVehicle(target.Definition) && target.Seat(0).isDefined) { - zone.LocalEvents ! LocalServiceMessage( - zoneid, - LocalAction.SendGenericObjectActionMessage(PlanetSideGUID(-1), target.GUID, GenericObjectActionEnum.BFRShieldsDown)) - zone.LocalEvents ! LocalServiceMessage( - zoneid, - LocalAction.SendResponse( - FrameVehicleStateMessage(target.GUID, 0, target.Position, target.Orientation, Some(Vector3(0f, 0f, 0f)), unk2=false, 0, 0, is_crouched=true, is_airborne=false, ascending_flight=false, 10, 0, 0))) - zone.LocalEvents ! LocalServiceMessage( - zoneid, - LocalAction.SendResponse( - VehicleStateMessage(target.GUID, 0, target.Position, target.Orientation, Some(Vector3(0f, 0f, 0f)), None, 0, 0, 15, is_decelerating=false, is_cloaked=false))) + vehicleMessages.appendAll(List( + MessageEnvelope(zoneid, + GenericObjectAction(target.GUID, GenericObjectActionEnum.BFRShieldsDown.id) + ), + MessageEnvelope(zoneid, + SendResponse( + FrameVehicleStateMessage(target.GUID, 0, target.Position, target.Orientation, Some(Vector3(0f, 0f, 0f)), unk2=false, 0, 0, is_crouched=true, is_airborne=false, ascending_flight=false, 10, 0, 0) + ) + ), + MessageEnvelope(zoneid, + SendResponse( + VehicleStateMessage(target.GUID, 0, target.Position, target.Orientation, Some(Vector3(0f, 0f, 0f)), None, 0, 0, 15, is_decelerating=false, is_cloaked=false) + ) + ) + )) } }) // If the vehicle can fly and is flying: deconstruct it; and well played to whomever managed to hack a plane in mid air @@ -311,25 +320,17 @@ object Vehicles { Vehicles.Own(target, hacker) //todo: Send HackMessage -> HackCleared to vehicle? can be found in packet captures. Not sure if necessary. // And broadcast the faction change to other clients - zone.AvatarEvents ! AvatarServiceMessage( - zoneid, - AvatarAction.SetEmpire(Service.defaultPlayerGUID, tGuid, hFaction) - ) + vehicleMessages.append(MessageEnvelope(zoneid, SetEmpire(tGuid, hFaction))) } - localEvents ! LocalServiceMessage( + vehicleMessages.append(MessageEnvelope( zoneid, - LocalAction.TriggerSound(hGuid, TriggeredSound.HackVehicle, target.Position, 30, 0.49803925f) - ) + hGuid, + LocalAction.TriggerSound(TriggeredSound.HackVehicle, target.Position, 30, 0.49803925f) + )) if (zone.Players.exists(_.name.equals(previousOwnerName))) { - localEvents ! LocalServiceMessage( - previousOwnerName, - LocalAction.SendResponse(ChatMsg(ChatMessageType.UNK_226, "@JackStolen")) - ) + vehicleMessages.append(MessageEnvelope(previousOwnerName, SendResponse(ChatMsg(ChatMessageType.UNK_226, "@JackStolen")))) } - localEvents ! LocalServiceMessage( - hacker.Name, - LocalAction.SendResponse(ChatMsg(ChatMessageType.UNK_226, "@JackVehicleOwned")) - ) + occupantMessages.append(MessageEnvelope(hacker.Name, SendResponse(ChatMsg(ChatMessageType.UNK_226, "@JackVehicleOwned")))) // Clean up after specific vehicles, e.g. remove router telepads // If AMS is deployed, swap it to the new faction target.Definition match { @@ -341,16 +342,15 @@ object Vehicles { util.Actor ! TelepadLike.Activate(util) } case GlobalDefinitions.ams if target.DeploymentState == DriveState.Deployed => - vehicleEvents ! VehicleServiceMessage.AMSDeploymentChange(zone) + vehicleMessages.append(MessageEnvelope(zone.id, VehicleAction.AMSDeploymentChange(zone))) case _ => () } - vehicleEvents ! VehicleServiceMessage( + vehicleMessages.append(MessageEnvelope( zoneid, - VehicleAction.SendResponse( - Service.defaultPlayerGUID, - HackMessage(HackState1.Unk2, tGuid, tGuid, 0, 1L, HackState.HackCleared, HackState7.Unk8) - ) - ) + SendResponse(HackMessage(HackState1.Unk2, tGuid, tGuid, 0, 1L, HackState.HackCleared, HackState7.Unk8)) + )) + avatarEvents ! BundledEnvelope(occupantMessages) + vehicleEvents ! BundledEnvelope(vehicleMessages) target.Actor ! CommonMessages.ClearHack() } diff --git a/src/main/scala/net/psforever/objects/avatar/AvatarBotActor.scala b/src/main/scala/net/psforever/objects/avatar/AvatarBotActor.scala index 486289d66..b388fa988 100644 --- a/src/main/scala/net/psforever/objects/avatar/AvatarBotActor.scala +++ b/src/main/scala/net/psforever/objects/avatar/AvatarBotActor.scala @@ -4,7 +4,6 @@ package net.psforever.objects.avatar import akka.actor.{Actor, ActorRef} import net.psforever.actors.zone.ShootingRangeTargetSpawner import net.psforever.objects.{GlobalDefinitions, Tool} -import net.psforever.objects.avatar.AvatarBot import net.psforever.objects.equipment._ import net.psforever.objects.serverobject.aura.{Aura, AuraEffectBehavior} import net.psforever.objects.serverobject.CommonMessages @@ -14,18 +13,17 @@ import net.psforever.objects.vital.resolution.ResolutionCalculations.Output import net.psforever.objects.zones._ import net.psforever.packet.game._ import net.psforever.types._ -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.avatar.AvatarAction import net.psforever.objects.serverobject.environment.interaction.RespondsToZoneEnvironment import net.psforever.objects.serverobject.repair.Repairable import net.psforever.objects.sourcing.PlayerSource import net.psforever.objects.vital.{HealFromEquipment, RepairFromEquipment} import net.psforever.objects.vital.etc.SuicideReason import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} import java.util.concurrent.{Executors, TimeUnit} - import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ import scala.util.Random @@ -53,7 +51,7 @@ class AvatarBotActor(bot: AvatarBot, spawnerActor: ActorRef) def InteractiveObject: AvatarBot = bot - private[this] val log = org.log4s.getLogger(bot.Name) + //private[this] val log = org.log4s.getLogger(bot.Name) private[this] val damageLog = org.log4s.getLogger(Damageable.LogChannel) private val scheduler = Executors.newScheduledThreadPool(2) /** suffocating, or regaining breath? */ @@ -107,14 +105,13 @@ class AvatarBotActor(bot: AvatarBot, spawnerActor: ActorRef) if (!(bot.isMoving || user.isMoving)) { //only allow stationary heals val newHealth = bot.Health = originalHealth + 10 val magazine = item.Discharge() - events ! AvatarServiceMessage( + events ! MessageEnvelope( uname, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, + SendResponse( InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong) ) ) - events ! AvatarServiceMessage(zone.id, AvatarAction.PlanetsideAttributeToAll(guid, 0, newHealth)) + events ! MessageEnvelope(zone.id, PlanetsideAttribute(guid, 0, newHealth)) bot.LogActivity( HealFromEquipment( PlayerSource(user), @@ -124,10 +121,9 @@ class AvatarBotActor(bot: AvatarBot, spawnerActor: ActorRef) ) } //progress bar remains visible for all heal attempts - events ! AvatarServiceMessage( + events ! MessageEnvelope( uname, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, + SendResponse( RepairMessage(guid, bot.Health * 100 / definition.MaxHealth) ) ) @@ -150,14 +146,13 @@ class AvatarBotActor(bot: AvatarBot, spawnerActor: ActorRef) val newArmor = bot.Armor = originalArmor + Repairable.applyLevelModifier(user, item, RepairToolValue(item)).toInt + definition.RepairMod val magazine = item.Discharge() - events ! AvatarServiceMessage( + events ! MessageEnvelope( uname, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, + SendResponse( InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong) ) ) - events ! AvatarServiceMessage(zone.id, AvatarAction.PlanetsideAttributeToAll(guid, 4, bot.Armor)) + events ! MessageEnvelope(zone.id, PlanetsideAttribute(guid, 4, bot.Armor)) bot.LogActivity( RepairFromEquipment( PlayerSource(user), @@ -167,10 +162,9 @@ class AvatarBotActor(bot: AvatarBot, spawnerActor: ActorRef) ) } //progress bar remains visible for all repair attempts - events ! AvatarServiceMessage( + events ! MessageEnvelope( uname, - AvatarAction - .SendResponse(Service.defaultPlayerGUID, RepairMessage(guid, bot.Armor * 100 / bot.MaxArmor)) + SendResponse(RepairMessage(guid, bot.Armor * 100 / bot.MaxArmor)) ) } @@ -231,9 +225,9 @@ class AvatarBotActor(bot: AvatarBot, spawnerActor: ActorRef) //always do armor update if (damageToArmor > 0) { val zone = target.Zone - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.PlanetsideAttributeToAll(target.GUID, 4, target.Armor) + PlanetsideAttribute(target.GUID, 4, target.Armor) ) } //choose @@ -280,16 +274,13 @@ class AvatarBotActor(bot: AvatarBot, spawnerActor: ActorRef) announceConfrontation = true //TODO should we? } if (damageToHealth > 0) { - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 0, health)) + events ! MessageEnvelope(zoneId, PlanetsideAttribute(targetGUID, 0, health)) announceConfrontation = true } val countableDamage = damageToHealth + damageToArmor if(announceConfrontation) { if (aggravated) { - events ! AvatarServiceMessage( - zoneId, - AvatarAction.SendResponse(targetGUID, AggravatedDamageMessage(targetGUID, countableDamage)) - ) + events ! MessageEnvelope(zoneId, targetGUID, SendResponse(AggravatedDamageMessage(targetGUID, countableDamage))) } else { //activity on map zone.Activity ! Zone.HotSpot.Activity(cause) @@ -332,22 +323,22 @@ class AvatarBotActor(bot: AvatarBot, spawnerActor: ActorRef) cause.adversarial match { case Some(a) => damageLog.info(s"${a.defender.Name} was killed by ${a.attacker.Name}") - events ! AvatarServiceMessage( + events ! MessageEnvelope( zoneChannel, AvatarAction.DestroyDisplay(a.attacker, a.defender, a.implement) ) case _ => damageLog.info(s"${bot.Name} killed ${bot.Sex.pronounObject}self") - events ! AvatarServiceMessage(zoneChannel, AvatarAction.DestroyDisplay(cause.interaction.target, cause.interaction.target, 0)) + events ! MessageEnvelope(zoneChannel, AvatarAction.DestroyDisplay(cause.interaction.target, cause.interaction.target, 0)) } - events ! AvatarServiceMessage(nameChannel, AvatarAction.Killed(bot_guid, cause, None)) //align client interface fields with state - events ! AvatarServiceMessage(zoneChannel, AvatarAction.PlanetsideAttributeToAll(bot_guid, 0, 0)) //health + events ! MessageEnvelope(nameChannel, bot_guid, AvatarAction.Killed(cause, None)) //align client interface fields with state + events ! MessageEnvelope(zoneChannel, PlanetsideAttribute(bot_guid, 0, 0)) //health val attribute = DamageableEntity.attributionTo(cause, target.Zone, bot_guid) - events ! AvatarServiceMessage( + events ! MessageEnvelope( nameChannel, - AvatarAction.SendResponse( - bot_guid, + bot_guid, + SendResponse( DestroyMessage(bot_guid, attribute, bot_guid, pos) ) //how many players get this message? ) @@ -402,13 +393,13 @@ class AvatarBotActor(bot: AvatarBot, spawnerActor: ActorRef) private def performEmote(): Unit = { val zone = bot.Zone zone.blockMap.sector(bot).livePlayerList.collect { t => - zone.LocalEvents ! LocalServiceMessage(t.Name, LocalAction.SendResponse(TriggerBotAction(bot.GUID))) + zone.LocalEvents ! MessageEnvelope(t.Name, SendResponse(TriggerBotAction(bot.GUID))) } } private def tickLogic(): Unit = { val zone = bot.Zone - if (!bot.Destroyed && zone.AllPlayers.size > 0) { + if (!bot.Destroyed && zone.AllPlayers.nonEmpty) { bot.zoneInteractions() val rotateRNG = Random.nextDouble() if (canRotate) { @@ -424,10 +415,10 @@ class AvatarBotActor(bot: AvatarBot, spawnerActor: ActorRef) ) } } - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, + bot.GUID, AvatarAction.PlayerState( - bot.GUID, bot.Position, bot.Velocity, bot.Orientation.z, @@ -436,10 +427,10 @@ class AvatarBotActor(bot: AvatarBot, spawnerActor: ActorRef) 0, bot.Crouching, bot.Jumping, - false, - bot.Cloaked, - false, - false + jump_thrust = false, + is_cloaked = bot.Cloaked, + spectator = false, + weaponInHand = false ) ) if (canEmote) { @@ -469,9 +460,9 @@ class AvatarBotActor(bot: AvatarBot, spawnerActor: ActorRef) override def StartJammeredSound(target: Any, dur: Int): Unit = target match { case obj: AvatarBot if !jammedSound => - obj.Zone.AvatarEvents ! AvatarServiceMessage( + obj.Zone.AvatarEvents ! MessageEnvelope( obj.Zone.id, - AvatarAction.PlanetsideAttributeToAll(obj.GUID, 27, 1) + PlanetsideAttribute(obj.GUID, 27, 1) ) super.StartJammeredSound(obj, 3000) case _ => ; @@ -502,9 +493,9 @@ class AvatarBotActor(bot: AvatarBot, spawnerActor: ActorRef) override def CancelJammeredSound(target: Any): Unit = target match { case obj: AvatarBot if jammedSound => - obj.Zone.AvatarEvents ! AvatarServiceMessage( + obj.Zone.AvatarEvents ! MessageEnvelope( obj.Zone.id, - AvatarAction.PlanetsideAttributeToAll(obj.GUID, 27, 0) + PlanetsideAttribute(obj.GUID, 27, 0) ) super.CancelJammeredSound(obj) case _ => ; @@ -521,10 +512,9 @@ class AvatarBotActor(bot: AvatarBot, spawnerActor: ActorRef) } def UpdateAuraEffect(target: AuraEffectBehavior.Target) : Unit = { - import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} val zone = target.Zone val value = target.Aura.foldLeft(0)(_ + AvatarBotActor.auraEffectToAttributeValue(_)) - zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.PlanetsideAttributeToAll(target.GUID, 54, value)) + zone.AvatarEvents ! MessageEnvelope(zone.id, PlanetsideAttribute(target.GUID, 54, value)) } } diff --git a/src/main/scala/net/psforever/objects/avatar/CorpseControl.scala b/src/main/scala/net/psforever/objects/avatar/CorpseControl.scala index 6e1cafafe..3537cfbd7 100644 --- a/src/main/scala/net/psforever/objects/avatar/CorpseControl.scala +++ b/src/main/scala/net/psforever/objects/avatar/CorpseControl.scala @@ -8,8 +8,8 @@ import net.psforever.objects.serverobject.containable.{Containable, ContainableB import net.psforever.packet.game.{ObjectAttachMessage, ObjectCreateDetailedMessage, ObjectDetachMessage} import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent import net.psforever.types.{PlanetSideEmpire, Vector3} -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{ObjectDelete, SendResponse} class CorpseControl(player: Player) extends Actor with ContainableBehavior { def ContainerObject = player @@ -25,9 +25,9 @@ class CorpseControl(player: Player) extends Actor with ContainableBehavior { val obj = ContainerObject obj.Find(item) match { case Some(slot) => - obj.Zone.AvatarEvents ! AvatarServiceMessage( + obj.Zone.AvatarEvents ! MessageEnvelope( player.Zone.id, - AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectAttachMessage(obj.GUID, item.GUID, slot)) + SendResponse(ObjectAttachMessage(obj.GUID, item.GUID, slot)) ) case None => ; } @@ -40,7 +40,7 @@ class CorpseControl(player: Player) extends Actor with ContainableBehavior { val zone = obj.Zone val events = zone.AvatarEvents item.Faction = PlanetSideEmpire.NEUTRAL - events ! AvatarServiceMessage(zone.id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item.GUID)) + events ! MessageEnvelope(zone.id, ObjectDelete(item.GUID)) } def PutItemInSlotCallback(item: Equipment, slot: Int): Unit = { @@ -48,10 +48,9 @@ class CorpseControl(player: Player) extends Actor with ContainableBehavior { val zone = obj.Zone val events = zone.AvatarEvents val definition = item.Definition - events ! AvatarServiceMessage( + events ! MessageEnvelope( zone.id, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, + SendResponse( ObjectCreateDetailedMessage( definition.ObjectId, item.GUID, @@ -65,12 +64,9 @@ class CorpseControl(player: Player) extends Actor with ContainableBehavior { def SwapItemCallback(item: Equipment, fromSlot: Int): Unit = { val obj = ContainerObject val zone = obj.Zone - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - ObjectDetachMessage(obj.GUID, item.GUID, Vector3.Zero, 0f) - ) + SendResponse(ObjectDetachMessage(obj.GUID, item.GUID, Vector3.Zero, 0f)) ) } } diff --git a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala index d597ea565..b25803856 100644 --- a/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala +++ b/src/main/scala/net/psforever/objects/avatar/PlayerControl.scala @@ -25,9 +25,8 @@ import net.psforever.objects.zones._ import net.psforever.packet.game._ import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent import net.psforever.types._ -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.local.LocalAction import net.psforever.objects.locker.LockerContainerControl import net.psforever.objects.serverobject.environment.interaction.RespondsToZoneEnvironment import net.psforever.objects.serverobject.repair.Repairable @@ -36,6 +35,8 @@ import net.psforever.objects.vital.collision.CollisionReason import net.psforever.objects.vital.etc.{PainboxReason, SuicideReason} import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult} import net.psforever.packet.PlanetSideGamePacket +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{EventMessage, HintsAtAttacker, ObjectDelete, PlanetsideAttribute, SendResponse} import org.joda.time.{LocalDateTime, Seconds} import scala.concurrent.duration._ @@ -125,7 +126,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm val zoneId = zone.id sendResponse(zone, zoneId, PlanetsideAttributeMessage(revivalTargetGuid, attribute_type=0, health)) sendResponse(zone, zoneId, AvatarDeadStateMessage(DeadState.Alive, timer_max=0, timer=0, player.Position, player.Faction, unk5=true)) - sendResponse(zone, zoneId, AvatarAction.PlanetsideAttributeToAll(revivalTargetGuid, attribute_type=0, health)) + sendResponse(zone, zoneId, PlanetsideAttributeMessage(revivalTargetGuid, attribute_type=0, health)) avatarActor ! AvatarActor.InitializeImplants avatarActor ! AvatarActor.SuspendStaminaRegeneration(Duration(1, "second")) @@ -147,14 +148,11 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm if (!(player.isMoving || user.isMoving)) { //only allow stationary heals val newHealth = player.Health = originalHealth + 10 val magazine = item.Discharge() - events ! AvatarServiceMessage( + events ! MessageEnvelope( uname, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong) - ) + SendResponse(InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong)) ) - events ! AvatarServiceMessage(zone.id, AvatarAction.PlanetsideAttributeToAll(guid, 0, newHealth)) + events ! MessageEnvelope(zone.id, PlanetsideAttribute(guid, 0, newHealth)) player.LogActivity( HealFromEquipment( PlayerSource(user), @@ -174,14 +172,11 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm } if (player != user) { //"Someone is trying to heal you" - events ! AvatarServiceMessage(player.Name, AvatarAction.PlanetsideAttributeToAll(guid, 55, 1)) + events ! MessageEnvelope(player.Name, PlanetsideAttribute(guid, 55, 1)) //progress bar remains visible for all heal attempts - events ! AvatarServiceMessage( + events ! MessageEnvelope( uname, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - RepairMessage(guid, player.Health * 100 / definition.MaxHealth) - ) + SendResponse(RepairMessage(guid, player.Health * 100 / definition.MaxHealth)) ) } } @@ -219,14 +214,11 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm val newArmor = player.Armor = originalArmor + Repairable.applyLevelModifier(user, item, RepairToolValue(item)).toInt + definition.RepairMod val magazine = item.Discharge() - events ! AvatarServiceMessage( + events ! MessageEnvelope( uname, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong) - ) + SendResponse(InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong)) ) - events ! AvatarServiceMessage(zone.id, AvatarAction.PlanetsideAttributeToAll(guid, 4, player.Armor)) + events ! MessageEnvelope(zone.id, PlanetsideAttribute(guid, 4, player.Armor)) player.LogActivity( RepairFromEquipment( PlayerSource(user), @@ -247,16 +239,15 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm if (player != user) { if (player.isAlive) { //"Someone is trying to repair you" gets strobed twice for visibility - val msg = AvatarServiceMessage(player.Name, AvatarAction.PlanetsideAttributeToAll(guid, 56, 1)) + val msg = MessageEnvelope(player.Name, PlanetsideAttribute(guid, 56, 1)) events ! msg import scala.concurrent.ExecutionContext.Implicits.global context.system.scheduler.scheduleOnce(250 milliseconds, events, msg) } //progress bar remains visible for all repair attempts - events ! AvatarServiceMessage( + events ! MessageEnvelope( uname, - AvatarAction - .SendResponse(Service.defaultPlayerGUID, RepairMessage(guid, player.Armor * 100 / player.MaxArmor)) + SendResponse(RepairMessage(guid, player.Armor * 100 / player.MaxArmor)) ) } } @@ -321,16 +312,16 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm avatarActor ! AvatarActor.UpdateUseTime(kdef) player.Slot(slot).Equipment = None //remove from slot immediately; must exist on client for now TaskWorkflow.execute(GUIDTask.unregisterEquipment(zone.GUID, kit)) - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.PlanetsideAttributeToAll(player.GUID, attribute, value) + PlanetsideAttribute(player.GUID, attribute, value) ) - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( player.Name, AvatarAction.UseKit(kguid, kdef.ObjectId) ) case _ => - player.Zone.AvatarEvents ! AvatarServiceMessage( + player.Zone.AvatarEvents ! MessageEnvelope( player.Name, AvatarAction.KitNotUsed(kit.GUID, msg) ) @@ -344,15 +335,17 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm val events = player.Zone.AvatarEvents val resistance = player.TestArmMotion(slot) if (resistance && !updateMyHolsterArm) { - events ! AvatarServiceMessage( + events ! MessageEnvelope( player.Name, - AvatarAction.ObjectHeld(player.GUID, before, -1) + player.GUID, + AvatarAction.ObjectHeld(before, -1) ) } else if ((!resistance && before != slot && (player.DrawnSlot = slot) != before) && ItemSwapSlot != before) { val mySlot = if (updateMyHolsterArm) slot else -1 //use as a short-circuit - events ! AvatarServiceMessage( + events ! MessageEnvelope( Players.ZoneChannelIfSpectating(player), - AvatarAction.ObjectHeld(player.GUID, mySlot, player.LastDrawnSlot) + player.GUID, + AvatarAction.ObjectHeld(mySlot, player.LastDrawnSlot) ) val isHolsters = player.VisibleSlots.contains(slot) val equipment = player.Slot(slot).Equipment.orElse { player.Slot(before).Equipment } @@ -362,9 +355,10 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm log.info(s"${player.Name} has drawn a ${unholsteredItem.Definition.Name} from its holster") if (unholsteredItem.Definition == GlobalDefinitions.remote_electronics_kit) { //rek beam/icon colour must match the player's correct hack level - events ! AvatarServiceMessage( + events ! MessageEnvelope( Players.ZoneChannelIfSpectating(player), - AvatarAction.PlanetsideAttribute(unholsteredItem.GUID, 116, player.avatar.hackingSkillLevel()) + unholsteredItem.GUID, + PlanetsideAttribute(unholsteredItem.GUID, 116, player.avatar.hackingSkillLevel()) ) } case None => () @@ -376,7 +370,11 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm //make sure the player didn't just initialte an orbital strike. If not (the if below is true), make sure waypoint is removed if (holsteredEquipment.Definition == GlobalDefinitions.command_detonater && player.avatar.cr.value > 3 && !player.avatar.cooldowns.purchase.exists(os => os._1 == "orbital_strike" && Seconds.secondsBetween(os._2, LocalDateTime.now()).getSeconds < 12)) { - player.Zone.LocalEvents ! LocalServiceMessage(s"${player.Faction}", LocalAction.SendPacket(OrbitalStrikeWaypointMessage(player.GUID, None))) + player.Zone.LocalEvents ! MessageEnvelope( + s"${player.Faction}", + PlanetSideGUID(-1), + SendResponse(OrbitalStrikeWaypointMessage(player.GUID, None)) + ) } case None => log.info(s"${player.Name} lowers ${player.Sex.possessive} hand") @@ -400,7 +398,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm if (exosuit == ExoSuitType.MAX) { player.ResistArmMotion(PlayerControl.maxRestriction) } - player.Zone.AvatarEvents ! AvatarServiceMessage( + player.Zone.AvatarEvents ! MessageEnvelope( player.Name, AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, result) ) @@ -532,7 +530,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm Deployables.initializeConstructionItem(player.avatar.certifications, citem) } val zone = player.Zone - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( Players.ZoneChannelIfSpectating(player), AvatarAction.ChangeLoadout( player.GUID, @@ -548,7 +546,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm itemsToDrop ) ) - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( player.Name, AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, result=true) ) @@ -577,9 +575,9 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm case Zone.Ground.CanNotPickupItem(_, item_guid, reason) => log.warn(s"${player.Name} failed to pick up an item ($item_guid) from the ground because $reason") if (reason.startsWith("@")) { - player.Zone.AvatarEvents ! AvatarServiceMessage( + player.Zone.AvatarEvents ! MessageEnvelope( player.Name, - AvatarAction.SendResponse(Service.defaultPlayerGUID, ChatMsg(ChatMessageType.UNK_227, reason)) + SendResponse(ChatMsg(ChatMessageType.UNK_227, reason)) ) } @@ -597,7 +595,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm val trigger = new BoomerTrigger trigger.Companion = obj.GUID obj.Trigger = trigger - zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, tool.GUID)) + zone.AvatarEvents ! MessageEnvelope(zone.id, ObjectDelete(tool.GUID)) TaskWorkflow.execute(GUIDTask.unregisterEquipment(zone.GUID, tool)) player.Find(tool) match { case Some(index) if player.VisibleSlots.contains(index) => @@ -649,7 +647,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm case Player.LoseDeployable(obj) => if (player.avatar.deployables.Remove(obj)) { - player.Zone.LocalEvents ! LocalServiceMessage(player.Name, LocalAction.DeployableUIFor(obj.Definition.Item)) + player.Zone.LocalEvents ! MessageEnvelope(player.Name, LocalAction.DeployableUIFor(obj.Definition.Item)) } case _ => ; @@ -722,7 +720,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm //insert afterHolsters.foreach(elem => player.Slot(elem.start).Equipment = elem.obj) afterInventory.foreach(elem => player.Inventory.InsertQuickly(elem.start, elem.obj)) - player.Zone.AvatarEvents ! AvatarServiceMessage( + player.Zone.AvatarEvents ! MessageEnvelope( Players.ZoneChannelIfSpectating(player), AvatarAction.ChangeExosuit( player.GUID, @@ -748,7 +746,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm def loseDeployableOwnership(obj: Deployable): Boolean = { if (player.avatar.deployables.Remove(obj)) { obj.Actor ! Deployable.Ownership(None) - player.Zone.LocalEvents ! LocalServiceMessage(player.Name, LocalAction.DeployableUIFor(obj.Definition.Item)) + player.Zone.LocalEvents ! MessageEnvelope(player.Name, LocalAction.DeployableUIFor(obj.Definition.Item)) true } else { @@ -769,18 +767,15 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm case GlobalDefinitions.router_telepad => () /* no special animation */ case GlobalDefinitions.ace if obj.Definition.deployAnimation == DeployAnimation.Standard => - zone.LocalEvents ! LocalServiceMessage( + val ownerGuid = obj.OwnerGuid.getOrElse(Default.GUID0) + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.TriggerEffectLocation( - obj.OwnerGuid.getOrElse(Service.defaultPlayerGUID), - "spawn_object_effect", - obj.Position, - obj.Orientation - ) + ownerGuid, + LocalAction.TriggerEffectLocation("spawn_object_effect", obj.Position, obj.Orientation) ) case GlobalDefinitions.advanced_ace if obj.Definition.deployAnimation == DeployAnimation.Fdu => - zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.PutDownFDU(player.GUID)) + zone.AvatarEvents ! MessageEnvelope(zone.id, AvatarAction.PutDownFDU(player.GUID)) case _ => org.log4s .getLogger(name = "Deployables") @@ -883,9 +878,9 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm //always do armor update if (damageToArmor > 0) { val zone = target.Zone - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.PlanetsideAttributeToAll(target.GUID, 4, target.Armor) + PlanetsideAttribute(target.GUID, 4, target.Armor) ) } //choose @@ -941,10 +936,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm target.LogActivity(cause) //stat changes if (damageToCapacitor > 0) { - events ! AvatarServiceMessage( - target.Name, - AvatarAction.PlanetsideAttributeSelf(targetGUID, 7, target.Capacitor.toLong) - ) + events ! MessageEnvelope(target.Name, PlanetsideAttribute(targetGUID, 7, target.Capacitor.toLong)) announceConfrontation = true //TODO should we? } if (damageToStamina > 0) { @@ -952,15 +944,15 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm announceConfrontation = true //TODO should we? } if (damageToHealth > 0) { - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 0, health)) + events ! MessageEnvelope(zoneId, PlanetsideAttribute(targetGUID, 0, health)) announceConfrontation = true } val countableDamage = damageToHealth + damageToArmor if(announceConfrontation) { if (aggravated) { - events ! AvatarServiceMessage( + events ! MessageEnvelope( zoneId, - AvatarAction.SendResponse(Service.defaultPlayerGUID, AggravatedDamageMessage(targetGUID, countableDamage)) + SendResponse(AggravatedDamageMessage(targetGUID, countableDamage)) ) } else { //activity on map @@ -973,42 +965,37 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm val name = pSource.Name zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match { case Some(tplayer) => - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( target.Name, - AvatarAction.HitHint(tplayer.GUID, target.GUID) + target.GUID, + HintsAtAttacker(tplayer.GUID) ) case None => - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( target.Name, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - DamageWithPositionMessage(countableDamage, pSource.Position) - ) + SendResponse(DamageWithPositionMessage(countableDamage, pSource.Position)) ) } case source => - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( target.Name, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - DamageWithPositionMessage(countableDamage, source.Position) - ) + SendResponse(DamageWithPositionMessage(countableDamage, source.Position)) ) } case None => cause.interaction.cause match { case o: PainboxReason => - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( target.Name, AvatarAction.EnvironmentalDamage(target.GUID, o.entity.GUID, countableDamage) ) case _: CollisionReason => - events ! AvatarServiceMessage( + events ! MessageEnvelope( zoneId, - AvatarAction.SendResponse(Service.defaultPlayerGUID, AggravatedDamageMessage(targetGUID, countableDamage)) + SendResponse(AggravatedDamageMessage(targetGUID, countableDamage)) ) case _ => - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( target.Name, AvatarAction.EnvironmentalDamage(target.GUID, ValidPlanetSideGUID(0), countableDamage) ) @@ -1064,19 +1051,16 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm damageLog.info(s"${player.Name} killed ${player.Sex.pronounObject}self") } - events ! AvatarServiceMessage(nameChannel, AvatarAction.Killed(player_guid, cause, target.VehicleSeated)) //align client interface fields with state - events ! AvatarServiceMessage(zoneChannel, AvatarAction.PlanetsideAttributeToAll(player_guid, 0, 0)) //health + events ! MessageEnvelope(nameChannel, player_guid, AvatarAction.Killed(cause, target.VehicleSeated)) //align client interface fields with state + events ! MessageEnvelope(zoneChannel, PlanetsideAttribute(player_guid, 0, 0)) //health if (target.Capacitor > 0) { target.Capacitor = 0 - events ! AvatarServiceMessage(nameChannel, AvatarAction.PlanetsideAttributeToAll(player_guid, 7, 0)) // capacitor + events ! MessageEnvelope(nameChannel, PlanetsideAttribute(player_guid, 7, 0)) // capacitor } val attribute = DamageableEntity.attributionTo(cause, target.Zone, player_guid) - events ! AvatarServiceMessage( + events ! MessageEnvelope( nameChannel, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - DestroyMessage(player_guid, attribute, Service.defaultPlayerGUID, pos) - ) //how many players get this message? + SendResponse(DestroyMessage(player_guid, attribute, Default.GUID0, pos)) //how many players get this message? ) } @@ -1105,12 +1089,12 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm override def StartJammeredSound(target: Any, dur: Int): Unit = target match { case obj: Player if !jammedSound => - obj.Zone.AvatarEvents ! AvatarServiceMessage( + obj.Zone.AvatarEvents ! MessageEnvelope( obj.Zone.id, - AvatarAction.PlanetsideAttributeToAll(obj.GUID, 27, 1) + PlanetsideAttribute(obj.GUID, 27, 1) ) super.StartJammeredSound(obj, 3000) - case _ => ; + case _ => () } /** @@ -1141,12 +1125,12 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm override def CancelJammeredSound(target: Any): Unit = target match { case obj: Player if jammedSound => - obj.Zone.AvatarEvents ! AvatarServiceMessage( + obj.Zone.AvatarEvents ! MessageEnvelope( obj.Zone.id, - AvatarAction.PlanetsideAttributeToAll(obj.GUID, 27, 0) + PlanetsideAttribute(obj.GUID, 27, 0) ) super.CancelJammeredSound(obj) - case _ => ; + case _ => () } def RepairToolValue(item: Tool): Float = { @@ -1167,9 +1151,9 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm obj.Find(item) match { case Some(slot) => PutItemInSlotCallback(item, slot) - case None => ; + case None => () } - case _ => ; + case _ => () } } @@ -1188,7 +1172,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm if (slot == obj.DrawnSlot) { obj.DrawnSlot = Player.HandsDownSlot } - events ! AvatarServiceMessage(toChannel, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item.GUID)) + events ! MessageEnvelope(toChannel, ObjectDelete(item.GUID)) } def PutItemInSlotCallback(item: Equipment, slot: Int): Unit = { @@ -1209,10 +1193,10 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm case Some(obj: BoomerDeployable) => val deployables = player.avatar.deployables if (!deployables.Contains(obj) && deployables.Valid(obj)) { - events ! AvatarServiceMessage(toChannel, AvatarAction.SendResponse( - Service.defaultPlayerGUID, - GenericObjectAction2Message(1, player.GUID, trigger.GUID) - )) + events ! MessageEnvelope( + toChannel, + SendResponse(GenericObjectAction2Message(1, player.GUID, trigger.GUID)) + ) Players.gainDeployableOwnership(player, obj, deployables.AddOverLimit) } case _ => () @@ -1228,15 +1212,12 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm case _ => () } - events ! AvatarServiceMessage( + events ! MessageEnvelope( toChannel, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - OCM.detailed(item, ObjectCreateMessageParent(guid, slot)) - ) + SendResponse(OCM.detailed(item, ObjectCreateMessageParent(guid, slot))) ) if (!player.isBackpack && willBeVisible) { - events ! AvatarServiceMessage(zone.id, AvatarAction.EquipmentInHand(guid, guid, slot, item)) + events ! MessageEnvelope(zone.id, guid, AvatarAction.EquipmentInHand(guid, slot, item)) } } @@ -1250,17 +1231,16 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm } else { player.Name } - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( toChannel, - AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item.GUID) + ObjectDelete(item.GUID) ) } def UpdateAuraEffect(target: AuraEffectBehavior.Target) : Unit = { - import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} val zone = target.Zone val value = target.Aura.foldLeft(0)(_ + PlayerControl.auraEffectToAttributeValue(_)) - zone.AvatarEvents ! AvatarServiceMessage(zone.id, AvatarAction.PlanetsideAttributeToAll(target.GUID, 54, value)) + zone.AvatarEvents ! MessageEnvelope(zone.id, PlanetsideAttribute(target.GUID, 54, value)) } } @@ -1290,11 +1270,11 @@ object PlayerControl { } def sendResponse(zone: Zone, channel: String, msg: PlanetSideGamePacket): Unit = { - zone.AvatarEvents ! AvatarServiceMessage(channel, AvatarAction.SendResponse(Service.defaultPlayerGUID, msg)) + zone.AvatarEvents ! MessageEnvelope(channel, SendResponse(msg)) } - def sendResponse(zone: Zone, channel: String, msg: AvatarAction.Action): Unit = { - zone.AvatarEvents ! AvatarServiceMessage(channel, msg) + def sendResponse(zone: Zone, channel: String, filter: PlanetSideGUID, msg: EventMessage): Unit = { + zone.AvatarEvents ! MessageEnvelope(channel, filter, msg) } def maxRestriction(player: Player, slot: Int): Boolean = { diff --git a/src/main/scala/net/psforever/objects/avatar/interaction/WithEntrance.scala b/src/main/scala/net/psforever/objects/avatar/interaction/WithEntrance.scala index 4e15faf4f..0e475b85f 100644 --- a/src/main/scala/net/psforever/objects/avatar/interaction/WithEntrance.scala +++ b/src/main/scala/net/psforever/objects/avatar/interaction/WithEntrance.scala @@ -6,6 +6,8 @@ import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, Env import net.psforever.objects.serverobject.environment.interaction.{InteractionWith, RespondsToZoneEnvironment} import net.psforever.objects.serverobject.interior.{Sidedness, TraditionalInteriorAware} import net.psforever.objects.zones.interaction.InteractsWithZone +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse import net.psforever.types.Vector3 import scala.annotation.unused @@ -90,33 +92,32 @@ class WithEntrance() ): Sidedness = { import net.psforever.objects.{Player, Vehicle} import net.psforever.packet.game.ChatMsg - import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} - import net.psforever.types.{ChatMessageType, PlanetSideGUID} + import net.psforever.types.ChatMessageType val channel = obj match { case p: Player => p.Name case v: Vehicle => v.Actor.toString() case _ => "" } if (door.Outwards == Vector3.Zero) { - obj.Zone.AvatarEvents ! AvatarServiceMessage( + obj.Zone.AvatarEvents ! MessageEnvelope( channel, - AvatarAction.SendResponse(PlanetSideGUID(0), ChatMsg(ChatMessageType.UNK_229, "Door not configured.")) + SendResponse(ChatMsg(ChatMessageType.UNK_229, "Door not configured.")) ) WhichSide } else { val result = Vector3.DotProduct(Vector3.Unit(obj.Position - door.Position), door.Outwards) > 0f if (result && WhichSide != Sidedness.OutsideOf) { //outside - obj.Zone.AvatarEvents ! AvatarServiceMessage( + obj.Zone.AvatarEvents ! MessageEnvelope( channel, - AvatarAction.SendResponse(PlanetSideGUID(0), ChatMsg(ChatMessageType.UNK_229, "You are now outside")) + SendResponse(ChatMsg(ChatMessageType.UNK_229, "You are now outside")) ) Sidedness.OutsideOf } else if (!result && WhichSide != Sidedness.InsideOf) { //inside - obj.Zone.AvatarEvents ! AvatarServiceMessage( + obj.Zone.AvatarEvents ! MessageEnvelope( channel, - AvatarAction.SendResponse(PlanetSideGUID(0), ChatMsg(ChatMessageType.UNK_229, "You are now inside")) + SendResponse(ChatMsg(ChatMessageType.UNK_229, "You are now inside")) ) Sidedness.InsideOf } else { diff --git a/src/main/scala/net/psforever/objects/avatar/interaction/WithGantry.scala b/src/main/scala/net/psforever/objects/avatar/interaction/WithGantry.scala index 09d4a330a..dc20521f6 100644 --- a/src/main/scala/net/psforever/objects/avatar/interaction/WithGantry.scala +++ b/src/main/scala/net/psforever/objects/avatar/interaction/WithGantry.scala @@ -7,8 +7,8 @@ import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, Env import net.psforever.objects.serverobject.shuttle.OrbitalShuttlePad import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.packet.game.{ChatMsg, PlayerStateShiftMessage, ShiftState} -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse import net.psforever.services.hart.ShuttleState import net.psforever.types.ChatMessageType @@ -35,18 +35,12 @@ class WithGantry(val channel: String) player.VehicleSeated.isEmpty => val (pos, ang) = Vehicles.dismountShuttle(shuttle, field.mountPoint) val events = shuttle.Zone.AvatarEvents - events ! AvatarServiceMessage( + events ! MessageEnvelope( channel, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - PlayerStateShiftMessage(ShiftState(0, pos, ang, None))) - ) - events ! AvatarServiceMessage( + SendResponse(PlayerStateShiftMessage(ShiftState(0, pos, ang, None)))) + events ! MessageEnvelope( channel, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - ChatMsg(ChatMessageType.UNK_227, "@Vehicle_OS_PlacedOutsideHallway") - ) + SendResponse(ChatMsg(ChatMessageType.UNK_227, "@Vehicle_OS_PlacedOutsideHallway")) ) case (Some(_: Vehicle) , _)=> obj.Actor ! RespondsToZoneEnvironment.Timer( diff --git a/src/main/scala/net/psforever/objects/avatar/interaction/WithWater.scala b/src/main/scala/net/psforever/objects/avatar/interaction/WithWater.scala index 73af2ad60..ae2ca54af 100644 --- a/src/main/scala/net/psforever/objects/avatar/interaction/WithWater.scala +++ b/src/main/scala/net/psforever/objects/avatar/interaction/WithWater.scala @@ -7,7 +7,8 @@ import net.psforever.objects.serverobject.environment.interaction.common.Watery import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget import net.psforever.objects.serverobject.environment.{EnvironmentTrait, PieceOfEnvironment, interaction} import net.psforever.objects.zones.interaction.InteractsWithZone -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.types.OxygenState import scala.concurrent.duration._ @@ -299,7 +300,7 @@ class WithWater(val channel: String) cond: OxygenStateTarget, data: Option[OxygenStateTarget] ): Unit = { - obj.Zone.AvatarEvents ! AvatarServiceMessage(channel, AvatarAction.OxygenState(cond, data)) + obj.Zone.AvatarEvents ! MessageEnvelope(channel, AvatarAction.OxygenState(cond, data)) } } diff --git a/src/main/scala/net/psforever/objects/avatar/scoring/Statistic.scala b/src/main/scala/net/psforever/objects/avatar/scoring/Statistic.scala index e6b5b03a3..10dcd36b9 100644 --- a/src/main/scala/net/psforever/objects/avatar/scoring/Statistic.scala +++ b/src/main/scala/net/psforever/objects/avatar/scoring/Statistic.scala @@ -2,7 +2,7 @@ package net.psforever.objects.avatar.scoring /** - * Organizes the eight fields one would find in an `AvatarServiceMessage` statistic field. + * Organizes the eight fields one would find in an statistic field. * The `_c` fields and the `_s` fields are paired when the values populate the packet * where `c` stands for "campaign" and `s` stands for "session". * "Session" values reflect on the UI as the K in K/D diff --git a/src/main/scala/net/psforever/objects/ce/DeployableBehavior.scala b/src/main/scala/net/psforever/objects/ce/DeployableBehavior.scala index fbdfa85ca..18a4585fa 100644 --- a/src/main/scala/net/psforever/objects/ce/DeployableBehavior.scala +++ b/src/main/scala/net/psforever/objects/ce/DeployableBehavior.scala @@ -6,9 +6,9 @@ import net.psforever.objects.guid.{GUIDTask, TaskWorkflow} import net.psforever.objects._ import net.psforever.objects.zones.Zone import net.psforever.packet.game._ -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.SetEmpire +import net.psforever.services.local.LocalAction import net.psforever.types.PlanetSideEmpire import scala.concurrent.duration._ @@ -111,7 +111,7 @@ trait DeployableBehavior { obj, toOwner = "", toFaction, - DeployableInfo(obj.GUID, Deployable.Icon.apply(obj.Definition.Item), obj.Position, Service.defaultPlayerGUID) + DeployableInfo(obj.GUID, Deployable.Icon.apply(obj.Definition.Item), obj.Position, Default.GUID0) ) startOwnerlessDecay() } @@ -198,14 +198,13 @@ trait DeployableBehavior { None } //zone build - localEvents ! LocalServiceMessage(zone.id, LocalAction.DeployItem(obj)) - //zone map icon - localEvents ! LocalServiceMessage( - obj.Faction.toString, - LocalAction.DeployableMapIcon( - Service.defaultPlayerGUID, - DeploymentAction.Build, - DeployableInfo(obj.GUID, Deployable.Icon(obj.Definition.Item), obj.Position, obj.OwnerGuid.getOrElse(Service.defaultPlayerGUID)) + localEvents ! BundledEnvelope( + /* zone build */ + MessageEnvelope(zone.id, LocalAction.DeployItem(obj)), + /* zone map icon */ + MessageEnvelope( + obj.Faction.toString, + LocalAction.DeployableMapIcon(DeploymentAction.Build, DeployableInfo(obj.GUID, Deployable.Icon(obj.Definition.Item), obj.Position, obj.OwnerGuid.getOrElse(Default.GUID0))) ) ) //local build management @@ -250,7 +249,7 @@ trait DeployableBehavior { //there's no special meaning behind directing any replies from from zone governance straight back to zone governance //this deployable control agency, however, will be expiring and can not be a recipient zone.Deployables ! Zone.Deployable.Dismiss(obj) - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, LocalAction.EliminateDeployable(obj, obj.GUID, obj.Position, deletionType) ) @@ -285,27 +284,21 @@ object DeployableBehavior { val localEvents = zone.LocalEvents if (originalFaction != toFaction) { obj.Faction = toFaction - //visual tells in regards to ownership by faction - zone.AvatarEvents ! AvatarServiceMessage( - zone.id, - AvatarAction.SetEmpire(Service.defaultPlayerGUID, dGuid, toFaction) - ) - //remove knowledge by the previous owner's faction - localEvents ! LocalServiceMessage( - originalFaction.toString, - LocalAction.DeployableMapIcon(Service.defaultPlayerGUID, DeploymentAction.Dismiss, info) + localEvents ! BundledEnvelope( + /* visual tells in regards to ownership by faction */ + MessageEnvelope(zone.id, SetEmpire(dGuid, toFaction)), + /* remove knowledge by the previous owner's faction */ + MessageEnvelope(originalFaction.toString, LocalAction.DeployableMapIcon(DeploymentAction.Dismiss, info)), + /* display to the given faction */ + MessageEnvelope(toFaction.toString, LocalAction.DeployableMapIcon(DeploymentAction.Build, info)) ) //remove deployable from original owner's toolbox and UI counter - zone.AllPlayers.filter(p => obj.OriginalOwnerName.contains(p.Name)) - .foreach { originalOwner => + localEvents ! BundledEnvelope(zone.AllPlayers + .filter(p => obj.OriginalOwnerName.contains(p.Name)) + .map { originalOwner => originalOwner.avatar.deployables.Remove(obj) - originalOwner.Zone.LocalEvents ! LocalServiceMessage(originalOwner.Name, LocalAction.DeployableUIFor(obj.Definition.Item)) - } - //display to the given faction - localEvents ! LocalServiceMessage( - toFaction.toString, - LocalAction.DeployableMapIcon(Service.defaultPlayerGUID, DeploymentAction.Build, info) - ) + MessageEnvelope(originalOwner.Name, LocalAction.DeployableUIFor(obj.Definition.Item)) + }) } } } diff --git a/src/main/scala/net/psforever/objects/ce/TelepadLike.scala b/src/main/scala/net/psforever/objects/ce/TelepadLike.scala index ad018f418..f90273a38 100644 --- a/src/main/scala/net/psforever/objects/ce/TelepadLike.scala +++ b/src/main/scala/net/psforever/objects/ce/TelepadLike.scala @@ -9,7 +9,9 @@ import net.psforever.objects.vehicles.Utility.InternalTelepad import net.psforever.objects.zones.Zone import net.psforever.packet.game.{GenericObjectActionMessage, ObjectCreateMessage, ObjectDeleteMessage} import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.SendResponse +import net.psforever.services.local.LocalAction import net.psforever.types.PlanetSideGUID trait TelepadLike { @@ -113,24 +115,22 @@ object TelepadLike { normally dispatched while the Router is transitioned into its Deploying state it is safe, however, to perform these actions at any time during and after the Deploying state */ - events ! LocalServiceMessage( - zoneId, - LocalAction.SendResponse( - ObjectCreateMessage( - udef.ObjectId, - utilityGUID, - ObjectCreateMessageParent(routerGUID, 2), //TODO stop assuming slot number - udef.Packet.ConstructorData(obj).get + events ! BundledEnvelope( + MessageEnvelope( + zoneId, + SendResponse( + ObjectCreateMessage( + udef.ObjectId, + utilityGUID, + ObjectCreateMessageParent(routerGUID, 2), //TODO stop assuming slot number + udef.Packet.ConstructorData(obj).get + ) ) - ) - ) - events ! LocalServiceMessage( - zoneId, - LocalAction.SendResponse(GenericObjectActionMessage(utilityGUID, 27)) - ) - events ! LocalServiceMessage( - zoneId, - LocalAction.SendResponse(GenericObjectActionMessage(utilityGUID, 30)) + ), + MessageEnvelope(zoneId, SendResponse(Seq( + GenericObjectActionMessage(utilityGUID, 27), + GenericObjectActionMessage(utilityGUID, 30) + ))) ) LinkTelepad(zone, utilityGUID) } @@ -138,14 +138,10 @@ object TelepadLike { def LinkTelepad(zone: Zone, telepadGUID: PlanetSideGUID): Unit = { val events = zone.LocalEvents val zoneId = zone.id - events ! LocalServiceMessage( - zoneId, - LocalAction.SendResponse(GenericObjectActionMessage(telepadGUID, 27)) - ) - events ! LocalServiceMessage( - zoneId, - LocalAction.SendResponse(GenericObjectActionMessage(telepadGUID, 28)) - ) + events ! MessageEnvelope(zoneId, SendResponse(Seq( + GenericObjectActionMessage(telepadGUID, 27), + GenericObjectActionMessage(telepadGUID, 28) + ))) } def InitializeTelepadDeployable(zone: Zone, internal: InternalTelepad, pad: TelepadDeployable): Unit = { @@ -175,7 +171,7 @@ class TelepadControl(obj: InternalTelepad) extends akka.actor.Actor { oldTpad.Actor ! TelepadLike.SeverLink(obj) } obj.Telepad = None - zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SendResponse(ObjectDeleteMessage(obj.GUID, 0))) + zone.LocalEvents ! MessageEnvelope(zone.id, SendResponse(ObjectDeleteMessage(obj.GUID, 0))) case TelepadLike.RequestLink(tpad: TelepadDeployable) => val zone = obj.Zone @@ -196,7 +192,7 @@ class TelepadControl(obj: InternalTelepad) extends akka.actor.Actor { } } else { val channel = obj.Owner.asInstanceOf[Vehicle].OwnerName.getOrElse("") - zone.LocalEvents ! LocalServiceMessage(channel, LocalAction.RouterTelepadMessage("@Teleport_NotDeployed")) + zone.LocalEvents ! MessageEnvelope(channel, LocalAction.RouterTelepadMessage("@Teleport_NotDeployed")) tpad.Actor ! TelepadLike.SeverLink(obj) } @@ -204,7 +200,7 @@ class TelepadControl(obj: InternalTelepad) extends akka.actor.Actor { if (obj.Telepad.contains(tpad.GUID)) { obj.Telepad = None val zone = obj.Zone - zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SendResponse(ObjectDeleteMessage(obj.GUID, 0))) + zone.LocalEvents ! MessageEnvelope(zone.id, SendResponse(ObjectDeleteMessage(obj.GUID, 0))) } case _ => () diff --git a/src/main/scala/net/psforever/objects/equipment/ArmorSiphonBehavior.scala b/src/main/scala/net/psforever/objects/equipment/ArmorSiphonBehavior.scala index 5547f36fd..9341e324d 100644 --- a/src/main/scala/net/psforever/objects/equipment/ArmorSiphonBehavior.scala +++ b/src/main/scala/net/psforever/objects/equipment/ArmorSiphonBehavior.scala @@ -10,8 +10,8 @@ import net.psforever.objects.vital.RepairFromArmorSiphon import net.psforever.objects.vital.etc.{ArmorSiphonModifiers, ArmorSiphonReason} import net.psforever.objects.vital.interaction.DamageInteraction import net.psforever.packet.game.QuantityUpdateMessage -import net.psforever.services.Service -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} import net.psforever.types.PlanetSideGUID import scala.collection.mutable @@ -78,9 +78,9 @@ object ArmorSiphonBehavior { if(before < after) { obj.LogActivity(RepairFromArmorSiphon(asr.siphon.Definition, VehicleSource(obj), before - after)) val zone = obj.Zone - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 0, after) + PlanetsideAttribute(obj.GUID, 0, after) ) } case _ => ; @@ -97,9 +97,9 @@ object ArmorSiphonBehavior { val siphon = siphonSlot.Equipment.get.asInstanceOf[Tool] val zone = obj.Zone //update current charge level - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( obj.Actor.toString, - VehicleAction.SendResponse(Service.defaultPlayerGUID, QuantityUpdateMessage(siphon.AmmoSlot.Box.GUID, siphon.Magazine)) + SendResponse(QuantityUpdateMessage(siphon.AmmoSlot.Box.GUID, siphon.Magazine)) ) siphonRecharge.put(guid, context.system.scheduler.scheduleWithFixedDelay( initialDelay = 3000 milliseconds, @@ -119,9 +119,9 @@ object ArmorSiphonBehavior { val before = siphon.Magazine val after = siphon.Magazine = before + 1 if (after > before) { - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( obj.Actor.toString, - VehicleAction.SendResponse(Service.defaultPlayerGUID, QuantityUpdateMessage(siphon.AmmoSlot.Box.GUID, after)) + SendResponse(QuantityUpdateMessage(siphon.AmmoSlot.Box.GUID, after)) ) if (after == siphon.MaxMagazine) { endSiphonRecharge(guid) diff --git a/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala b/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala index 84443d560..cd50540b2 100644 --- a/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala +++ b/src/main/scala/net/psforever/objects/equipment/JammingUnit.scala @@ -8,9 +8,9 @@ import net.psforever.objects.vehicles.MountedWeapons import net.psforever.objects.vital.interaction.DamageResult import net.psforever.objects.vital.projectile.ProjectileReason import net.psforever.objects.zones.{Zone, ZoneAware} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.PlanetsideAttribute import net.psforever.types.Vector3 -import net.psforever.services.Service -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import scala.collection.mutable import scala.concurrent.duration._ @@ -234,7 +234,6 @@ trait JammableBehavior { * @see `Service` * @see `VehicleAction` * @see `VehicleService` - * @see `VehicleServiceMessage` * @see `Zone.VehicleEvents` */ trait JammableMountedWeapons extends JammableBehavior { @@ -243,9 +242,9 @@ trait JammableMountedWeapons extends JammableBehavior { override def StartJammeredSound(target: Any, dur: Int): Unit = { target match { case obj: PlanetSideServerObject with MountedWeapons with JammableUnit if !jammedSound => - obj.Zone.VehicleEvents ! VehicleServiceMessage( + obj.Zone.VehicleEvents ! MessageEnvelope( obj.Zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 1) + PlanetsideAttribute(obj.GUID, 27, 1) ) super.StartJammeredSound(target, dur) case _ => ; @@ -264,9 +263,9 @@ trait JammableMountedWeapons extends JammableBehavior { override def CancelJammeredSound(target: Any): Unit = { target match { case obj: PlanetSideServerObject if jammedSound => - obj.Zone.VehicleEvents ! VehicleServiceMessage( + obj.Zone.VehicleEvents ! MessageEnvelope( obj.Zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 27, 0) + PlanetsideAttribute(obj.GUID, 27, 0) ) case _ => ; } @@ -308,9 +307,9 @@ object JammableMountedWeapons { def JammedWeaponStatus(zone: Zone, target: Equipment with JammableUnit, statusCode: Int): Unit = { target.Jammed = statusCode == 1 - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 27, statusCode) + PlanetsideAttribute(target.GUID, 27, statusCode) ) } } 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/locker/LockerContainerControl.scala b/src/main/scala/net/psforever/objects/locker/LockerContainerControl.scala index 67eab42f8..bef430721 100644 --- a/src/main/scala/net/psforever/objects/locker/LockerContainerControl.scala +++ b/src/main/scala/net/psforever/objects/locker/LockerContainerControl.scala @@ -6,8 +6,8 @@ import net.psforever.objects.equipment.Equipment import net.psforever.objects.serverobject.containable.{Containable, ContainableBehavior} import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent import net.psforever.packet.game.{ObjectAttachMessage, ObjectCreateDetailedMessage, ObjectDetachMessage} -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{ObjectDelete, SendResponse} import net.psforever.types.{PlanetSideEmpire, Vector3} /** @@ -19,7 +19,7 @@ import net.psforever.types.{PlanetSideEmpire, Vector3} class LockerContainerControl(locker: LockerContainer, toChannel: String) extends Actor with ContainableBehavior { - def ContainerObject = locker + def ContainerObject: LockerContainer = locker def receive: Receive = containerBehavior @@ -34,9 +34,9 @@ class LockerContainerControl(locker: LockerContainer, toChannel: String) val obj = ContainerObject obj.Find(item) match { case Some(slot) => - obj.Zone.AvatarEvents ! AvatarServiceMessage( + obj.Zone.AvatarEvents ! MessageEnvelope( toChannel, - AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectAttachMessage(obj.GUID, item.GUID, slot)) + SendResponse(ObjectAttachMessage(obj.GUID, item.GUID, slot)) ) case None => ; } @@ -46,17 +46,16 @@ class LockerContainerControl(locker: LockerContainer, toChannel: String) def RemoveItemFromSlotCallback(item: Equipment, slot: Int): Unit = { val zone = locker.Zone - zone.AvatarEvents ! AvatarServiceMessage(toChannel, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, item.GUID)) + zone.AvatarEvents ! MessageEnvelope(toChannel, ObjectDelete(item.GUID)) } def PutItemInSlotCallback(item: Equipment, slot: Int): Unit = { val zone = locker.Zone val definition = item.Definition item.Faction = PlanetSideEmpire.NEUTRAL - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( toChannel, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, + SendResponse( ObjectCreateDetailedMessage( definition.ObjectId, item.GUID, @@ -69,12 +68,9 @@ class LockerContainerControl(locker: LockerContainer, toChannel: String) def SwapItemCallback(item: Equipment, fromSlot: Int): Unit = { val zone = locker.Zone - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( toChannel, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - ObjectDetachMessage(locker.GUID, item.GUID, Vector3.Zero, 0f) - ) + SendResponse(ObjectDetachMessage(locker.GUID, item.GUID, Vector3.Zero, 0f)) ) } } diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableAmenity.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableAmenity.scala index b9369c38a..ec412e277 100644 --- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableAmenity.scala +++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableAmenity.scala @@ -3,7 +3,9 @@ package net.psforever.objects.serverobject.damage import net.psforever.objects.serverobject.structures.Amenity import net.psforever.objects.vital.interaction.DamageResult -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.packet.game.PlanetsideAttributeMessage +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse /** * The "control" `Actor` mixin for damage-handling code @@ -25,8 +27,7 @@ object DamageableAmenity { * A destroyed `Amenity` target dispatches two messages to chance its model and operational states. * The common manifestation is a sparking entity that will no longer report being accessible. * These `PlanetSideAttributeMessage` attributes are the same as reported during zone load client configuration. - * @see `AvatarAction.PlanetsideAttributeToAll` - * @see `AvatarServiceMessage` + * @see `PlanetsideAttribute` * @see `Zone.AvatarEvents` * @param target the entity being destroyed * @param cause historical information about the damage @@ -36,7 +37,9 @@ object DamageableAmenity { val zoneId = zone.id val events = zone.AvatarEvents val targetGUID = target.GUID - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 50, 1)) - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 51, 1)) + events ! MessageEnvelope( + zoneId, + SendResponse(PlanetsideAttributeMessage(targetGUID, 50, 1), PlanetsideAttributeMessage(targetGUID, 51, 1)) + ) } } diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableEntity.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableEntity.scala index e594de2db..1e5224f04 100644 --- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableEntity.scala +++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableEntity.scala @@ -1,14 +1,16 @@ //Copyright (c) 2020 PSForever package net.psforever.objects.serverobject.damage +import net.psforever.objects.Default import net.psforever.objects.equipment.JammableUnit import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.objects.vital.interaction.DamageResult import net.psforever.objects.vital.resolution.ResolutionCalculations import net.psforever.objects.zones.Zone import net.psforever.types.PlanetSideGUID -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.PlanetsideAttribute /** * The "control" `Actor` mixin for damage-handling code, @@ -140,12 +142,11 @@ object DamageableEntity { * - reports its adjusted its health; * - alert the activity monitor for that `Zone` about the damage; and, * - provide a feedback message regarding the damage. - * @see `AvatarAction.PlanetsideAttributeToAll` - * @see `AvatarAction.SendResponse` - * @see `AvatarServiceMessage` + * @see `PlanetsideAttribute` + * @see `SendResponse` * @see `DamageFeedbackMessage` * @see `JammableUnit.Jammered` - * @see `Service.defaultPlayerGUID` + * @see `Default.GUID0` * @see `Zone.Activity` * @see `Zone.AvatarEvents` * @see `Zone.HotSpot.Activity` @@ -165,9 +166,9 @@ object DamageableEntity { def DamageToHealth(target: Damageable.Target, cause: DamageResult, amount: Int): Boolean = { if (amount > 0 && !target.Destroyed) { val zone = target.Zone - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.PlanetsideAttributeToAll(target.GUID, 0, target.Health) + PlanetsideAttribute(target.GUID, 0, target.Health) ) true } @@ -181,8 +182,7 @@ object DamageableEntity { * - reports its adjusted its health; and, * - report about its destruction. * @see `AvatarAction.Destroy` - * @see `AvatarAction.PlanetsideAttribute` - * @see `AvatarServiceMessage` + * @see `PlanetsideAttribute` * @see `DamageFeedbackMessage` * @see `JammableUnit.ClearJammeredSound` * @see `JammableUnit.ClearJammeredStatus` @@ -199,12 +199,12 @@ object DamageableEntity { val zoneId = zone.id val tguid = target.GUID val attribution = attributionTo(cause, target.Zone) - zone.AvatarEvents ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 0, target.Health)) + zone.AvatarEvents ! MessageEnvelope(zoneId, PlanetsideAttribute(tguid, 0, target.Health)) if (target.isInstanceOf[SpawnTube]) {}//do nothing to prevent issue #1057 else { - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zoneId, - AvatarAction.Destroy(tguid, attribution, Service.defaultPlayerGUID, target.Position) + AvatarAction.Destroy(tguid, attribution, Default.GUID0, target.Position) ) } } diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala index 2356fb11b..90e859a9e 100644 --- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala +++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableMountable.scala @@ -1,13 +1,14 @@ //Copyright (c) 2020 PSForever package net.psforever.objects.serverobject.damage -import net.psforever.objects.Player +import net.psforever.objects.{Default, Player} import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.sourcing.{PlayerSource, SourceEntry} import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult} import net.psforever.packet.game.DamageWithPositionMessage -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.{HintsAtAttacker, SendResponse} /** * Functions to assist other damage-dealing code for objects that contain users. @@ -17,12 +18,11 @@ object DamageableMountable { /** * A damaged target alerts its occupants (as it is a `Mountable` object) of the source of the damage. * - * @see `AvatarAction.HitHint` - * @see `AvatarAction.SendResponse` - * @see `AvatarServiceMessage` + * @see `HitHint` + * @see `SendResponse` * @see `DamageWithPositionMessage` * @see `Mountable.Seats` - * @see `Service.defaultPlayerGUID` + * @see `Default.GUID0` * @see `Zone.AvatarEvents` * @see `Zone.LivePlayers` * @param target the entity being damaged @@ -37,32 +37,34 @@ object DamageableMountable { val zone = target.Zone val events = zone.AvatarEvents val occupants = target.Seats.values.toSeq.flatMap { seat => seat.occupants.filter(_.isAlive) } - ((cause.adversarial match { - case Some(adversarial) => Some(adversarial.attacker) - case None => None - }) match { - case Some(pSource: PlayerSource) => //player damage - val name = pSource.Name - (zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match { - case Some(player) => - AvatarAction.HitHint(player.GUID, player.GUID) - case None => - AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, pSource.Position)) - }) match { - case AvatarAction.HitHint(_, guid) => - occupants.map { tplayer => (tplayer.Name, AvatarAction.HitHint(guid, tplayer.GUID)) } - case msg => - occupants.map { tplayer => (tplayer.Name, msg) } - } - case Some(source) => //object damage - val msg = AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(countableDamage, source.Position)) - occupants.map { tplayer => (tplayer.Name, msg) } - case None => - List.empty - }).foreach { - case (channel, msg) => - events ! AvatarServiceMessage(channel, msg) - } + events ! BundledEnvelope( + ((cause.adversarial match { + case Some(adversarial) => Some(adversarial.attacker) + case None => None + }) match { + case Some(pSource: PlayerSource) => //player damage + val name = pSource.Name + (zone.LivePlayers.find(_.Name == name).orElse(zone.Corpses.find(_.Name == name)) match { + case Some(player) => + HintsAtAttacker(player.GUID) + case None => + SendResponse(DamageWithPositionMessage(countableDamage, pSource.Position)) + }) match { + case msg @ HintsAtAttacker(guid) => + occupants.map { tplayer => (tplayer.Name, guid, msg) } + case msg => + occupants.map { tplayer => (tplayer.Name, Default.GUID0, msg) } + } + case Some(source) => //object damage + val msg = SendResponse(DamageWithPositionMessage(countableDamage, source.Position)) + occupants.map { tplayer => (tplayer.Name, Default.GUID0, msg) } + case None => + List.empty + }).map { + case (channel, filter, msg) => + MessageEnvelope(channel, filter, msg) + } + ) } /** @@ -78,7 +80,7 @@ object DamageableMountable { val targets = target.Seats.values.flatMap(_.occupant).filter(_.isAlive) targets.foreach { player => //make llu visible to others in zone if passenger is carrying one - player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.DropSpecialItem()) + player.Zone.AvatarEvents ! MessageEnvelope(player.Name, AvatarAction.DropSpecialItem()) //player.LogActivity(cause) player.Actor ! Player.Die( DamageInteraction(interaction.resolution, SourceEntry(player), interaction.cause, interaction.hitPos) diff --git a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableVehicle.scala b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableVehicle.scala index 28cb07aae..4d609076a 100644 --- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableVehicle.scala +++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableVehicle.scala @@ -2,7 +2,7 @@ package net.psforever.objects.serverobject.damage import akka.actor.{Actor, Cancellable} -import net.psforever.objects.{Vehicle, Vehicles} +import net.psforever.objects.{Default, Vehicle, Vehicles} import net.psforever.objects.equipment.JammableUnit import net.psforever.objects.serverobject.damage.Damageable.Target import net.psforever.objects.sourcing.VehicleSource @@ -12,8 +12,8 @@ import net.psforever.objects.vital.resolution.ResolutionCalculations import net.psforever.objects.zones.Zone import net.psforever.objects.zones.exp.ToDatabase import net.psforever.packet.game.DamageWithPositionMessage -import net.psforever.services.Service -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} import net.psforever.types.Vector3 import scala.concurrent.duration._ @@ -102,10 +102,9 @@ trait DamageableVehicle * Most all vehicles and the weapons mounted to them can jam * if the projectile that strikes (near) them has jammering properties. * If this vehicle has shields that were affected by previous damage, that is also reported to the clients. - * @see `Service.defaultPlayerGUID` + * @see `Default.GUID0` * @see `Vehicle.CargoHolds` - * @see `VehicleAction.PlanetsideAttribute` - * @see `VehicleServiceMessage` + * @see `PlanetsideAttribute` * @param target the entity being destroyed * @param cause historical information about the damage * @param amount how much damage was performed @@ -142,27 +141,27 @@ trait DamageableVehicle } //stat changes if (damageToShields > 0) { - events ! VehicleServiceMessage( + events ! MessageEnvelope( shieldChannel, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, obj.Definition.shieldUiAttribute, obj.Shields) + PlanetsideAttribute(targetGUID, obj.Definition.shieldUiAttribute, obj.Shields) ) announceConfrontation = true } if (damageToHealth > 0) { - events ! VehicleServiceMessage( + events ! MessageEnvelope( healthChannel, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, obj.Health) + PlanetsideAttribute(targetGUID, 0, obj.Health) ) announceConfrontation = true } } if (announceConfrontation) { if (showAsAggravated) { - val msg = VehicleAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(totalDamage, Vector3.Zero)) + val msg = SendResponse(DamageWithPositionMessage(totalDamage, Vector3.Zero)) obj.Seats.values .collect { case seat if seat.occupant.nonEmpty => seat.occupant.get.Name } .foreach { channel => - events ! VehicleServiceMessage(channel, msg) + events ! MessageEnvelope(channel, msg) } } else { @@ -180,13 +179,11 @@ trait DamageableVehicle * Finally, the vehicle is tasked for deconstruction. * @see `Deployment.TryDeploymentChange` * @see `DriveState.Undeploying` - * @see `Service.defaultPlayerGUID` + * @see `Default.GUID0` * @see `Vehicle.CargoHolds` - * @see `VehicleAction.PlanetsideAttribute` + * @see `PlanetsideAttribute` * @see `RemoverActor.AddTask` * @see `RemoverActor.ClearSpecific` - * @see `VehicleServiceMessage` - * @see `VehicleServiceMessage.Decon` * @see `Zone.VehicleEvents` * @param target the entity being destroyed * @param cause historical information about the damage @@ -217,9 +214,9 @@ trait DamageableVehicle //shields if (obj.Shields > 0) { obj.Shields = 0 - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, obj.Definition.shieldUiAttribute, 0) + PlanetsideAttribute(target.GUID, obj.Definition.shieldUiAttribute, 0) ) } //database entry @@ -243,22 +240,22 @@ trait DamageableVehicle import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ val obj = DamageableObject - val guid0 = Service.defaultPlayerGUID + val guid0 = Default.GUID0 val zone = obj.Zone val zoneid = zone.id val events = zone.VehicleEvents //health to 1, shields to 0 obj.Health = 1 val guid = obj.GUID - events ! VehicleServiceMessage( + events ! MessageEnvelope( zoneid, - VehicleAction.PlanetsideAttribute(guid0, guid, 0, 1) + PlanetsideAttribute(guid, 0, 1) ) if (obj.Shields > 0) { obj.Shields = 0 - zone.VehicleEvents ! VehicleServiceMessage( + events ! MessageEnvelope( zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, obj.Definition.shieldUiAttribute, 0) + PlanetsideAttribute(obj.GUID, obj.Definition.shieldUiAttribute, 0) ) } //aggravation cancel 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 bef9a243a..250aa61c2 100644 --- a/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala +++ b/src/main/scala/net/psforever/objects/serverobject/damage/DamageableWeaponTurret.scala @@ -9,10 +9,10 @@ import net.psforever.objects.vital.interaction.DamageResult import net.psforever.objects.zones.Zone import net.psforever.packet.game.DamageWithPositionMessage import net.psforever.types.Vector3 -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.vehicle.support.TurretUpgrader -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.{ObjectDelete, PlanetsideAttribute, SendResponse} +import net.psforever.services.base.support.SupportActor +import net.psforever.services.vehicle.support.{TurretEnvelope, TurretUpgrader} /** * The "control" `Actor` mixin for damage-handling code for `WeaponTurret` objects. @@ -62,21 +62,22 @@ trait DamageableWeaponTurret //TODO some turrets have shields if (damageToHealth > 0) { DamageableMountable.DamageAwareness(DamageableObject, cause, damageToHealth) - events ! VehicleServiceMessage( + events ! MessageEnvelope( zoneId, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, targetGUID, 0, obj.Health) + PlanetsideAttribute(targetGUID, 0, obj.Health) ) announceConfrontation = true } } if (announceConfrontation) { if (aggravated) { - val msg = VehicleAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(damageToHealth, Vector3.Zero)) - obj.Seats.values + val msg = SendResponse(DamageWithPositionMessage(damageToHealth, Vector3.Zero)) + events ! BundledEnvelope(obj.Seats.values .collect { case seat if seat.occupant.nonEmpty => seat.occupant.get.Name } - .foreach { channel => - events ! VehicleServiceMessage(channel, msg) + .map { channel => + MessageEnvelope(channel, msg) } + ) } else { //activity on map @@ -103,16 +104,15 @@ object DamageableWeaponTurret { * A destroyed target dispatches a message to conceal (delete) its weapons from users. * If affected by a jammer property, the jammer propoerty will be removed. * If the type of entity is a `WeaponTurret`, the weapons are converted to their "normal" upgrade state. - * @see `AvatarAction.DeleteObject` - * @see `AvatarServiceMessage` + * @see `DeleteObject` * @see `MountedWeapons` * @see `MountedWeapons.Weapons` - * @see `Service.defaultPlayerGUID` + * @see `Default.GUID0` * @see `TurretUpgrade.None` * @see `TurretUpgrader.AddTask` * @see `TurretUpgrader.ClearSpecific` * @see `WeaponTurret` - * @see `VehicleServiceMessage.TurretUpgrade` + * @see `TurretMessage` * @see `Zone.AvatarEvents` * @see `Zone.VehicleEvents` * @param target the entity being destroyed; @@ -125,20 +125,18 @@ object DamageableWeaponTurret { val zone = target.Zone val zoneId = zone.id val avatarEvents = zone.AvatarEvents - target.Weapons.values - .filter { - _.Equipment.nonEmpty + avatarEvents ! BundledEnvelope(target.Weapons.values + .filter(_.Equipment.nonEmpty) + .map { slot => + MessageEnvelope(zoneId, ObjectDelete(slot.Equipment.get.GUID)) } - .foreach(slot => { - val wep = slot.Equipment.get - avatarEvents ! AvatarServiceMessage(zoneId, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, wep.GUID)) - }) + ) target match { case turret: WeaponTurret => if (turret.Upgrade != TurretUpgrade.None) { val vehicleEvents = zone.VehicleEvents - vehicleEvents ! VehicleServiceMessage.TurretUpgrade(TurretUpgrader.ClearSpecific(List(turret), zone)) - vehicleEvents ! VehicleServiceMessage.TurretUpgrade(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/deploy/DeploymentBehavior.scala b/src/main/scala/net/psforever/objects/serverobject/deploy/DeploymentBehavior.scala index 0eb8cf718..2544b2ded 100644 --- a/src/main/scala/net/psforever/objects/serverobject/deploy/DeploymentBehavior.scala +++ b/src/main/scala/net/psforever/objects/serverobject/deploy/DeploymentBehavior.scala @@ -4,8 +4,8 @@ package net.psforever.objects.serverobject.deploy import akka.actor.{Actor, ActorRef, Cancellable} import net.psforever.objects.Default import net.psforever.types.{DriveState, Vector3} -import net.psforever.services.Service -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.vehicle.VehicleAction import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ @@ -97,16 +97,15 @@ trait DeploymentBehavior { val guid = obj.GUID val zone = obj.Zone val zoneChannel = zone.id - val GUID0 = Service.defaultPlayerGUID //TODO remove this arbitrary allowance angle when no longer helpful if (obj.Orientation.x > 30 && obj.Orientation.x < 330) { obj.DeploymentState = prevState prevState } else if (state == DriveState.Deploying) { obj.Velocity = Some(Vector3.Zero) //no velocity - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zoneChannel, - VehicleAction.DeployRequest(GUID0, guid, state, 0, unk2=false, Vector3.Zero) + VehicleAction.DeployRequest(guid, state, 0, unk2=false, Vector3.Zero) ) deploymentTimer.cancel() deploymentTimer = context.system.scheduler.scheduleOnce(obj.DeployTime.milliseconds)({ @@ -115,9 +114,9 @@ trait DeploymentBehavior { state } else if (state == DriveState.Deployed) { obj.Velocity = Some(Vector3.Zero) //no velocity - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zoneChannel, - VehicleAction.DeployRequest(GUID0, guid, state, 0, unk2=false, Vector3.Zero) + VehicleAction.DeployRequest(guid, state, 0, unk2=false, Vector3.Zero) ) state } else { @@ -134,11 +133,10 @@ trait DeploymentBehavior { val guid = obj.GUID val zone = obj.Zone val zoneChannel = zone.id - val GUID0 = Service.defaultPlayerGUID if (state == DriveState.Undeploying) { - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zoneChannel, - VehicleAction.DeployRequest(GUID0, guid, state, 0, unk2=false, Vector3.Zero) + VehicleAction.DeployRequest(guid, state, 0, unk2=false, Vector3.Zero) ) import scala.concurrent.ExecutionContext.Implicits.global deploymentTimer.cancel() @@ -147,9 +145,9 @@ trait DeploymentBehavior { }) state } else if (state == DriveState.Mobile) { - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zoneChannel, - VehicleAction.DeployRequest(GUID0, guid, state, 0, unk2=false, Vector3.Zero) + VehicleAction.DeployRequest(guid, state, 0, unk2=false, Vector3.Zero) ) state } else { diff --git a/src/main/scala/net/psforever/objects/serverobject/dome/ForceDomeControl.scala b/src/main/scala/net/psforever/objects/serverobject/dome/ForceDomeControl.scala index 802a6ed31..56a18b455 100644 --- a/src/main/scala/net/psforever/objects/serverobject/dome/ForceDomeControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/dome/ForceDomeControl.scala @@ -14,8 +14,9 @@ import net.psforever.objects.vital.interaction.DamageInteraction import net.psforever.objects.vital.prop.DamageWithPosition import net.psforever.objects.zones.Zone import net.psforever.packet.game.ChatMsg -import net.psforever.services.Service -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.SendResponse +import net.psforever.services.local.LocalAction import net.psforever.types.{ChatMessageType, PlanetSideEmpire, PlanetSideGeneratorState, Vector3} import scala.annotation.unused @@ -45,9 +46,9 @@ object ForceDomeControl { val owner = dome.Owner val zone = owner.Zone owner.Actor ! BuildingActor.AmenityStateChange(dome) - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.UpdateForceDomeStatus(Service.defaultPlayerGUID, owner.GUID, activationState) + LocalAction.UpdateForceDomeStatus(owner.GUID, activationState) ) } @@ -124,11 +125,11 @@ object ForceDomeControl { state: Boolean ): Unit = { val zone = building.Zone - val message = LocalAction.SendResponse(ChatMsg( + val message = SendResponse(ChatMsg( ChatMessageType.UNK_229, s"The Capitol force dome at ${building.Name} will remain ${if (state) "activated" else "deactivated"}." )) - zone.LocalEvents ! LocalServiceMessage(zone.id, message) + zone.LocalEvents ! MessageEnvelope(zone.id, message) } /** @@ -138,13 +139,13 @@ object ForceDomeControl { */ def NormalDomeStateMessage(building: Building): Unit = { val events = building.Zone.LocalEvents - val message = LocalAction.SendResponse(ChatMsg( + val message = SendResponse(ChatMsg( ChatMessageType.UNK_227, "Expected capitol force dome state change will resume." )) - building.PlayersInSOI.foreach { player => - events ! LocalServiceMessage(player.Name, message) - } + events ! BundledEnvelope(building.PlayersInSOI.map { player => + MessageEnvelope(player.Name, message) + }) } /** diff --git a/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala b/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala index eb90ed91d..9d8d635ee 100644 --- a/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala +++ b/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala @@ -67,37 +67,10 @@ object Door { */ sealed trait Exchange - /** - * Message that carries the result of the processed request message back to the original user (`player`). - * @param player the player who sent this request message - * @param msg the original packet carrying the request - * @param response the result of the processed request - */ - final case class DoorMessage(player: Player, msg: UseItemMessage, response: Exchange) - - /** - * This door will open. - */ - final case class OpenEvent() extends Exchange - - /** - * This door will close. - */ - final case class CloseEvent() extends Exchange - - /** - * This door will do nothing. - */ - final case class NoEvent() extends Exchange - type LockingMechanismLogic = (PlanetSideServerObject, Door) => Boolean final case class UpdateMechanism(mechanism: LockingMechanismLogic) extends Exchange - case object Lock extends Exchange - - case object Unlock extends Exchange - /** * Overloaded constructor. * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields 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 1cd30f116..056966621 100644 --- a/src/main/scala/net/psforever/objects/serverobject/doors/DoorControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/doors/DoorControl.scala @@ -7,8 +7,10 @@ import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObjec import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} import net.psforever.objects.serverobject.locks.IFFLock import net.psforever.objects.serverobject.structures.PoweredAmenityControl -import net.psforever.services.Service -import net.psforever.services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse} +import net.psforever.services.base.envelope.GenericResponseEnvelope +import net.psforever.services.base.support.SupportActor +import net.psforever.services.local.support.{DoorCloseActor, DoorMessage} +import net.psforever.services.local.{LocalAction, LocalStamp} /** * An `Actor` that handles messages being dispatched to a specific `Door`. @@ -19,24 +21,21 @@ class DoorControl(door: Door) with FactionAffinityBehavior.Check { def FactionObject: FactionAffinity = door - private var isLocked: Boolean = false private var lockingMechanism: Door.LockingMechanismLogic = DoorControl.alwaysOpen def commonBehavior: Receive = checkBehavior .orElse { - case Door.Lock => - isLocked = true + case Door.UpdateMechanism(logic) => + lockingMechanism = logic if (door.isOpen) { val zone = door.Zone door.Open = None - zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.DoorSlamsShut(door)) + zone.LocalEvents ! DoorMessage( + zone.id, + LocalAction.DoorCloses(door.GUID), + SupportActor.ClearSpecific(List(door), zone) + ) } - - case Door.Unlock => - isLocked = false - - case Door.UpdateMechanism(logic) => - lockingMechanism = logic } def poweredStateLogic: Receive = @@ -48,7 +47,7 @@ class DoorControl(door: Door) case CommonMessages.Use(player, _) => testToOpenDoor(player, door, door.Definition.initialOpeningDistance, sender()) - case IFFLock.DoorOpenResponse(target: Player) if !isLocked => + case IFFLock.DoorOpenResponse(target: Player) => DoorControl.openDoor(target, door) case _ => () @@ -57,7 +56,7 @@ class DoorControl(door: Door) def unpoweredStateLogic: Receive = { commonBehavior .orElse { - case CommonMessages.Use(player, _) if !isLocked => + case CommonMessages.Use(player, _) => //without power, the door opens freely DoorControl.openDoor(player, door) @@ -83,7 +82,7 @@ class DoorControl(door: Door) ): Unit = { if ( Doors.testForSpecificTargetHoldingDoorOpen(player, door, maximumDistance * maximumDistance).contains(player) && - lockingMechanism(player, door) && !isLocked + lockingMechanism(player, door) ) { DoorControl.openDoor(player, door, replyTo) } @@ -107,20 +106,21 @@ object DoorControl { */ private def openDoor(player: Player, door: Door, replyTo: ActorRef = Default.Actor): Unit = { val zone = door.Zone - val doorGUID = door.GUID if (!door.isOpen) { //global open door.Open = player - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! DoorMessage( zone.id, - LocalAction.DoorOpens(Service.defaultPlayerGUID, zone, door) + LocalAction.DoorOpens(zone, door), + DoorCloseActor.DoorIsOpen(door, zone, System.currentTimeMillis()) ) } else { //the door should already open, but the requesting player does not see it as open - replyTo ! LocalServiceResponse( + replyTo ! GenericResponseEnvelope( + LocalStamp, player.Name, - Service.defaultPlayerGUID, - LocalResponse.DoorOpens(doorGUID) + Default.GUID0, + LocalAction.DoorOpens(door.Zone, door) ) } } diff --git a/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala b/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala index a7b0c78d5..223354272 100644 --- a/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/generator/GeneratorControl.scala @@ -12,9 +12,9 @@ import net.psforever.objects.serverobject.terminals.{GeneratorTerminalDefinition import net.psforever.objects.vital.interaction.DamageResult import net.psforever.objects.zones.Zone import net.psforever.packet.game.TriggerEffectMessage +import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.types.{PlanetSideGeneratorState, Vector3} -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.base.message.SendResponse import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits.global @@ -111,12 +111,9 @@ class GeneratorControl(gen: Generator) super.DestructionAwareness(gen, gen.LastDamage.get) GeneratorControl.UpdateOwner(gen, Some(GeneratorControl.Event.Destroyed)) //kaboom - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - TriggerEffectMessage(gen.GUID, "explosion_generator", None, None) - ) + SendResponse(TriggerEffectMessage(gen.GUID, "explosion_generator", None, None)) ) queuedExplosion.cancel() queuedExplosion = Default.Cancellable 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 b52f4afd6..bc29ce162 100644 --- a/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala +++ b/src/main/scala/net/psforever/objects/serverobject/hackable/GenericHackables.scala @@ -11,9 +11,10 @@ import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObjec import net.psforever.objects.zones.blockmap.BlockMapEntity import net.psforever.packet.game.{GenericObjectActionMessage, HackMessage, HackState, HackState1, HackState7, TriggeredSound} import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse +import net.psforever.services.local.support.{HackClearActor, HackClearEnvelope, HackEntityEnvelope} +import net.psforever.services.local.LocalAction import scala.annotation.unused import scala.util.{Failure, Success} @@ -88,10 +89,9 @@ object GenericHackables { } else { (HackState.Ongoing, progress.toInt) } - target.Zone.AvatarEvents ! AvatarServiceMessage( + target.Zone.AvatarEvents ! MessageEnvelope( hacker.Name, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, + SendResponse( HackMessage(progressType, target.GUID, hacker.GUID, progressGrade, 0L, progressState, HackState7.Unk8) ) ) @@ -171,14 +171,17 @@ object GenericHackables { val zoneId = zone.id val pguid = tplayer.GUID log.info(s"${user.Name} hacked a ${target.Definition.Name}") - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zoneId, - LocalAction.TriggerSound(pguid, target.HackSound, tplayer.Position, 30, 0.49803925f) + pguid, + LocalAction.TriggerSound(target.HackSound, tplayer.Position, 30, 0.49803925f) ) - zone.LocalEvents ! LocalServiceMessage( + val duration = target.HackEffectDuration(user.avatar.hackingSkillLevel()) + zone.LocalEvents ! HackEntityEnvelope( zoneId, - LocalAction - .HackTemporarily(pguid, zone, target, hackValue, hackClearValue, target.HackEffectDuration(user.avatar.hackingSkillLevel())) + pguid, + LocalAction.HackObject(target.GUID, hackValue, HackState7.Unk8), + HackClearActor.ObjectIsHacked(target, zone, hackClearValue, HackState7.Unk8, duration) ) case Failure(_) => log.warn(s"Hack message failed on target: ${target.Definition.Name}@${target.GUID.guid}") @@ -203,29 +206,19 @@ object GenericHackables { val currVirus = building.virusId building.virusId = 8 building.virusInstalledBy = None - zone.LocalEvents ! LocalServiceMessage( - zoneId, - LocalAction - .ClearTemporaryHack(pguid, target) - ) - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(target)) + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.SendResponse(GenericObjectActionMessage(target.GUID, 60)) + SendResponse(GenericObjectActionMessage(target.GUID, 60)) ) currVirus match { case 0L => building.HackableAmenities.filter(d => d.Definition == GlobalDefinitions.lock_external).foreach { iff => - zone.LocalEvents ! LocalServiceMessage( - zoneId, - LocalAction.ClearTemporaryHack(PlanetSideGUID(0), iff) - ) + zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(iff)) } case 4L => building.HackableAmenities.filter(d => d.Definition == GlobalDefinitions.order_terminal).foreach { term => - zone.LocalEvents ! LocalServiceMessage( - zoneId, - LocalAction.ClearTemporaryHack(PlanetSideGUID(0), term) - ) + zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(term)) } case _ => () } @@ -239,19 +232,13 @@ object GenericHackables { case 0L => if (virus != 0) { building.HackableAmenities.filter(d => d.Definition == GlobalDefinitions.lock_external).foreach { iff => - zone.LocalEvents ! LocalServiceMessage( - zoneId, - LocalAction.ClearTemporaryHack(PlanetSideGUID(0), 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 ! LocalServiceMessage( - zoneId, - LocalAction.ClearTemporaryHack(PlanetSideGUID(0), term) - ) + zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(term)) } } case _ => () @@ -274,42 +261,42 @@ object GenericHackables { val hackState = hackStateMap.getOrElse(virus, HackState7.Unk8) building.virusId = virus building.virusInstalledBy = Some(tplayer.Faction.id) - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zoneId, - LocalAction.TriggerSound(pguid, TriggeredSound.TREKSuccessful, tplayer.Position, 30, 0.49803925f) + pguid, + LocalAction.TriggerSound(TriggeredSound.TREKSuccessful, tplayer.Position, 30, 0.49803925f) ) - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! HackEntityEnvelope( zoneId, - LocalAction - .HackTemporarily(pguid, zone, target, installedVirusDuration, hackClearValue, installedVirusDuration, unk2=hackState) + pguid, + LocalAction.HackObject(target.GUID, installedVirusDuration.toLong, hackState), + HackClearActor.ObjectIsHacked(target, zone, hackClearValue, hackState, installedVirusDuration) ) - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.SendResponse(GenericObjectActionMessage(target.GUID, 61)) - ) - zone.LocalEvents ! LocalServiceMessage( - zone.id, - LocalAction.SendResponse(GenericObjectActionMessage(target.GUID, 58)) + SendResponse(GenericObjectActionMessage(target.GUID, 61), GenericObjectActionMessage(target.GUID, 58)) ) //amenities if applicable virus match { case 0L => building.HackableAmenities.filter(d => d.Definition == GlobalDefinitions.lock_external).foreach{ iff => - var setHacked = iff.asInstanceOf[PlanetSideServerObject with Hackable] - setHacked.HackedBy = tplayer - zone.LocalEvents ! LocalServiceMessage( - zoneId, - LocalAction.HackTemporarily(pguid, zone, iff, hackValue, hackClearValue, installedVirusDuration) - ) + iff.HackedBy = tplayer + zone.LocalEvents ! HackEntityEnvelope( + zoneId, + pguid, + LocalAction.HackObject(target.GUID, hackValue.toLong, HackState7.Unk8), + HackClearActor.ObjectIsHacked(target, zone, hackClearValue, HackState7.Unk8, installedVirusDuration) + ) } case 4L => building.HackableAmenities.filter(d => d.Definition == GlobalDefinitions.order_terminal).foreach{ term => - var setHacked = term.asInstanceOf[PlanetSideServerObject with Hackable] - setHacked.HackedBy = tplayer - zone.LocalEvents ! LocalServiceMessage( - zoneId, - LocalAction.HackTemporarily(pguid, zone, term, hackValue, hackClearValue, installedVirusDuration) - ) + term.HackedBy = tplayer + zone.LocalEvents ! HackEntityEnvelope( + zoneId, + pguid, + LocalAction.HackObject(term.GUID, hackValue.toLong, HackState7.Unk8), + HackClearActor.ObjectIsHacked(target, zone, hackClearValue, HackState7.Unk8, installedVirusDuration) + ) } case _ => () } 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 4e10f1fba..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.Service -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.local.support.{HackClearActor, HackClearEnvelope} object IFFLocks { @@ -14,9 +13,6 @@ object IFFLocks { */ def FinishResecuringIFFLock(lock: IFFLock)(): Unit = { val zone = lock.Zone - lock.Zone.LocalEvents ! LocalServiceMessage( - zone.id, - LocalAction.ClearTemporaryHack(Service.defaultPlayerGUID, lock) - ) + lock.Zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(lock)) } } diff --git a/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala b/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala index 29bd0f511..21a78ed9b 100644 --- a/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnControl.scala @@ -11,6 +11,7 @@ import net.psforever.objects.sourcing.AmenitySource import net.psforever.objects.vital.TerminalUsedActivity import net.psforever.objects.zones.{Zone, ZoneAware, Zoning} import net.psforever.objects.{Default, PlanetSideGameObject, Player, Vehicle} +import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.types.{PlanetSideGUID, TransactionType, Vector3} import scala.annotation.tailrec @@ -128,7 +129,7 @@ class VehicleSpawnControl(pad: VehicleSpawnPad) } trackedOrder = None handleOrderFunc = NewTasking - pad.Zone.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad) //cautious animation reset + pad.Zone.VehicleEvents ! MessageEnvelope(pad.Zone.id, VehicleSpawnPad.ResetSpawnPad(pad)) //cautious animation reset self ! akka.actor.Kill //should cause the actor to restart, which will abort any trapped messages case _ => () @@ -164,19 +165,17 @@ class VehicleSpawnControl(pad: VehicleSpawnPad) //first queued order orders = List(order) queueManagementTask() - pad.Zone.VehicleEvents ! VehicleSpawnPad.PeriodicReminder( + pad.Zone.VehicleEvents ! MessageEnvelope( name, - VehicleSpawnPad.Reminders.Queue, - Some(s"@SVCP_PositionInQueue^2~^2~") + VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Queue, Some(s"@SVCP_PositionInQueue^2~^2~")) ) case -1 => //new order orders = orders :+ order val size = orders.size + 1 - pad.Zone.VehicleEvents ! VehicleSpawnPad.PeriodicReminder( + pad.Zone.VehicleEvents ! MessageEnvelope( name, - VehicleSpawnPad.Reminders.Queue, - Some(s"@SVCP_PositionInQueue^$size~^$size~") + VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Queue, Some(s"@SVCP_PositionInQueue^$size~^$size~")) ) case n if orders(n).vehicle.Definition ne order.vehicle.Definition => //replace existing order with new order @@ -185,10 +184,9 @@ class VehicleSpawnControl(pad: VehicleSpawnPad) val originalVehicle = originalOrder.vehicle.Definition.Name orders = (orders.take(n) :+ order) ++ orders.drop(n+1) VehicleSpawnControl.DisposeVehicle(originalOrder.vehicle, zone) - zone.VehicleEvents ! VehicleSpawnPad.PeriodicReminder( + zone.VehicleEvents ! MessageEnvelope( name, - VehicleSpawnPad.Reminders.Queue, - Some(s"@SVCP_ReplacedVehicleWithVehicle^@$originalVehicle~^@${order.vehicle.Definition.Name}~") + VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Queue, Some(s"@SVCP_ReplacedVehicleWithVehicle^@$originalVehicle~^@${order.vehicle.Definition.Name}~")) ) case _ => //order is the duplicate of an existing order; do nothing to the queue @@ -245,10 +243,9 @@ class VehicleSpawnControl(pad: VehicleSpawnPad) val newOrder = VehicleSpawnControl.Order(driver, vehicle) recursiveOrderReminder(orders.iterator, size) trace(s"processing next order - a ${vehicle.Definition.Name} for $name") - pad.Zone.VehicleEvents ! VehicleSpawnPad.PeriodicReminder( + pad.Zone.VehicleEvents ! MessageEnvelope( name, - VehicleSpawnPad.Reminders.Queue, - Some(s"@SVCP_PositionInQueue^1~^$size~") + VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Queue, Some(s"@SVCP_PositionInQueue^1~^$size~")) ) trackedOrder = Some(newOrder) //guard on context.system.scheduler.scheduleOnce(2000 milliseconds, concealPlayer, newOrder) @@ -324,7 +321,10 @@ class VehicleSpawnControl(pad: VehicleSpawnPad) private def CancelOrder(vehicle: Vehicle, player: Player, msg: Option[String]): Unit = { if (vehicle.Seats.values.count(_.isOccupied) == 0) { VehicleSpawnControl.DisposeSpawnedVehicle(vehicle, player, pad.Zone) - pad.Zone.VehicleEvents ! VehicleSpawnPad.PeriodicReminder(player.Name, VehicleSpawnPad.Reminders.Cancelled, msg) + pad.Zone.VehicleEvents ! MessageEnvelope( + player.Name, + VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Cancelled, msg) + ) } } @@ -451,10 +451,9 @@ class VehicleSpawnControl(pad: VehicleSpawnPad) entry: VehicleSpawnPad.VehicleOrder, cause: Option[Any] ): Unit = { - pad.Zone.VehicleEvents ! VehicleSpawnPad.PeriodicReminder( + pad.Zone.VehicleEvents ! MessageEnvelope( entry.player.Name, - VehicleSpawnPad.Reminders.Blocked, - cause + VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Blocked, cause) ) } @@ -465,10 +464,9 @@ class VehicleSpawnControl(pad: VehicleSpawnPad) ): Unit = { if (iter.hasNext) { val recipient = iter.next() - pad.Zone.VehicleEvents ! VehicleSpawnPad.PeriodicReminder( + pad.Zone.VehicleEvents ! MessageEnvelope( recipient.player.Name, - VehicleSpawnPad.Reminders.Queue, - Some(s"@SVCP_PositionInQueue^$position~^$size~") + VehicleSpawnPad.PeriodicReminder(VehicleSpawnPad.Reminders.Queue, Some(s"@SVCP_PositionInQueue^$position~^$size~")) ) recursiveOrderReminder(iter, size, position + 1) } @@ -564,7 +562,7 @@ object VehicleSpawnControl { */ private def DisposeSpawnedVehicle(vehicle: Vehicle, player: Player, zone: Zone): Unit = { DisposeVehicle(vehicle, zone) - zone.VehicleEvents ! VehicleSpawnPad.RevealPlayer(player.GUID) + zone.VehicleEvents ! MessageEnvelope(zone.id, VehicleSpawnPad.RevealPlayer(player.GUID)) } /** diff --git a/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala b/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala index 4b9bb47c4..340cb235d 100644 --- a/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala +++ b/src/main/scala/net/psforever/objects/serverobject/pad/VehicleSpawnPad.scala @@ -5,6 +5,7 @@ import net.psforever.objects.serverobject.interior.Sidedness import net.psforever.objects.{Player, Vehicle} import net.psforever.objects.serverobject.structures.Amenity import net.psforever.objects.serverobject.terminals.Terminal +import net.psforever.services.base.message.SelfRespondingEvent import net.psforever.types.PlanetSideGUID /** @@ -25,7 +26,6 @@ class VehicleSpawnPad(spDef: VehicleSpawnPadDefinition) extends Amenity { } object VehicleSpawnPad { - /** * Message to the spawn pad to enqueue the following vehicle order. * This is the entry point to vehicle spawn pad functionality. @@ -39,14 +39,14 @@ object VehicleSpawnPad { * @see `GenericObjectActionMessage` * @param player_guid the player */ - final case class ConcealPlayer(player_guid: PlanetSideGUID) + final case class ConcealPlayer(player_guid: PlanetSideGUID) extends SelfRespondingEvent /** * Message is intended to undo the effects of the above message, `ConcealPlayer`. * @see `ConcealPlayer` * @param player_guid the player */ - final case class RevealPlayer(player_guid: PlanetSideGUID) + final case class RevealPlayer(player_guid: PlanetSideGUID) extends SelfRespondingEvent /** * Message to attach the vehicle to the spawn pad's lifting platform ("put on rails"). @@ -55,7 +55,7 @@ object VehicleSpawnPad { * @param vehicle the vehicle being spawned * @param pad the spawn pad */ - final case class AttachToRails(vehicle: Vehicle, pad: VehicleSpawnPad) + final case class AttachToRails(vehicle: Vehicle, pad: VehicleSpawnPad) extends SelfRespondingEvent /** * Message to detach the vehicle from the spawn pad's lifting platform ("put on rails"). @@ -63,72 +63,64 @@ object VehicleSpawnPad { * @param vehicle the vehicle being spawned * @param pad the spawn pad */ - final case class DetachFromRails(vehicle: Vehicle, pad: VehicleSpawnPad) + final case class DetachFromRails(vehicle: Vehicle, pad: VehicleSpawnPad) extends SelfRespondingEvent /** * Message that resets the spawn pad for its next order fulfillment operation by lowering the lifting platform. * @see `GenericObjectActionMessage` * @param pad the spawn pad */ - final case class ResetSpawnPad(pad: VehicleSpawnPad) + final case class ResetSpawnPad(pad: VehicleSpawnPad) extends SelfRespondingEvent /** * Message that acts as callback to the driver that the process of sitting in the driver mount will be initiated soon. * This information should only be communicated to the driver's client only. - * @param driver_name the person who will drive the vehicle * @param vehicle the vehicle being spawned * @param pad the spawn pad */ - final case class StartPlayerSeatedInVehicle(driver_name: String, vehicle: Vehicle, pad: VehicleSpawnPad) + final case class StartPlayerSeatedInVehicle(vehicle: Vehicle, pad: VehicleSpawnPad) extends SelfRespondingEvent /** * Message that acts as callback to the driver that the process of sitting in the driver mount should be finished. * This information should only be communicated to the driver's client only. - * @param driver_name the person who will drive the vehicle * @param vehicle the vehicle being spawned * @param pad the spawn pad */ - final case class PlayerSeatedInVehicle( - driver_name: String, - vehicle: Vehicle, - pad: VehicleSpawnPad - ) //TODO while using fake rails + //TODO while using fake rails (later edit: what does this mean?) + final case class PlayerSeatedInVehicle(vehicle: Vehicle, pad: VehicleSpawnPad) extends SelfRespondingEvent /** * Message that starts the newly-spawned vehicle to begin driving away from the spawn pad. * Information about the driving process is available on the vehicle itself. * This information should only be communicated to the driver's client only. * @see `VehicleDefinition` - * @param driver_name the person who will drive the vehicle * @param vehicle the vehicle * @param pad the spawn pad */ - final case class ServerVehicleOverrideStart(driver_name: String, vehicle: Vehicle, pad: VehicleSpawnPad) + final case class ServerVehicleOverrideStart(vehicle: Vehicle, pad: VehicleSpawnPad) extends SelfRespondingEvent /** * Message that transitions the newly-spawned vehicle into a cancellable auto-drive state. * Information about the driving process is available on the vehicle itself. * This information should only be communicated to the driver's client only. * @see `VehicleDefinition` - * @param driver_name the person who will drive the vehicle * @param vehicle the vehicle * @param pad the spawn pad */ - final case class ServerVehicleOverrideEnd(driver_name: String, vehicle: Vehicle, pad: VehicleSpawnPad) + final case class ServerVehicleOverrideEnd(vehicle: Vehicle, pad: VehicleSpawnPad) extends SelfRespondingEvent /** * Message to initiate the process of properly disposing of the vehicle that may have been or was spawned into the game world. * @param vehicle the vehicle */ - final case class DisposeVehicle(vehicle: Vehicle) + final case class DisposeVehicle(vehicle: Vehicle) extends SelfRespondingEvent /** * Message to send targeted messages to the clients of specific users. - * @param driver_name the person who will drive the vehicle * @param reason the nature of the message * @param data optional information for rendering the message to the client */ - final case class PeriodicReminder(driver_name: String, reason: Reminders.Value, data: Option[Any] = None) + final case class PeriodicReminder(reason: Reminders.Value, data: Option[Any] = None) extends SelfRespondingEvent /** * An `Enumeration` of reasons for sending a periodic reminder to the user. diff --git a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlConcealPlayer.scala b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlConcealPlayer.scala index b035a7849..dc8391395 100644 --- a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlConcealPlayer.scala +++ b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlConcealPlayer.scala @@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.pad.process import akka.actor.Props import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} +import net.psforever.services.base.envelope.MessageEnvelope import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ @@ -28,7 +29,7 @@ class VehicleSpawnControlConcealPlayer(pad: VehicleSpawnPad) extends VehicleSpaw case order @ VehicleSpawnControl.Order(driver, vehicle) => if (VehicleSpawnControl.validateOrderCredentials(pad, driver, vehicle).isEmpty) { trace(s"hiding ${driver.Name}") - pad.Zone.VehicleEvents ! VehicleSpawnPad.ConcealPlayer(driver.GUID) + pad.Zone.VehicleEvents ! MessageEnvelope(pad.Zone.id, VehicleSpawnPad.ConcealPlayer(driver.GUID)) context.system.scheduler.scheduleOnce(2000 milliseconds, loadVehicle, order) } else { trace(s"integral component lost; abort order fulfillment") diff --git a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlDriverControl.scala b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlDriverControl.scala index dbef9a3c8..8632a10f9 100644 --- a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlDriverControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlDriverControl.scala @@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.pad.process import akka.actor.Props import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} +import net.psforever.services.base.envelope.MessageEnvelope /** * An `Actor` that handles vehicle spawning orders for a `VehicleSpawnPad`. @@ -24,7 +25,7 @@ class VehicleSpawnControlDriverControl(pad: VehicleSpawnPad) extends VehicleSpaw case order @ VehicleSpawnControl.Order(driver, vehicle) => trace(s"returning control of ${vehicle.Definition.Name} to its current driver") if (vehicle.PassengerInSeat(driver).nonEmpty) { - pad.Zone.VehicleEvents ! VehicleSpawnPad.ServerVehicleOverrideEnd(driver.Name, vehicle, pad) + pad.Zone.VehicleEvents ! MessageEnvelope(driver.Name, VehicleSpawnPad.ServerVehicleOverrideEnd(vehicle, pad)) } finalClear ! order diff --git a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlFinalClearance.scala b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlFinalClearance.scala index 67c8d6e61..d67eff488 100644 --- a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlFinalClearance.scala +++ b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlFinalClearance.scala @@ -4,8 +4,9 @@ package net.psforever.objects.serverobject.pad.process import akka.actor.Cancellable import net.psforever.objects.Default import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} -import net.psforever.types.{PlanetSideGUID, Vector3} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.types.Vector3 +import net.psforever.services.vehicle.VehicleAction import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ @@ -35,15 +36,9 @@ class VehicleSpawnControlFinalClearance(pad: VehicleSpawnPad) extends VehicleSpa //ensure the vacant vehicle is above the trench and the doors vehicle.Position = pad.Position + Vector3.z(pad.Definition.VehicleCreationZOffset) val definition = vehicle.Definition - pad.Zone.VehicleEvents ! VehicleServiceMessage( + pad.Zone.VehicleEvents ! MessageEnvelope( s"${pad.Continent}", - VehicleAction.LoadVehicle( - PlanetSideGUID(0), - vehicle, - definition.ObjectId, - vehicle.GUID, - definition.Packet.ConstructorData(vehicle).get - ) + VehicleAction.LoadVehicle(vehicle, definition.ObjectId, vehicle.GUID, definition.Packet.ConstructorData(vehicle).get) ) } context.parent ! VehicleSpawnControl.ProcessControl.Reminder @@ -70,7 +65,7 @@ class VehicleSpawnControlFinalClearance(pad: VehicleSpawnPad) extends VehicleSpa } case VehicleSpawnControlFinalClearance.NextOrder => - pad.Zone.VehicleEvents ! VehicleSpawnPad.ResetSpawnPad(pad) + pad.Zone.VehicleEvents ! MessageEnvelope(pad.Zone.id, VehicleSpawnPad.ResetSpawnPad(pad)) context.parent ! VehicleSpawnControl.ProcessControl.GetNewOrder case _ => () diff --git a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala index 7bbb1123c..3460b4fd8 100644 --- a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala +++ b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlLoadVehicle.scala @@ -7,8 +7,8 @@ import akka.util.Timeout import net.psforever.objects.GlobalDefinitions import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} import net.psforever.objects.zones.Zone -import net.psforever.services.Service -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.vehicle.VehicleAction import net.psforever.types.Vector3 import net.psforever.zones.Zones @@ -68,9 +68,9 @@ class VehicleSpawnControlLoadVehicle(pad: VehicleSpawnPad) extends VehicleSpawnC val vtype = definition.ObjectId val vguid = v.GUID val vdata = definition.Packet.ConstructorData(v).get - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.LoadVehicle(Service.defaultPlayerGUID, v, vtype, vguid, vdata) + VehicleAction.LoadVehicle(v, vtype, vguid, vdata) ) railJack ! temp.get temp = None diff --git a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlRailJack.scala b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlRailJack.scala index c7740c7da..4dda6e120 100644 --- a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlRailJack.scala +++ b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlRailJack.scala @@ -11,6 +11,7 @@ import net.psforever.objects.vital.interaction.{DamageInteraction, DamageResult} import net.psforever.objects.vital.prop.DamageProperties import net.psforever.objects.vital.Vitality import net.psforever.objects.zones.Zone +import net.psforever.services.base.envelope.MessageEnvelope import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ @@ -40,7 +41,7 @@ class VehicleSpawnControlRailJack(pad: VehicleSpawnPad) extends VehicleSpawnCont pad.Definition.killBox(pad, vehicle.Definition.CanFly), Zone.findAllTargets ) - pad.Zone.VehicleEvents ! VehicleSpawnPad.AttachToRails(vehicle, pad) + pad.Zone.VehicleEvents ! MessageEnvelope(pad.Zone.id, VehicleSpawnPad.AttachToRails(vehicle, pad)) context.system.scheduler.scheduleOnce(10 milliseconds, seatDriver, order) case msg @ (VehicleSpawnControl.ProcessControl.Reminder | VehicleSpawnControl.ProcessControl.GetNewOrder) => diff --git a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala index 520d0c1ec..6c7218b17 100644 --- a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala +++ b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlSeatDriver.scala @@ -4,6 +4,7 @@ package net.psforever.objects.serverobject.pad.process import akka.actor.{ActorRef, Props} import net.psforever.objects.{Default, Vehicle} import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} +import net.psforever.services.base.envelope.MessageEnvelope import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ @@ -46,7 +47,7 @@ class VehicleSpawnControlSeatDriver(pad: VehicleSpawnPad) extends VehicleSpawnCo vehicle.Actor ! Vehicle.Deconstruct(Some(pad.Definition.VehicleCreationDeconstructTime.seconds)) if (VehicleSpawnControl.validateOrderCredentials(pad, driver, vehicle).isEmpty) { trace("driver to be made seated in vehicle") - pad.Zone.VehicleEvents ! VehicleSpawnPad.StartPlayerSeatedInVehicle(driver.Name, vehicle, pad) + pad.Zone.VehicleEvents ! MessageEnvelope(driver.Name, VehicleSpawnPad.StartPlayerSeatedInVehicle(vehicle, pad)) } else { trace("driver lost; vehicle stranded on pad") } @@ -58,7 +59,7 @@ class VehicleSpawnControlSeatDriver(pad: VehicleSpawnPad) extends VehicleSpawnCo if (VehicleSpawnControl.validateOrderCredentials(pad, driver, vehicle).isEmpty && entry.vehicle.PassengerInSeat(entry.driver).contains(0)) { trace(s"driver ${entry.driver.Name} has taken the wheel") - pad.Zone.VehicleEvents ! VehicleSpawnPad.PlayerSeatedInVehicle(entry.driver.Name, entry.vehicle, pad) + pad.Zone.VehicleEvents ! MessageEnvelope(entry.driver.Name, VehicleSpawnPad.PlayerSeatedInVehicle(entry.vehicle, pad)) } else { trace("driver lost, but operations can continue") } diff --git a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlServerVehicleOverride.scala b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlServerVehicleOverride.scala index bbec75af4..496d06938 100644 --- a/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlServerVehicleOverride.scala +++ b/src/main/scala/net/psforever/objects/serverobject/pad/process/VehicleSpawnControlServerVehicleOverride.scala @@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.pad.process import akka.actor.Props import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad} +import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.types.Vector3 import scala.concurrent.ExecutionContext.Implicits.global @@ -31,18 +32,18 @@ class VehicleSpawnControlServerVehicleOverride(pad: VehicleSpawnPad) extends Veh val driverFailState = !driver.isAlive || driver.Continent != pad.Continent || !vehicle.PassengerInSeat(driver).contains(0) vehicle.MountedIn = None - pad.Zone.VehicleEvents ! VehicleSpawnPad.DetachFromRails(vehicle, pad) + pad.Zone.VehicleEvents ! MessageEnvelope(pad.Zone.id, VehicleSpawnPad.DetachFromRails(vehicle, pad)) if (vehicleFailState || driverFailState) { if (vehicleFailState) { trace(s"vehicle was already destroyed") } else { trace(s"driver is not ready") } - pad.Zone.VehicleEvents ! VehicleSpawnPad.RevealPlayer(order.DriverGUID) + pad.Zone.VehicleEvents ! MessageEnvelope(pad.Zone.id, VehicleSpawnPad.RevealPlayer(order.DriverGUID)) driverControl ! order } else { trace(s"telling ${driver.Name} that the server is assuming control of the ${vehicle.Definition.Name}") - pad.Zone.VehicleEvents ! VehicleSpawnPad.ServerVehicleOverrideStart(driver.Name, vehicle, pad) + pad.Zone.VehicleEvents ! MessageEnvelope(driver.Name, VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, pad)) context.system.scheduler.scheduleOnce(4000 milliseconds, driverControl, order) } diff --git a/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala b/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala index 4e673d621..b246a6edb 100644 --- a/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/painbox/PainboxControl.scala @@ -59,7 +59,7 @@ class PainboxControl(painbox: Painbox) extends PoweredAmenityControl { } var commonBehavior: Receive = { - case Service.Startup() => + case Service.Startup => if (!disabled && domain.midpoint == Vector3.Zero) { initialStartup() } diff --git a/src/main/scala/net/psforever/objects/serverobject/repair/RepairableAmenity.scala b/src/main/scala/net/psforever/objects/serverobject/repair/RepairableAmenity.scala index 1042acfe4..88b8cbe25 100644 --- a/src/main/scala/net/psforever/objects/serverobject/repair/RepairableAmenity.scala +++ b/src/main/scala/net/psforever/objects/serverobject/repair/RepairableAmenity.scala @@ -5,7 +5,9 @@ import net.psforever.objects.Tool import net.psforever.objects.serverobject.structures.Amenity import net.psforever.objects.sourcing.{SourceEntry, SourceWithHealthEntry} import net.psforever.objects.vital.{DamagingActivity, RepairFromEquipment, SpawningActivity} -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.packet.game.PlanetsideAttributeMessage +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse /** * The "control" `Actor` mixin for repair-handling code @@ -27,8 +29,7 @@ object RepairableAmenity { /** * A restored `Amenity` target dispatches two messages to chance its model and operational states. * These `PlanetSideAttributeMessage` attributes are the same as reported during zone load client configuration. - * @see `AvatarAction.PlanetsideAttributeToAll` - * @see `AvatarServiceMessage` + * @see `PlanetsideAttribute` * @see `Zone.AvatarEvents` * @param target the entity being destroyed */ @@ -37,8 +38,10 @@ object RepairableAmenity { val zoneId = zone.id val events = zone.AvatarEvents val targetGUID = target.GUID - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 50, 0)) - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(targetGUID, 51, 0)) + events ! MessageEnvelope( + zoneId, + SendResponse(PlanetsideAttributeMessage(targetGUID, 50, 0), PlanetsideAttributeMessage(targetGUID, 51, 0)) + ) RestorationOfHistory(target) } diff --git a/src/main/scala/net/psforever/objects/serverobject/repair/RepairableEntity.scala b/src/main/scala/net/psforever/objects/serverobject/repair/RepairableEntity.scala index 232a3c8c5..53130db81 100644 --- a/src/main/scala/net/psforever/objects/serverobject/repair/RepairableEntity.scala +++ b/src/main/scala/net/psforever/objects/serverobject/repair/RepairableEntity.scala @@ -8,8 +8,8 @@ import net.psforever.objects.vital.RepairFromEquipment import net.psforever.objects.{Player, Tool} import net.psforever.packet.game.{ChatMsg, InventoryStateMessage, RepairMessage} import net.psforever.types.{ChatMessageType, PlanetSideEmpire, Vector3} -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} /** * The "control" `Actor` mixin for repair-handling code, @@ -67,13 +67,13 @@ trait RepairableEntity extends Repairable { * Calculate the health points change and enact that repair action if the targets are stationary. * Restore the target entity to a not destroyed state if applicable. * Always show the repair progress bar window by using the appropriate packet. - * @see `AvatarAction.PlanetsideAttributeToAll` - * @see `AvatarAction.SendResponse` + * @see `PlanetsideAttribute` + * @see `SendResponse` * @see `AvatarService` * @see `InventoryStateMessage` * @see `PlanetSideGameObject.isMoving` * @see `RepairMessage` - * @see `Service.defaultPlayerGUID` + * @see `Default.GUID0` * @see `Tool.Discharge` * @see `Zone.AvatarEvents` * @param target the entity being repaired @@ -89,12 +89,9 @@ trait RepairableEntity extends Repairable { if (!(player.isMoving(test = 1f) || target.isMoving(test = 1f))) { //only allow stationary repairs within margin of error val repairValue = Repairable.applyLevelModifier(player, item, RepairToolValue(item)).toInt + target.Definition.RepairMod val magazine = item.Discharge() - events ! AvatarServiceMessage( + events ! MessageEnvelope( player.Name, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong) - ) + SendResponse(InventoryStateMessage(item.AmmoSlot.Box.GUID, item.GUID, magazine.toLong)) ) target.LogActivity( RepairFromEquipment( @@ -108,12 +105,9 @@ trait RepairableEntity extends Repairable { originalHealth } //progress bar remains visible - events ! AvatarServiceMessage( + events ! MessageEnvelope( name, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - RepairMessage(target.GUID, updatedHealth * 100 / definition.MaxHealth) - ) + SendResponse(RepairMessage(target.GUID, updatedHealth * 100 / definition.MaxHealth)) ) //if vehicle and vehicle is owned by another player, send repair chat message to the vehicle's owner if (target.Zone.Vehicles.exists(_.GUID == target.GUID)) { @@ -146,11 +140,11 @@ trait RepairableEntity extends Repairable { val newHealth = target.Health = target.Health + amount if (target.Destroyed) { if (newHealth >= target.Definition.RepairRestoresAt) { - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 0, newHealth)) + events ! MessageEnvelope(zoneId, PlanetsideAttribute(tguid, 0, newHealth)) Restoration(target) } } else { - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 0, newHealth)) + events ! MessageEnvelope(zoneId, PlanetsideAttribute(tguid, 0, newHealth)) } newHealth } diff --git a/src/main/scala/net/psforever/objects/serverobject/repair/RepairableWeaponTurret.scala b/src/main/scala/net/psforever/objects/serverobject/repair/RepairableWeaponTurret.scala index f7f5fd693..5c716411f 100644 --- a/src/main/scala/net/psforever/objects/serverobject/repair/RepairableWeaponTurret.scala +++ b/src/main/scala/net/psforever/objects/serverobject/repair/RepairableWeaponTurret.scala @@ -5,8 +5,8 @@ import net.psforever.objects.Tool import net.psforever.objects.equipment.EquipmentSlot import net.psforever.objects.serverobject.turret.WeaponTurret import net.psforever.objects.vehicles.MountedWeapons -import net.psforever.services.Service -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.vehicle.VehicleAction /** * The "control" `Actor` mixin for repair-handling code for `WeaponTurret` objects. @@ -28,10 +28,9 @@ object RepairableWeaponTurret { * and may have been concealed/deleted when the target was destroyed. * @see `MountedWeapons` * @see `MountedWeapons.Weapons` - * @see `Service.defaultPlayerGUID` + * @see `Default.GUID0` * @see `WeaponTurret` - * @see `VehicleAction.EquipmentInSlot` - * @see `VehicleServiceMessage` + * @see `EquipmentInSlot` * @see `Zone.VehicleEvents` * @param target the entity being destroyed; * note: `MountedWeapons` is a parent of `WeaponTurret` @@ -42,14 +41,15 @@ object RepairableWeaponTurret { val zoneId = zone.id val tguid = target.GUID val events = zone.VehicleEvents - target.Weapons + events ! BundledEnvelope(target.Weapons .map({ case (index, slot: EquipmentSlot) => (index, slot.Equipment) }) .collect { case (index: Int, Some(tool: Tool)) => - events ! VehicleServiceMessage( + MessageEnvelope( zoneId, - VehicleAction.EquipmentInSlot(Service.defaultPlayerGUID, tguid, index, tool) + VehicleAction.EquipmentInSlot(tguid, index, tool) ) } + ) } } diff --git a/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSiloControl.scala b/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSiloControl.scala index 996b4d876..9f6b2b0ff 100644 --- a/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSiloControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/resourcesilo/ResourceSiloControl.scala @@ -11,8 +11,9 @@ import net.psforever.objects.zones import net.psforever.objects.{GlobalDefinitions, Ntu, NtuContainer, NtuStorageBehavior, Vehicle} import net.psforever.types.{ExperienceType, PlanetSideEmpire} import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.PlanetsideAttribute import net.psforever.util.Config import scala.concurrent.ExecutionContext.Implicits.global @@ -39,7 +40,7 @@ class ResourceSiloControl(resourceSilo: ResourceSilo) private var drainMultiplier: Float = 1.0f def receive: Receive = { - case Service.Startup() => + case Service.Startup => resourceSilo.Owner match { case building: Building => UpdateChargeLevel(amount = 0) @@ -106,9 +107,9 @@ class ResourceSiloControl(resourceSilo: ResourceSilo) log.trace(s"LowNtuWarning: Silo ${resourceSilo.GUID} low ntu warning set to $enabled") val building = resourceSilo.Owner val zone = building.Zone - building.Zone.AvatarEvents ! AvatarServiceMessage( + building.Zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.PlanetsideAttribute(building.GUID, 47, if (resourceSilo.LowNtuWarningOn) 1 else 0) + PlanetsideAttribute(building.GUID, 47, if (resourceSilo.LowNtuWarningOn) 1 else 0) ) } @@ -129,9 +130,9 @@ class ResourceSiloControl(resourceSilo: ResourceSilo) log.trace( s"UpdateChargeLevel: silo ${resourceSilo.GUID} NTU bar level has changed from $siloDisplayBeforeChange to ${resourceSilo.CapacitorDisplay}" ) - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.PlanetsideAttribute(resourceSilo.GUID, 45, resourceSilo.CapacitorDisplay) + PlanetsideAttribute(resourceSilo.GUID, 45, resourceSilo.CapacitorDisplay) ) building.Actor ! BuildingActor.MapUpdate() } @@ -202,12 +203,16 @@ class ResourceSiloControl(resourceSilo: ResourceSilo) (Config.app.game.experience.sep.ntuSiloDepositReward.toFloat * amount * resourceSilo.Definition.ChargeTime.toSeconds.toFloat / resourceSilo.MaxNtuCapacitor ).toLong - vehicle.Zone.AvatarEvents ! AvatarServiceMessage( - owner.name, - AvatarAction.AwardBep(owner.charId, deposit, ExperienceType.Normal) + vehicle.Zone.AvatarEvents ! BundledEnvelope( + MessageEnvelope( + owner.name, + AvatarAction.AwardBep(owner.charId, deposit, ExperienceType.Normal) + ), + MessageEnvelope( + owner.name, + AvatarAction.ShareAntExperienceWithSquad(owner, deposit, vehicle) + ) ) - vehicle.Zone.AvatarEvents ! AvatarServiceMessage( - owner.name, AvatarAction.ShareAntExperienceWithSquad(owner, deposit, vehicle)) zones.exp.ToDatabase.reportNtuActivity(owner.charId, resourceSilo.Zone.Number, resourceSilo.Owner.GUID.guid, deposit) } } @@ -229,9 +234,9 @@ class ResourceSiloControl(resourceSilo: ResourceSilo) val amount = (if (trigger > 0) { // panel glow & orb particles on val zone = resourceSilo.Zone - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, resourceSilo.GUID, 49, 1) + PlanetsideAttribute(resourceSilo.GUID, 49, 1) ) math.min(resourceSilo.MaxNtuCapacitor - currentlyHas, trigger) } else if (trigger < 0) { @@ -240,9 +245,9 @@ class ResourceSiloControl(resourceSilo: ResourceSilo) } else { // panel glow & orb particles off val zone = resourceSilo.Zone - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, resourceSilo.GUID, 49, 0) + PlanetsideAttribute(resourceSilo.GUID, 49, 0) ) 0 }) * 0.9f 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 7173f5990..380d7b95c 100644 --- a/src/main/scala/net/psforever/objects/serverobject/shuttle/OrbitalShuttlePadControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/shuttle/OrbitalShuttlePadControl.scala @@ -8,9 +8,12 @@ import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.zones.Zone import net.psforever.packet.game.ChatMsg -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse +import net.psforever.services.base.support.SupportActor +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 @@ -48,8 +51,13 @@ class OrbitalShuttlePadControl(pad: OrbitalShuttlePad) extends Actor { managedDoors.foreach { door => door.Actor ! Door.UpdateMechanism(OrbitalShuttlePadControl.lockedWaitingForShuttle) val zone = pad.Zone - if(door.isOpen) { - zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.DoorSlamsShut(door)) + if (door.isOpen) { + door.Open = None + zone.LocalEvents ! DoorMessage( + zone.id, + LocalAction.DoorCloses(door.GUID), + SupportActor.ClearSpecific(List(door), zone) + ) } } @@ -98,7 +106,7 @@ class OrbitalShuttlePadControl(pad: OrbitalShuttlePad) extends Actor { * register and add the shuttle as a common vehicle of the said zone */ val startUp: Receive = { - case Service.Startup() => + case Service.Startup => import net.psforever.types.Vector3 import net.psforever.types.Vector3.DistanceSquared import net.psforever.objects.GlobalDefinitions._ @@ -175,8 +183,7 @@ object OrbitalShuttlePadControl { * Logic for door mechanism that keeps select doors shut when the shuttle is not ready for boarding. * A message flashes onscreen to explain this reason. * The message will not flash if the door has no expectation of ever opening for a user. - * @see `AvatarAction.SendResponse` - * @see `AvatarServiceMessage` + * @see `SendResponse` * @see `ChatMessageType` * @see `ChatMsg` * @see `Player` @@ -190,15 +197,14 @@ object OrbitalShuttlePadControl { val zone = door.Zone obj match { case p: Player if p.Faction == door.Faction => - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( p.Name, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, + SendResponse( ChatMsg(ChatMessageType.UNK_225, wideContents=false, "", "@DoorWillOpenWhenShuttleReturns", None) ) ) p.Name - case _ => ; + case _ => () } false } diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/participation/FacilityHackParticipation.scala b/src/main/scala/net/psforever/objects/serverobject/structures/participation/FacilityHackParticipation.scala index a4962f5a9..24da2b2a6 100644 --- a/src/main/scala/net/psforever/objects/serverobject/structures/participation/FacilityHackParticipation.scala +++ b/src/main/scala/net/psforever/objects/serverobject/structures/participation/FacilityHackParticipation.scala @@ -4,6 +4,9 @@ package net.psforever.objects.serverobject.structures.participation import net.psforever.objects.Player import net.psforever.objects.avatar.scoring.Kill import net.psforever.objects.sourcing.UniquePlayer +import net.psforever.packet.game.GenericObjectActionMessage +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.SendResponse import net.psforever.types.{PlanetSideEmpire, Vector3} import scala.collection.mutable @@ -106,16 +109,15 @@ trait FacilityHackParticipation extends ParticipationLogic { if (building.virusId != 8) { import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.objects.GlobalDefinitions - import net.psforever.services.Service - import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} val mainTerm = building.Amenities.filter(x => x.isInstanceOf[Terminal] && x.Definition == GlobalDefinitions.main_terminal).head.GUID - val msg1 = AvatarAction.GenericObjectAction(Service.defaultPlayerGUID, mainTerm, 61) - val msg2 = AvatarAction.GenericObjectAction(Service.defaultPlayerGUID, mainTerm, 58) + val pkts = SendResponse( + GenericObjectActionMessage(mainTerm, 61), + GenericObjectActionMessage(mainTerm, 58) + ) val events = building.Zone.AvatarEvents - list.foreach { p => - events ! AvatarServiceMessage(p.Name, msg1) - events ! AvatarServiceMessage(p.Name, msg2) - } + events ! BundledEnvelope(list.map { p => + MessageEnvelope(p.Name, pkts) + }) } } } diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/participation/MajorFacilityHackParticipation.scala b/src/main/scala/net/psforever/objects/serverobject/structures/participation/MajorFacilityHackParticipation.scala index 29fd9a7c3..a30211873 100644 --- a/src/main/scala/net/psforever/objects/serverobject/structures/participation/MajorFacilityHackParticipation.scala +++ b/src/main/scala/net/psforever/objects/serverobject/structures/participation/MajorFacilityHackParticipation.scala @@ -4,7 +4,7 @@ package net.psforever.objects.serverobject.structures.participation import net.psforever.objects.serverobject.structures.{Building, StructureType} import net.psforever.objects.sourcing.{PlayerSource, UniquePlayer} import net.psforever.objects.zones.{HotSpotInfo, ZoneHotSpotProjector} -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction import net.psforever.types.{ChatMessageType, PlanetSideEmpire, Vector3} import net.psforever.util.Config import akka.pattern.ask @@ -15,7 +15,8 @@ import net.psforever.objects.avatar.scoring.Kill import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.zones.exp.ToDatabase import net.psforever.packet.game.ChatMsg -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.SendResponse import scala.collection.mutable import scala.concurrent.duration._ @@ -303,17 +304,18 @@ final case class MajorFacilityHackParticipation(building: Building) extends Faci finalCep, expType = "cep" ) - events ! AvatarServiceMessage(hacker.Name, AvatarAction.AwardCep(hackerId, finalCep)) + events ! MessageEnvelope(hacker.Name, AvatarAction.AwardCep(hackerId, finalCep)) }*/ //bystanders (cep if squad leader, bep otherwise) - contributingPlayers + events ! BundledEnvelope(contributingPlayers //.filterNot { _.CharId == hackerId } - .foreach { player => + .map { player => val charId = player.CharId val contributionMultiplier = contributionPerPlayerByTime.getOrElse(charId, 1f) val outputValue = (finalCep * contributionMultiplier).toLong - events ! AvatarServiceMessage(player.Name, AvatarAction.FacilityCaptureRewards(buildingId, zoneNumber, outputValue)) + MessageEnvelope(player.Name, AvatarAction.FacilityCaptureRewards(buildingId, zoneNumber, outputValue)) } + ) //flag carrier (won't be in soi, but earns cep from capture) flagCarrier.collect { case player if !isResecured => @@ -337,7 +339,7 @@ final case class MajorFacilityHackParticipation(building: Building) extends Faci finalModifiedCep, expType = "llu" ) - events ! AvatarServiceMessage(player.Name, AvatarAction.AwardCep(charId, finalModifiedCep)) + events ! MessageEnvelope(player.Name, AvatarAction.AwardCep(charId, finalModifiedCep)) } } else { //no need to calculate a fancy score @@ -438,9 +440,9 @@ object MajorFacilityHackParticipation { msg: ChatMsg ): Unit = { val events = building.Zone.LocalEvents - val message = LocalAction.SendResponse(msg) - targets.foreach { player => - events ! LocalServiceMessage(player.Name, message) - } + val message = SendResponse(msg) + events ! BundledEnvelope(targets.map { player => + MessageEnvelope(player.Name, message) + }) } } diff --git a/src/main/scala/net/psforever/objects/serverobject/structures/participation/TowerHackParticipation.scala b/src/main/scala/net/psforever/objects/serverobject/structures/participation/TowerHackParticipation.scala index 505c401ae..08d2453da 100644 --- a/src/main/scala/net/psforever/objects/serverobject/structures/participation/TowerHackParticipation.scala +++ b/src/main/scala/net/psforever/objects/serverobject/structures/participation/TowerHackParticipation.scala @@ -4,7 +4,8 @@ package net.psforever.objects.serverobject.structures.participation import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.sourcing.PlayerSource import net.psforever.objects.zones.exp.ToDatabase -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} import net.psforever.types.{PlanetSideEmpire, Vector3} import net.psforever.util.Config @@ -144,7 +145,7 @@ final case class TowerHackParticipation(building: Building) extends FacilityHack //7. reward participants //Classically, only players in the SOI are rewarded //terminal hacker (always cep) - events ! AvatarServiceMessage(hacker.Name, AvatarAction.AwardCep(hacker.CharId, finalCep)) + events ! MessageEnvelope(hacker.Name, AvatarAction.AwardCep(hacker.CharId, finalCep)) ToDatabase.reportFacilityCapture( hackerId, zoneNumber, @@ -153,18 +154,19 @@ final case class TowerHackParticipation(building: Building) extends FacilityHack expType = "cep" ) //bystanders (cep if squad leader, bep otherwise) - soiPlayers + events ! BundledEnvelope(soiPlayers .filterNot(_.CharId == hackerId) - .foreach { player => + .map { player => val charId = player.CharId val contributionTimeMultiplier = contributionPerPlayerByTime.getOrElse(charId, 0.5f) val contributionDistanceMultiplier = contributionPerPlayerByDistanceFromGoal.getOrElse(charId, 0.5f) val outputValue = (finalCep * contributionTimeMultiplier * contributionDistanceMultiplier).toLong - events ! AvatarServiceMessage( + MessageEnvelope( player.Name, AvatarAction.FacilityCaptureRewards(buildingId, zoneNumber, outputValue) ) } + ) } else { //no need to calculate a fancy score ToDatabase.reportFacilityCaptureInBulk( diff --git a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala index ad700fbbe..e4e33b2aa 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminal.scala @@ -83,7 +83,7 @@ object ProximityTerminal { if (obj.Actor == Default.Actor) { obj.Actor = context.actorOf(Props(classOf[ProximityTerminalControl], obj), PlanetSideServerObject.UniqueActorName(obj)) - obj.Actor ! Service.Startup() + obj.Actor ! Service.Startup } } } 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 b26f1d617..c34eb6425 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala @@ -6,6 +6,9 @@ import net.psforever.objects.serverobject.damage.Damageable import net.psforever.objects.sourcing.AmenitySource import net.psforever.objects.vital.interaction.DamageResult import net.psforever.packet.game.HackState1 +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} +import net.psforever.services.local.support.{HackClearActor, HackClearEnvelope} import org.log4s.Logger import scala.annotation.unused @@ -24,10 +27,7 @@ import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityCo import net.psforever.objects.vital.{HealFromTerminal, RepairFromTerminal, Vitality} import net.psforever.objects.zones.ZoneAware import net.psforever.packet.game.InventoryStateMessage -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.local.LocalAction /** * An `Actor` that handles messages being dispatched to a specific `ProximityTerminal`. @@ -147,7 +147,7 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit) tryAutoRepair() if (term.HackedBy.nonEmpty) { val zone = term.Zone - zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.ClearTemporaryHack(Service.defaultPlayerGUID, term)) + zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(term)) } super.DestructionAwareness(target, cause) } @@ -181,7 +181,8 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit) self, ProximityTerminalControl.TerminalAction() ) - TerminalObject.Zone.LocalEvents ! Terminal.StartProximityEffect(term) + val zone = TerminalObject.Zone + zone.LocalEvents ! MessageEnvelope(zone.id, LocalAction.ProximityTerminalEffect(TerminalObject.GUID, effectState = true)) } } else { log.warn(s"ProximityTerminal.Use: $target was rejected by unit ${term.Definition.Name}@${term.GUID.guid}") @@ -201,7 +202,8 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit) //de-activation (global / local) if (term.NumberUsers == 0 && hadUsers) { terminalAction.cancel() - TerminalObject.Zone.LocalEvents ! Terminal.StopProximityEffect(term) + val zone = TerminalObject.Zone + zone.LocalEvents ! MessageEnvelope(zone.id, LocalAction.ProximityTerminalEffect(TerminalObject.GUID, effectState = false)) } } else { log.debug( @@ -216,12 +218,13 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit) terminalAction.cancel() if (callbacks.nonEmpty) { callbacks.clear() - TerminalObject.Zone.LocalEvents ! Terminal.StopProximityEffect(term) + val zone = TerminalObject.Zone + zone.LocalEvents ! MessageEnvelope(zone.id, LocalAction.ProximityTerminalEffect(TerminalObject.GUID, effectState = true)) } //clear hack state if (term.HackedBy.nonEmpty) { val zone = term.Zone - zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.ClearTemporaryHack(Service.defaultPlayerGUID, term)) + zone.LocalEvents ! HackClearEnvelope(HackClearActor.ObjectIsResecured(term)) } } @@ -347,9 +350,9 @@ object ProximityTerminalControl { if (oldMax < maxHealthCap) { target.MaxHealth = newMax - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.PlanetsideAttributeToAll(target.GUID, 1, newMax) + PlanetsideAttribute(target.GUID, 1, newMax) ) } if (target.Health < newMax) { @@ -362,17 +365,17 @@ object ProximityTerminalControl { def PlayerHealthCallback(target: PlanetSideGameObject with Vitality with ZoneAware): Unit = { val zone = target.Zone - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.PlanetsideAttributeToAll(target.GUID, 0, target.Health) + PlanetsideAttribute(target.GUID, 0, target.Health) ) } def VehicleHealthCallback(target: PlanetSideGameObject with Vitality with ZoneAware): Unit = { val zone = target.Zone - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, target.GUID, 0, target.Health) + PlanetsideAttribute(target.GUID, 0, target.Health) ) } @@ -401,9 +404,9 @@ object ProximityTerminalControl { target.Armor = armor + finalRepairAmount target.LogActivity(RepairFromTerminal(AmenitySource(terminal), finalRepairAmount)) val zone = target.Zone - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.PlanetsideAttributeToAll(target.GUID, 4, target.Armor) + PlanetsideAttribute(target.GUID, 4, target.Armor) ) target.Armor == maxArmor } else { @@ -429,12 +432,12 @@ object ProximityTerminalControl { val events = unit.Zone.AvatarEvents val channel = target.Name ancient.foreach { case (weapon, slots) => - slots.foreach { slot => - events ! AvatarServiceMessage( + events ! BundledEnvelope(slots.map { slot => + MessageEnvelope( channel, - AvatarAction.SendResponse(Service.defaultPlayerGUID, InventoryStateMessage(slot.Box.GUID, weapon.GUID, slot.Box.Capacity)) + SendResponse(InventoryStateMessage(slot.Box.GUID, weapon.GUID, slot.Box.Capacity)) ) - } + }) } !result.flatMap { _._2 }.exists { slot => slot.Magazine < slot.MaxMagazine() } } @@ -454,14 +457,14 @@ object ProximityTerminalControl { ) val events = unit.Zone.VehicleEvents val channel = target.Actor.toString - result.foreach { case (weapon, slots) => - slots.foreach { slot => - events ! VehicleServiceMessage( + events ! BundledEnvelope(result.flatMap { case (weapon, slots) => + slots.map { slot => + MessageEnvelope( channel, - VehicleAction.SendResponse(Service.defaultPlayerGUID, InventoryStateMessage(slot.Box.GUID, weapon.GUID, slot.Box.Capacity)) + SendResponse(InventoryStateMessage(slot.Box.GUID, weapon.GUID, slot.Box.Capacity)) ) } - } + }) !result.flatMap { _._2 }.exists { slot => slot.Magazine < slot.MaxMagazine() } } 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 b3a80776e..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.Service -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +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 ! LocalServiceMessage(zone.id, LocalAction.ClearTemporaryHack(Service.defaultPlayerGUID, 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 ! LocalServiceMessage(zone.id, LocalAction.ClearTemporaryHack(Service.defaultPlayerGUID, 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 9f07bce14..d8cbe03f9 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,8 +6,11 @@ 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.{LocalAction, LocalServiceMessage} +import net.psforever.services.local.LocalAction +import net.psforever.services.local.support.HackCaptureActor +import net.psforever.services.local.support.CaptureEnvelope import net.psforever.types.PlanetSideEmpire +import net.psforever.services.base.envelope.MessageEnvelope import scala.concurrent.duration._ import scala.util.{Failure, Success} @@ -39,22 +42,17 @@ object CaptureTerminals { val zoneid = zone.id val events = zone.LocalEvents val isResecured = hackingPlayer.Faction == target.Faction - events ! LocalServiceMessage( + events ! MessageEnvelope( zoneid, - LocalAction.TriggerSound(hackingPlayer.GUID, target.HackSound, hackingPlayer.Position, 30, 0.49803925f) + hackingPlayer.GUID, + LocalAction.TriggerSound(target.HackSound, hackingPlayer.Position, 30, 0.49803925f) ) if (isResecured) { // Resecure the CC - events ! LocalServiceMessage( - zoneid, - LocalAction.ResecureCaptureTerminal(target, PlayerSource(hackingPlayer)) - ) + events ! CaptureEnvelope(HackCaptureActor.ResecureCaptureTerminal(target, zone, PlayerSource(hackingPlayer))) } else { // Start the CC hack timer - events ! LocalServiceMessage( - zoneid, - LocalAction.StartCaptureTerminalHack(target) - ) + 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/terminals/implant/ImplantTerminalMechControl.scala b/src/main/scala/net/psforever/objects/serverobject/terminals/implant/ImplantTerminalMechControl.scala index c03709e69..f35249851 100644 --- a/src/main/scala/net/psforever/objects/serverobject/terminals/implant/ImplantTerminalMechControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/terminals/implant/ImplantTerminalMechControl.scala @@ -16,8 +16,9 @@ import net.psforever.objects.vital.interaction.DamageResult import net.psforever.objects.zones.Zone import net.psforever.objects.{GlobalDefinitions, Player, SimpleItem} import net.psforever.packet.game.HackState1 -import net.psforever.services.local.{LocalAction, LocalServiceMessage} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.SetEmpire +import net.psforever.services.vehicle.VehicleAction import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} import scala.annotation.unused @@ -144,14 +145,14 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech) val zone = mech.Zone val zoneId = zone.id val events = zone.VehicleEvents - mech.Seats.values.foreach(seat => + events ! BundledEnvelope(mech.Seats.values.flatMap(seat => seat.occupant.collect { case player => seat.unmount(player) player.VehicleSeated = None - events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, unk2=false, guid)) + MessageEnvelope(zoneId, player.GUID, VehicleAction.KickPassenger(4, unk2=false, guid)) } - ) + )) } def powerTurnOnCallback(): Unit = { @@ -173,7 +174,7 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech) if (player.Faction == localFaction) { if (mech.Owner.asInstanceOf[Building].CaptureTerminalIsHacked) { //this is actually futile, as a hacked base does not grant access to the terminal - events ! LocalServiceMessage(localFaction.toString, LocalAction.SetEmpire(guid, localFaction)) + events ! MessageEnvelope(localFaction.toString, SetEmpire(guid, localFaction)) } kickAllOccupantsNotOfFaction(zone, guid, mech, localFaction) } else { @@ -224,9 +225,9 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech) setToFaction: PlanetSideEmpire.Value ): Unit = { val events = zone.LocalEvents - opposingFactionsAre(setToFaction).foreach { faction => - events ! LocalServiceMessage(faction.toString, LocalAction.SetEmpire(guid, faction)) - } + events ! BundledEnvelope(opposingFactionsAre(setToFaction).toSeq.map { faction => + MessageEnvelope(faction.toString, SetEmpire(guid, faction)) + }) } private def noAccessByOpposingFactions( @@ -235,9 +236,9 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech) setToFaction: PlanetSideEmpire.Value ): Unit = { val events = zone.LocalEvents - opposingFactionsAre(setToFaction).foreach { faction => - events ! LocalServiceMessage(faction.toString, LocalAction.SetEmpire(guid, setToFaction)) - } + events ! BundledEnvelope(opposingFactionsAre(setToFaction).toSeq.map { faction => + MessageEnvelope(faction.toString, SetEmpire(guid, setToFaction)) + }) } private def opposingFactionsAre(faction: PlanetSideEmpire.Value): PlanetSideEmpire.ValueSet = { @@ -272,13 +273,13 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech) ): Unit = { val zoneId = zone.id val events = zone.LocalEvents - obj.Seats.values.foreach(seat => + events ! BundledEnvelope(obj.Seats.values.flatMap(seat => seat.occupant.collect { case player if test(player.Faction) => seat.unmount(player) player.VehicleSeated = None - events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, unk2 = false, guid)) + MessageEnvelope(zoneId, player.GUID, VehicleAction.KickPassenger(4, unk2 = false, guid)) } - ) + )) } } diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala index 24f1bdea4..eaf927db0 100644 --- a/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/turret/FacilityTurretControl.scala @@ -13,9 +13,10 @@ import net.psforever.objects.serverobject.turret.auto.AutomatedTurret.Target import net.psforever.objects.serverobject.turret.auto.{AffectedByAutomaticTurretFire, AutomatedTurret, AutomatedTurretBehavior} import net.psforever.objects.vital.interaction.DamageResult import net.psforever.packet.game.{ChangeFireModeMessage, HackState1} -import net.psforever.services.Service +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.SendResponse import net.psforever.services.vehicle.support.TurretUpgrader -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.vehicle.VehicleAction import net.psforever.types.{BailType, PlanetSideEmpire, PlanetSideGUID} /** @@ -179,7 +180,7 @@ class FacilityTurretControl(turret: FacilityTurret) seat.unmount(player) player.VehicleSeated = None if (player.HasGUID) { - events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, unk2=false, guid)) + events ! MessageEnvelope(zoneId, player.GUID, VehicleAction.KickPassenger(4, unk2=false, guid)) } case None => () } @@ -237,9 +238,9 @@ class FacilityTurretControl(turret: FacilityTurret) .flatMap(_.Equipment) .collect { case weapon: Tool if weapon.FireModeIndex > 0 => weapon.FireModeIndex = 0 - events ! VehicleServiceMessage( + events ! MessageEnvelope( zoneid, - VehicleAction.SendResponse(Service.defaultPlayerGUID, ChangeFireModeMessage(weapon.GUID, 0)) + SendResponse(ChangeFireModeMessage(weapon.GUID, 0)) ) } } @@ -335,15 +336,15 @@ class FacilityTurretControl(turret: FacilityTurret) val zone = turret.Zone val zoneId = zone.id val events = zone.VehicleEvents - turret.Seats.values.zipWithIndex.foreach { + events ! BundledEnvelope(turret.Seats.values.zipWithIndex.flatMap { case (seat, seat_num) => seat.occupant.collect { case player => seat.unmount(player) player.VehicleSeated = None - events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, seat_num, unk2=true, guid)) + MessageEnvelope(zoneId, player.GUID, VehicleAction.KickPassenger(seat_num, unk2=true, guid)) } - } + }) captureTerminalChanges(terminal, super.captureTerminalIsHacked, actionDelays = 3000L) } diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/TurretControl.scala b/src/main/scala/net/psforever/objects/serverobject/turret/TurretControl.scala index 2212a67ea..9a6845e78 100644 --- a/src/main/scala/net/psforever/objects/serverobject/turret/TurretControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/turret/TurretControl.scala @@ -8,7 +8,9 @@ import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior import net.psforever.objects.serverobject.damage.{Damageable, DamageableWeaponTurret} import net.psforever.objects.serverobject.repair.RepairableWeaponTurret import net.psforever.objects.vital.interaction.DamageResult -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.packet.game.PlanetsideAttributeMessage +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse trait TurretControl extends Actor @@ -39,8 +41,10 @@ trait TurretControl val zoneId = zone.id val events = zone.AvatarEvents val tguid = TurretObject.GUID - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 50, 0)) - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 51, 0)) + events ! MessageEnvelope( + zoneId, + SendResponse(PlanetsideAttributeMessage(tguid, 50, 0), PlanetsideAttributeMessage(tguid, 51, 0)) + ) } /** @@ -56,7 +60,9 @@ trait TurretControl val tguid = target.GUID CancelJammeredSound(target) CancelJammeredStatus(target) - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 50, 1)) - events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(tguid, 51, 1)) + events ! MessageEnvelope( + zoneId, + SendResponse(PlanetsideAttributeMessage(tguid, 50, 1), PlanetsideAttributeMessage(tguid, 51, 1)) + ) } } diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/VanuSentryControl.scala b/src/main/scala/net/psforever/objects/serverobject/turret/VanuSentryControl.scala index 4a6cb991c..38fbfb7ca 100644 --- a/src/main/scala/net/psforever/objects/serverobject/turret/VanuSentryControl.scala +++ b/src/main/scala/net/psforever/objects/serverobject/turret/VanuSentryControl.scala @@ -4,7 +4,8 @@ package net.psforever.objects.serverobject.turret import akka.actor.Cancellable import net.psforever.objects.serverobject.ServerObjectControl import net.psforever.objects.{Default, Player, Tool} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.local.LocalAction import net.psforever.types.Vector3 import scala.concurrent.ExecutionContext.Implicits.global @@ -49,13 +50,14 @@ class VanuSentryControl(turret: FacilityTurret) if (weapon.Magazine < weapon.MaxMagazine && System.currentTimeMillis() - weapon.LastDischarge > 3000L) { weapon.Magazine += 1 val seat = TurretObject.Seat(0).get - seat.occupant.collect { + TurretObject.Zone.LocalEvents ! BundledEnvelope(seat.occupant.collect { case player: Player => - TurretObject.Zone.LocalEvents ! LocalServiceMessage( + MessageEnvelope( TurretObject.Zone.id, - LocalAction.RechargeVehicleWeapon(player.GUID, TurretObject.GUID, weapon.GUID) + player.GUID, + LocalAction.RechargeVehicleWeapon(TurretObject.GUID, weapon.GUID) ) - } + }) } else if (weapon.Magazine == weapon.MaxMagazine && weaponAmmoRechargeTimer != Default.Cancellable) { weaponAmmoRechargeTimer.cancel() 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 03b8925ae..742a8c67c 100644 --- a/src/main/scala/net/psforever/objects/serverobject/turret/WeaponTurrets.scala +++ b/src/main/scala/net/psforever/objects/serverobject/turret/WeaponTurrets.scala @@ -5,11 +5,11 @@ import net.psforever.objects.avatar.Certification import net.psforever.objects.ce.Deployable import net.psforever.objects.{Player, Tool, TurretDeployable} import net.psforever.packet.game.{HackMessage, HackState, HackState1, HackState7, InventoryStateMessage} -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} -import net.psforever.services.vehicle.support.TurretUpgrader +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.{SendResponse, SetEmpire} +import net.psforever.services.local.LocalAction +import net.psforever.services.vehicle.support.{TurretEnvelope, TurretUpgrader} +import net.psforever.services.vehicle.VehicleAction import net.psforever.types.PlanetSideGUID object WeaponTurrets { @@ -31,9 +31,9 @@ object WeaponTurrets { upgrade: TurretUpgrade.Value )(): Unit = { tool.Magazine = 0 - target.Zone.AvatarEvents ! AvatarServiceMessage( + target.Zone.AvatarEvents ! MessageEnvelope( user.Name, - AvatarAction.SendResponse(Service.defaultPlayerGUID, InventoryStateMessage(tool.AmmoSlot.Box.GUID, tool.GUID, 0)) + SendResponse(InventoryStateMessage(tool.AmmoSlot.Box.GUID, tool.GUID, 0)) ) FinishUpgradingMannedTurret(target, upgrade) } @@ -45,7 +45,7 @@ object WeaponTurrets { * @see `TurretUpgrade` * @see `TurretUpgrader.AddTask` * @see `TurretUpgrader.ClearSpecific` - * @see `VehicleServiceMessage.TurretUpgrade` + * @see `TurretMessage` * @param target the facility turret being upgraded * @param upgrade the upgrade being applied to the turret (usually, it's weapon system) */ @@ -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 ! VehicleServiceMessage.TurretUpgrade(TurretUpgrader.ClearSpecific(List(target), zone)) - events ! VehicleServiceMessage.TurretUpgrade(TurretUpgrader.AddTask(target, zone, upgrade)) + events ! TurretEnvelope(TurretUpgrader.ClearSpecific(List(target), zone)) + events ! TurretEnvelope(TurretUpgrader.AddTask(target, zone, upgrade)) } /** @@ -85,10 +85,9 @@ object WeaponTurrets { turret.UpdateTurretUpgradeTime() (HackState.Ongoing, progress.toInt) } - turret.Zone.AvatarEvents ! AvatarServiceMessage( + turret.Zone.AvatarEvents ! MessageEnvelope( tplayer.Name, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, + SendResponse( HackMessage(progressType, turret.GUID, tplayer.GUID, progressGrade, -1f, progressState, HackState7.Unk8) ) ) @@ -101,28 +100,30 @@ object WeaponTurrets { val certs = hacker.avatar.certifications if (certs.contains(Certification.ExpertHacking) || certs.contains(Certification.ElectronicsExpert)) { // Forcefully dismount all seated occupants from the turret - target.Seats.values.foreach { seat => + zone.VehicleEvents ! BundledEnvelope(target.Seats.values.flatMap { seat => seat.occupant.collect { player: Player => seat.unmount(player) player.VehicleSeated = None - zone.VehicleEvents ! VehicleServiceMessage( + MessageEnvelope( zone.id, - VehicleAction.KickPassenger(player.GUID, 4, unk2 = false, target.GUID) + player.GUID, + VehicleAction.KickPassenger(4, unk2 = false, target.GUID) ) } - } + }) //hacker owns the deployable now target.OwnerGuid = None target.Actor ! Deployable.Ownership(hacker) //convert faction - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.SetEmpire(Service.defaultPlayerGUID, target.GUID, hacker.Faction) + SetEmpire(target.GUID, hacker.Faction) ) - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.TriggerSound(hacker.GUID, target.HackSound, target.Position, 30, 0.49803925f) + hacker.GUID, + LocalAction.TriggerSound(target.HackSound, target.Position, 30, 0.49803925f) ) } else { //deconstruct diff --git a/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretBehavior.scala b/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretBehavior.scala index b9ef525ac..0e3cd9add 100644 --- a/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretBehavior.scala +++ b/src/main/scala/net/psforever/objects/serverobject/turret/auto/AutomatedTurretBehavior.scala @@ -17,8 +17,8 @@ import net.psforever.objects.zones.Zone import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.objects.{Default, PlanetSideGameObject, Player} import net.psforever.packet.game.{ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ObjectDetectedMessage} -import net.psforever.services.Service -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse import net.psforever.types.{PlanetSideGUID, Vector3} import scala.concurrent.ExecutionContext.Implicits.global @@ -879,7 +879,7 @@ object AutomatedTurretBehavior { EffectTarget.Validation.AutoTurretBlankVehicleTarget ) - private val noTargets: List[PlanetSideGUID] = List(Service.defaultPlayerGUID) + private val noTargets: List[PlanetSideGUID] = List(Default.GUID0) /** * Are we tracking a target entity? @@ -889,9 +889,9 @@ object AutomatedTurretBehavior { * @param list target's globally unique identifier, in list form */ def startTracking(zone: Zone, channel: String, turretGuid: PlanetSideGUID, list: List[PlanetSideGUID]): Unit = { - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( channel, - LocalAction.SendResponse(ObjectDetectedMessage(turretGuid, turretGuid, 0, list)) + SendResponse(ObjectDetectedMessage(turretGuid, turretGuid, 0, list)) ) } @@ -902,9 +902,9 @@ object AutomatedTurretBehavior { * @param turretGuid turret */ def stopTracking(zone: Zone, channel: String, turretGuid: PlanetSideGUID): Unit = { - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( channel, - LocalAction.SendResponse(ObjectDetectedMessage(turretGuid, turretGuid, 0, noTargets)) + SendResponse(ObjectDetectedMessage(turretGuid, turretGuid, 0, noTargets)) ) } @@ -915,9 +915,9 @@ object AutomatedTurretBehavior { * @param weaponGuid turret's weapon */ def startShooting(zone: Zone, channel: String, weaponGuid: PlanetSideGUID): Unit = { - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( channel, - LocalAction.SendResponse(ChangeFireStateMessage_Start(weaponGuid)) + SendResponse(ChangeFireStateMessage_Start(weaponGuid)) ) } @@ -928,9 +928,9 @@ object AutomatedTurretBehavior { * @param weaponGuid turret's weapon */ def stopShooting(zone: Zone, channel: String, weaponGuid: PlanetSideGUID): Unit = { - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( channel, - LocalAction.SendResponse(ChangeFireStateMessage_Stop(weaponGuid)) + SendResponse(ChangeFireStateMessage_Stop(weaponGuid)) ) } diff --git a/src/main/scala/net/psforever/objects/vehicles/control/AmsControl.scala b/src/main/scala/net/psforever/objects/vehicles/control/AmsControl.scala index ccb69f2fe..f30cddc14 100644 --- a/src/main/scala/net/psforever/objects/vehicles/control/AmsControl.scala +++ b/src/main/scala/net/psforever/objects/vehicles/control/AmsControl.scala @@ -2,8 +2,9 @@ package net.psforever.objects.vehicles.control import net.psforever.objects._ -import net.psforever.services.Service -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.PlanetsideAttribute +import net.psforever.services.vehicle.VehicleAction import net.psforever.types.DriveState /** @@ -29,8 +30,8 @@ class AmsControl(vehicle: Vehicle) case None => "" } val events = zone.VehicleEvents - events ! VehicleServiceMessage.AMSDeploymentChange(zone) - events ! VehicleServiceMessage(driverChannel, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vehicle.GUID, 81, 1)) + events ! MessageEnvelope(zone.id, VehicleAction.AMSDeploymentChange(zone)) + events ! MessageEnvelope(driverChannel, PlanetsideAttribute(vehicle.GUID, 81, 1)) case _ => ; } } @@ -49,8 +50,8 @@ class AmsControl(vehicle: Vehicle) case None => "" } val events = zone.VehicleEvents - events ! VehicleServiceMessage.AMSDeploymentChange(zone) - events ! VehicleServiceMessage(driverChannel, VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vehicle.GUID, 81, 0)) + events ! MessageEnvelope(zone.id, VehicleAction.AMSDeploymentChange(zone)) + events ! MessageEnvelope(driverChannel, PlanetsideAttribute(vehicle.GUID, 81, 0)) case _ => ; } } diff --git a/src/main/scala/net/psforever/objects/vehicles/AntTransferBehavior.scala b/src/main/scala/net/psforever/objects/vehicles/control/AntTransferBehavior.scala similarity index 80% rename from src/main/scala/net/psforever/objects/vehicles/AntTransferBehavior.scala rename to src/main/scala/net/psforever/objects/vehicles/control/AntTransferBehavior.scala index 10efab816..edf37c040 100644 --- a/src/main/scala/net/psforever/objects/vehicles/AntTransferBehavior.scala +++ b/src/main/scala/net/psforever/objects/vehicles/control/AntTransferBehavior.scala @@ -1,27 +1,28 @@ -// Copyright (c) 2020 PSForever -package net.psforever.objects.vehicles +package net.psforever.objects.vehicles.control import akka.actor.{ActorRef, Cancellable} +import akka.actor.typed.scaladsl.adapter._ + import net.psforever.actors.commands.NtuCommand import net.psforever.actors.zone.BuildingActor import net.psforever.objects.serverobject.deploy.Deployment import net.psforever.objects.serverobject.resourcesilo.ResourceSilo import net.psforever.objects.serverobject.structures.WarpGate import net.psforever.objects.serverobject.transfer.{TransferBehavior, TransferContainer} -import net.psforever.objects.{NtuContainer, _} +import net.psforever.objects._ import net.psforever.types.DriveState -import net.psforever.services.Service -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} -import akka.actor.typed.scaladsl.adapter._ import net.psforever.objects.serverobject.transfer.TransferContainer.TransferMaterial +import net.psforever.packet.game.PlanetsideAttributeMessage +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ trait AntTransferBehavior extends TransferBehavior with NtuStorageBehavior { var panelAnimationFunc: () => Unit = NoCharge - var ntuChargingTick: Cancellable = Default.Cancellable - findChargeTargetFunc = Vehicles.FindANTChargingSource + var ntuChargingTick: Cancellable = Default.Cancellable + findChargeTargetFunc = Vehicles.FindANTChargingSource findDischargeTargetFunc = Vehicles.FindANTDischargingTarget def TransferMaterial: TransferMaterial = Ntu.Nanites @@ -31,30 +32,30 @@ trait AntTransferBehavior extends TransferBehavior with NtuStorageBehavior { def antBehavior: Receive = storageBehavior.orElse(transferBehavior) def ActivatePanelsForChargingEvent(vehicle: NtuContainer): Unit = { - val obj = ChargeTransferObject + val obj = ChargeTransferObject val zone = obj.Zone - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vehicle.GUID, 52, 1L) + PlanetsideAttribute(vehicle.GUID, 52, 1L) ) // panel glow on } /** Charging */ def StartNtuChargingEvent(vehicle: NtuContainer): Unit = { - val obj = ChargeTransferObject + val obj = ChargeTransferObject val zone = obj.Zone - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vehicle.GUID, 49, 1L) + PlanetsideAttribute(vehicle.GUID, 49, 1L) ) // orb particle effect on } def UpdateNtuUI(vehicle: Vehicle with NtuContainer): Unit = { if (vehicle.Seats.values.exists(_.isOccupied)) { val display = vehicle.NtuCapacitorScaled.toLong - vehicle.Zone.VehicleEvents ! VehicleServiceMessage( + vehicle.Zone.VehicleEvents ! MessageEnvelope( vehicle.Actor.toString, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vehicle.GUID, 45, display) + PlanetsideAttribute(vehicle.GUID, 45, display) ) } } @@ -158,18 +159,16 @@ trait AntTransferBehavior extends TransferBehavior with NtuStorageBehavior { val zoneId = zone.id val events = zone.VehicleEvents if (transferEvent == TransferBehavior.Event.Charging) { - events ! VehicleServiceMessage( + //1. panel glow off + //2. orb particle effect off + events ! MessageEnvelope( zoneId, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vguid, 52, 0L) - ) // panel glow off - events ! VehicleServiceMessage( - zoneId, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vguid, 49, 0L) - ) // orb particle effect off + SendResponse(PlanetsideAttributeMessage(vguid, 52, 0L), PlanetsideAttributeMessage(vguid, 49, 0L)) + ) } else if (transferEvent == TransferBehavior.Event.Discharging) { - events ! VehicleServiceMessage( + events ! MessageEnvelope( zoneId, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vguid, 52, 0L) + PlanetsideAttribute(vguid, 52, 0L) ) // panel glow off } } @@ -189,7 +188,8 @@ trait AntTransferBehavior extends TransferBehavior with NtuStorageBehavior { transferTarget match { case Some(silo: ResourceSilo) => scala.math.min( - scala.math.min(silo.MaxNtuCapacitor / silo.Definition.ChargeTime.toSeconds.toFloat, chargeable.NtuCapacitor), + scala.math + .min(silo.MaxNtuCapacitor / silo.Definition.ChargeTime.toSeconds.toFloat, chargeable.NtuCapacitor), max ) case _ => @@ -209,9 +209,11 @@ trait AntTransferBehavior extends TransferBehavior with NtuStorageBehavior { def HandleNtuGrant(sender: ActorRef, src: NtuContainer, amount: Float): Unit = { val obj = ChargeTransferObject - if (obj.DeploymentState == DriveState.Deployed && - transferEvent == TransferBehavior.Event.Charging && - ReceiveAndDepositUntilFull(obj, amount)) { + if ( + obj.DeploymentState == DriveState.Deployed && + transferEvent == TransferBehavior.Event.Charging && + ReceiveAndDepositUntilFull(obj, amount) + ) { panelAnimationFunc() } else { TryStopChargingEvent(obj) diff --git a/src/main/scala/net/psforever/objects/vehicles/control/ApcControl.scala b/src/main/scala/net/psforever/objects/vehicles/control/ApcControl.scala index c08b7a432..09236c8ae 100644 --- a/src/main/scala/net/psforever/objects/vehicles/control/ApcControl.scala +++ b/src/main/scala/net/psforever/objects/vehicles/control/ApcControl.scala @@ -10,8 +10,8 @@ import net.psforever.objects.vital.projectile.MaxDistanceCutoff import net.psforever.objects.vital.prop.DamageWithPosition import net.psforever.objects.zones.Zone import net.psforever.packet.game.{TriggerEffectMessage, TriggeredEffectLocation} -import net.psforever.services.Service -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse import net.psforever.types.PlanetSideGUID /** @@ -46,23 +46,20 @@ class ApcControl(vehicle: Vehicle) val zone = obj.Zone val events = zone.VehicleEvents val pos = obj.Position - val GUID0 = Service.defaultPlayerGUID + val GUID0 = Default.GUID0 val emp = ApcControl.apc_emp val faction = obj.Faction //drain the capacitor capacitorCharge(-vehicle.Capacitor) //cause the emp - events ! VehicleServiceMessage( + events ! MessageEnvelope( zone.id, - VehicleAction.SendResponse( - GUID0, - TriggerEffectMessage( + SendResponse(TriggerEffectMessage( GUID0, s"apc_explosion_emp_${faction.toString.toLowerCase}", None, Some(TriggeredEffectLocation(pos, obj.Orientation)) - ) - ) + )) ) //resolve what targets are affected by the emp Zone.serverSideDamage( diff --git a/src/main/scala/net/psforever/objects/vehicles/control/BfrControl.scala b/src/main/scala/net/psforever/objects/vehicles/control/BfrControl.scala index 4f956351b..af869a71f 100644 --- a/src/main/scala/net/psforever/objects/vehicles/control/BfrControl.scala +++ b/src/main/scala/net/psforever/objects/vehicles/control/BfrControl.scala @@ -16,8 +16,8 @@ import net.psforever.objects.vital.ShieldCharge import net.psforever.objects.vital.interaction.DamageResult import net.psforever.objects.zones.Zone import net.psforever.packet.game._ -import net.psforever.services.Service -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{GenericObjectAction, PlanetsideAttribute, SendResponse} import net.psforever.types._ import scala.annotation.unused @@ -269,9 +269,9 @@ class BfrControl(vehicle: Vehicle) def disableShield(): Unit = { val zone = vehicle.Zone - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( s"${zone.id}", - VehicleAction.SendResponse(PlanetSideGUID(0), GenericObjectActionMessage(vehicle.GUID, 45)) + SendResponse(GenericObjectActionMessage(vehicle.GUID, 45)) ) } @@ -283,9 +283,9 @@ class BfrControl(vehicle: Vehicle) def enableShield(): Unit = { val zone = vehicle.Zone - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( s"${zone.id}", - VehicleAction.SendResponse(PlanetSideGUID(0), GenericObjectActionMessage(vehicle.GUID, 44)) + SendResponse(GenericObjectActionMessage(vehicle.GUID, 44)) ) } @@ -334,9 +334,9 @@ class BfrControl(vehicle: Vehicle) val vguid = vehicle.GUID val zone = vehicle.Zone val shields = vehicle.Shields - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, vguid, vehicle.Definition.shieldUiAttribute, shields) + PlanetsideAttribute(vguid, vehicle.Definition.shieldUiAttribute, shields) ) } @@ -399,7 +399,7 @@ class BfrControl(vehicle: Vehicle) }) match { case (slot, Some(_)) => armManagementFunc(slot) - val guid0 = Service.defaultPlayerGUID + val guid0 = Default.GUID0 val doNotSendTo = other match { case Some(pguid: PlanetSideGUID) => pguid case _ => guid0 @@ -414,9 +414,10 @@ class BfrControl(vehicle: Vehicle) }) match { case Some(useThisGuid) => val zone = vehicle.Zone - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.GenericObjectAction(doNotSendTo, useThisGuid, action) + doNotSendTo, + GenericObjectAction(useThisGuid, action) ) case _ => () } @@ -546,7 +547,7 @@ class BfrControl(vehicle: Vehicle) val obj = ChargeTransferObject val zone = obj.Zone val events = zone.VehicleEvents - val GUID0 = Service.defaultPlayerGUID + val GUID0 = Default.GUID0 getNtuContainer() match { case Some(siphon : NtuSiphon) if GlobalDefinitions.isBattleFrameNTUSiphon(siphon.equipment.Definition) && @@ -563,17 +564,14 @@ class BfrControl(vehicle: Vehicle) //cause the emp siphon.equipment.lastDischarge = now //TODO this is the apc emp effect; is there an ntu siphon emp effect? - events ! VehicleServiceMessage( + events ! MessageEnvelope( zone.id, - VehicleAction.SendResponse( - GUID0, - TriggerEffectMessage( + SendResponse(TriggerEffectMessage( GUID0, s"apc_explosion_emp_${faction.toString.toLowerCase}", None, Some(TriggeredEffectLocation(pos, obj.Orientation)) - ) - ) + )) ) //resolve what targets are affected by the emp Zone.serverSideDamage( @@ -588,12 +586,9 @@ class BfrControl(vehicle: Vehicle) //the siphon is not ready to dispatch another emp; chat message borrowed from kit use logic //the client actually enforces a hard limit of 30s before it will react to use of the siphon emp mode //it does not even dispatch the packet before that, making it rare if this precautionary message is seen - events ! VehicleServiceMessage( + events ! MessageEnvelope( obj.Seats(0).occupant.get.Name, - VehicleAction.SendResponse( - GUID0, - ChatMsg(ChatMessageType.UNK_225, wideContents = false, "", s"@TimeUntilNextUse^${30000 - elapsedWait}", None) - ) + SendResponse(ChatMsg(ChatMessageType.UNK_225, wideContents = false, "", s"@TimeUntilNextUse^${30000 - elapsedWait}", None)) ) } case _ => () diff --git a/src/main/scala/net/psforever/objects/vehicles/BfrTransferBehavior.scala b/src/main/scala/net/psforever/objects/vehicles/control/BfrTransferBehavior.scala similarity index 96% rename from src/main/scala/net/psforever/objects/vehicles/BfrTransferBehavior.scala rename to src/main/scala/net/psforever/objects/vehicles/control/BfrTransferBehavior.scala index 24aa6d9c2..db80c1c9a 100644 --- a/src/main/scala/net/psforever/objects/vehicles/BfrTransferBehavior.scala +++ b/src/main/scala/net/psforever/objects/vehicles/control/BfrTransferBehavior.scala @@ -1,18 +1,20 @@ // Copyright (c) 2021 PSForever -package net.psforever.objects.vehicles +package net.psforever.objects.vehicles.control import akka.actor.ActorRef import akka.actor.typed.scaladsl.adapter._ import net.psforever.actors.commands.NtuCommand import net.psforever.actors.zone.BuildingActor -import net.psforever.objects.{NtuContainer, NtuContainerDefinition, _} +import net.psforever.objects._ import net.psforever.objects.definition.ObjectDefinition import net.psforever.objects.equipment.EquipmentSlot import net.psforever.objects.serverobject.resourcesilo.ResourceSilo import net.psforever.objects.serverobject.structures.WarpGate import net.psforever.objects.serverobject.transfer.{TransferBehavior, TransferContainer} +import net.psforever.objects.vehicles.VehicleSubsystem +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.vehicle.VehicleAction import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ @@ -116,9 +118,9 @@ trait BfrTransferBehavior def UpdateNtuUI(vehicle: Vehicle with NtuContainer, siphon: NtuContainer): Unit = { siphon match { case equip: NtuSiphon => - vehicle.Zone.VehicleEvents ! VehicleServiceMessage( + vehicle.Zone.VehicleEvents ! MessageEnvelope( vehicle.Actor.toString, - VehicleAction.InventoryState2(PlanetSideGUID(0), equip.storageGUID, siphon.GUID, siphon.NtuCapacitor.toInt) + VehicleAction.InventoryState2(equip.storageGUID, siphon.GUID, siphon.NtuCapacitor.toInt) ) case _ => ; } diff --git a/src/main/scala/net/psforever/objects/vehicles/CargoBehavior.scala b/src/main/scala/net/psforever/objects/vehicles/control/CargoBehavior.scala similarity index 98% rename from src/main/scala/net/psforever/objects/vehicles/CargoBehavior.scala rename to src/main/scala/net/psforever/objects/vehicles/control/CargoBehavior.scala index 2a9e9cd97..854adc71b 100644 --- a/src/main/scala/net/psforever/objects/vehicles/CargoBehavior.scala +++ b/src/main/scala/net/psforever/objects/vehicles/control/CargoBehavior.scala @@ -1,5 +1,5 @@ // Copyright (c) 2020 PSForever -package net.psforever.objects.vehicles +package net.psforever.objects.vehicles.control import akka.actor.Actor import net.psforever.objects._ diff --git a/src/main/scala/net/psforever/objects/vehicles/control/CargoCarrierControl.scala b/src/main/scala/net/psforever/objects/vehicles/control/CargoCarrierControl.scala index 5892d974f..3609f2775 100644 --- a/src/main/scala/net/psforever/objects/vehicles/control/CargoCarrierControl.scala +++ b/src/main/scala/net/psforever/objects/vehicles/control/CargoCarrierControl.scala @@ -3,7 +3,7 @@ package net.psforever.objects.vehicles.control import net.psforever.objects._ import net.psforever.objects.serverobject.damage.{Damageable, DamageableVehicle} -import net.psforever.objects.vehicles.{Cargo, CarrierBehavior} +import net.psforever.objects.vehicles.Cargo import net.psforever.objects.vital.interaction.DamageResult /** diff --git a/src/main/scala/net/psforever/objects/vehicles/CarrierBehavior.scala b/src/main/scala/net/psforever/objects/vehicles/control/CarrierBehavior.scala similarity index 90% rename from src/main/scala/net/psforever/objects/vehicles/CarrierBehavior.scala rename to src/main/scala/net/psforever/objects/vehicles/control/CarrierBehavior.scala index 93d47cabb..bf3417ce9 100644 --- a/src/main/scala/net/psforever/objects/vehicles/CarrierBehavior.scala +++ b/src/main/scala/net/psforever/objects/vehicles/control/CarrierBehavior.scala @@ -1,17 +1,18 @@ // Copyright (c) 2021 PSForever -package net.psforever.objects.vehicles +package net.psforever.objects.vehicles.control import akka.actor.{Actor, Cancellable} import net.psforever.actors.zone.ZoneActor -import net.psforever.objects.zones.Zone import net.psforever.objects._ import net.psforever.objects.sourcing.VehicleSource +import net.psforever.objects.vehicles.Cargo import net.psforever.objects.vital.VehicleCargoMountActivity +import net.psforever.objects.zones.Zone import net.psforever.packet.game.{CargoMountPointStatusMessage, ObjectAttachMessage, ObjectDetachMessage, PlanetsideAttributeMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse +import net.psforever.services.vehicle.VehicleAction import net.psforever.types.{BailType, CargoStatus, PlanetSideGUID, Vector3} -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.Service -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} import scala.concurrent.duration._ @@ -58,10 +59,9 @@ trait CarrierBehavior { ) { if (iteration == 0) { //open the cargo bay door - obj.Zone.AvatarEvents ! AvatarServiceMessage( + obj.Zone.AvatarEvents ! MessageEnvelope( obj.Zone.id, - AvatarAction.SendResponse( - Service.defaultPlayerGUID, + SendResponse( CargoMountPointStatusMessage( obj.GUID, PlanetSideGUID(0), @@ -206,14 +206,10 @@ object CarrierBehavior { log.debug(s"HandleCheckCargoMounting: mounting cargo vehicle in carrier at distance of $distance") CargoMountAction(carrier, cargo, hold, carrierGUID) cargo.Velocity = None - zone.VehicleEvents ! VehicleServiceMessage( - s"${cargo.Actor}", - VehicleAction.SendResponse(PlanetSideGUID(0), PlanetsideAttributeMessage(cargoGUID, 0, cargo.Health)) - ) - zone.VehicleEvents ! VehicleServiceMessage( - s"${cargo.Actor}", - VehicleAction.SendResponse(PlanetSideGUID(0), PlanetsideAttributeMessage(cargoGUID, cargo.Definition.shieldUiAttribute, cargo.Shields)) - ) + zone.VehicleEvents ! MessageEnvelope(s"${cargo.Actor}", SendResponse(Seq( + PlanetsideAttributeMessage(cargoGUID, 0, cargo.Health), + PlanetsideAttributeMessage(cargoGUID, cargo.Definition.shieldUiAttribute, cargo.Shields) + ))) CargoMountBehaviorForAll(carrier, cargo, mountPoint) zone.actor ! ZoneActor.RemoveFromBlockMap(cargo) false @@ -224,11 +220,10 @@ object CarrierBehavior { ) cargo.Actor ! CargoBehavior.EndCargoMounting(carrierGUID) val cargoDriverGUID = cargo.Seats(0).occupant.get.GUID - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.SendResponse( - cargoDriverGUID, - CargoMountPointStatusMessage( + cargoDriverGUID, + SendResponse(CargoMountPointStatusMessage( carrierGUID, PlanetSideGUID(0), PlanetSideGUID(0), @@ -236,8 +231,7 @@ object CarrierBehavior { mountPoint, CargoStatus.Empty, 0 - ) - ) + )) ) false //sending packet to the cargo vehicle's client results in player being lock in own vehicle @@ -326,11 +320,10 @@ object CarrierBehavior { ) cargo.Actor ! CargoBehavior.EndCargoDismounting(carrierGUID) val cargoDriverGUID = cargo.Seats(0).occupant.get.GUID - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.SendResponse( - cargoDriverGUID, - CargoMountPointStatusMessage( + cargoDriverGUID, + SendResponse(CargoMountPointStatusMessage( carrierGUID, PlanetSideGUID(0), PlanetSideGUID(0), @@ -338,8 +331,7 @@ object CarrierBehavior { mountPoint, CargoStatus.Empty, 0 - ) - ) + )) ) false //sending packet to the cargo vehicle's client results in player being lock in own vehicle @@ -448,18 +440,14 @@ object CarrierBehavior { //the lodestar's cargo hold is almost the center of the vehicle carrier.Position } - val GUID0 = Service.defaultPlayerGUID + val GUID0 = Default.GUID0 val zoneId = zone.id val events = zone.VehicleEvents val cargoActor = cargo.Actor - events ! VehicleServiceMessage( - s"$cargoActor", - VehicleAction.SendResponse(GUID0, PlanetsideAttributeMessage(cargoGUID, 0, cargo.Health)) - ) - events ! VehicleServiceMessage( - s"$cargoActor", - VehicleAction.SendResponse(GUID0, PlanetsideAttributeMessage(cargoGUID, cargo.Definition.shieldUiAttribute, cargo.Shields)) - ) + events ! MessageEnvelope(s"$cargoActor", SendResponse(Seq( + PlanetsideAttributeMessage(cargoGUID, 0, cargo.Health), + PlanetsideAttributeMessage(cargoGUID, cargo.Definition.shieldUiAttribute, cargo.Shields) + ))) zone.actor ! ZoneActor.AddToBlockMap(cargo, carrier.Position) if (carrier.isFlying) { //the carrier vehicle is flying; eject the cargo vehicle @@ -468,9 +456,7 @@ object CarrierBehavior { val detachCargoMsg = ObjectDetachMessage(carrierGUID, cargoGUID, cargoHoldPosition - Vector3.z(1), rotation) val resetCargoMsg = CargoMountPointStatusMessage(carrierGUID, GUID0, GUID0, cargoGUID, mountPoint, CargoStatus.Empty, 0) - events ! VehicleServiceMessage(zoneId, VehicleAction.SendResponse(GUID0, ejectCargoMsg)) - events ! VehicleServiceMessage(zoneId, VehicleAction.SendResponse(GUID0, detachCargoMsg)) - events ! VehicleServiceMessage(zoneId, VehicleAction.SendResponse(GUID0, resetCargoMsg)) + events ! MessageEnvelope(zoneId, SendResponse(Seq(ejectCargoMsg, detachCargoMsg, resetCargoMsg))) log.debug(s"HandleVehicleCargoDismount: eject - $ejectCargoMsg, detach - $detachCargoMsg") if (driverOpt.isEmpty) { //TODO cargo should drop like a rock like normal; until then, deconstruct it @@ -483,18 +469,17 @@ object CarrierBehavior { CargoMountPointStatusMessage(carrierGUID, GUID0, cargoGUID, GUID0, mountPoint, CargoStatus.InProgress, 0) val cargoDetachMessage = ObjectDetachMessage(carrierGUID, cargoGUID, cargoHoldPosition + Vector3.z(1f), rotation) - events ! VehicleServiceMessage(zoneId, VehicleAction.SendResponse(GUID0, cargoStatusMessage)) - events ! VehicleServiceMessage(zoneId, VehicleAction.SendResponse(GUID0, cargoDetachMessage)) + events ! MessageEnvelope(zoneId, SendResponse(Seq(cargoStatusMessage, cargoDetachMessage))) driverOpt match { case Some(driver) => - events ! VehicleServiceMessage( + events ! MessageEnvelope( s"${driver.Name}", - VehicleAction.KickCargo(GUID0, cargo, cargo.Definition.AutoPilotSpeed2, 2500) + VehicleAction.KickCargo(cargo, cargo.Definition.AutoPilotSpeed2, 2500) ) case None => val resetCargoMsg = CargoMountPointStatusMessage(carrierGUID, GUID0, GUID0, cargoGUID, mountPoint, CargoStatus.Empty, 0) - events ! VehicleServiceMessage(zoneId, VehicleAction.SendResponse(GUID0, resetCargoMsg)) //lazy + events ! MessageEnvelope(zoneId, SendResponse(resetCargoMsg)) //lazy //TODO cargo should back out like normal; until then, deconstruct it cargoActor ! Vehicle.Deconstruct() } @@ -610,8 +595,7 @@ object CarrierBehavior { attachMessage: ObjectAttachMessage, mountPointStatusMessage: CargoMountPointStatusMessage ): Unit = { - zone.VehicleEvents ! VehicleServiceMessage(zone.id, VehicleAction.SendResponse(exclude, attachMessage)) - zone.VehicleEvents ! VehicleServiceMessage(zone.id, VehicleAction.SendResponse(exclude, mountPointStatusMessage)) + zone.VehicleEvents ! MessageEnvelope(zone.id, exclude, SendResponse(Seq(attachMessage, mountPointStatusMessage))) } /** @@ -631,14 +615,7 @@ object CarrierBehavior { val zone = carrier.Zone val zoneId = zone.id val msgs @ (attachMessage, mountPointStatusMessage) = CargoMountMessages(carrier, cargo, mountPoint) - zone.VehicleEvents ! VehicleServiceMessage( - zoneId, - VehicleAction.SendResponse(Service.defaultPlayerGUID, attachMessage) - ) - zone.VehicleEvents ! VehicleServiceMessage( - zoneId, - VehicleAction.SendResponse(Service.defaultPlayerGUID, mountPointStatusMessage) - ) + zone.VehicleEvents ! MessageEnvelope(zoneId, SendResponse(Seq(attachMessage, mountPointStatusMessage))) msgs } diff --git a/src/main/scala/net/psforever/objects/vehicles/control/VehicleCapacitance.scala b/src/main/scala/net/psforever/objects/vehicles/control/VehicleCapacitance.scala index 2b84932a9..42bb8a01b 100644 --- a/src/main/scala/net/psforever/objects/vehicles/control/VehicleCapacitance.scala +++ b/src/main/scala/net/psforever/objects/vehicles/control/VehicleCapacitance.scala @@ -3,8 +3,8 @@ package net.psforever.objects.vehicles.control import akka.actor.{Actor, Cancellable} import net.psforever.objects._ -import net.psforever.services.Service -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.PlanetsideAttribute import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ @@ -59,9 +59,9 @@ trait VehicleCapacitance { protected def showCapacitorCharge(): Unit = { val obj = CapacitanceObject - obj.Zone.VehicleEvents ! VehicleServiceMessage( + obj.Zone.VehicleEvents ! MessageEnvelope( self.toString(), - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, obj.GUID, 113, obj.Capacitor) + PlanetsideAttribute(obj.GUID, 113, obj.Capacitor) ) } diff --git a/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala b/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala index adf02e282..6940e3c27 100644 --- a/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala +++ b/src/main/scala/net/psforever/objects/vehicles/control/VehicleControl.scala @@ -36,9 +36,10 @@ import net.psforever.packet.PlanetSideGamePacket import net.psforever.packet.game._ import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent import net.psforever.types._ -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{ObjectDelete, PlanetsideAttribute, SendResponse} +import net.psforever.services.vehicle.VehicleAction import scala.annotation.unused import scala.concurrent.ExecutionContext.Implicits.global @@ -204,8 +205,7 @@ class VehicleControl(vehicle: Vehicle) case Vehicle.UpdateSubsystemStates(toChannel, stateToResolve) => val events = vehicle.Zone.VehicleEvents - val guid0 = Service.defaultPlayerGUID - (stateToResolve match { + val pkts = (stateToResolve match { case Some(state) => vehicle.Subsystems().filter { _.Enabled == state } //only subsystems that are enabled or are disabled case None => @@ -213,7 +213,7 @@ class VehicleControl(vehicle: Vehicle) }) .flatMap { _.getMessage(vehicle) } .foreach { pkt => - events ! VehicleServiceMessage(toChannel, VehicleAction.SendResponse(guid0, pkt)) + events ! MessageEnvelope(toChannel, SendResponse(pkt)) } case FactionAffinity.ConvertFactionAffinity(faction) => @@ -244,11 +244,11 @@ class VehicleControl(vehicle: Vehicle) log.info(s"changing vehicle equipment loadout to ${player.Name}'s option #${msg.unk1 + 1}") val (oldWeapons, newWeapons, oldInventory, finalInventory) = handleTerminalMessageVehicleLoadout(player, definition, weapons, inventory) - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, VehicleAction.ChangeLoadout(vehicle.GUID, oldWeapons, newWeapons, oldInventory, finalInventory) ) - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( player.Name, AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, result = true) ) @@ -256,7 +256,7 @@ class VehicleControl(vehicle: Vehicle) case _ => () } } else { - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( player.Name, AvatarAction.TerminalOrderResult(msg.terminal_guid, msg.transaction_type, result = false) ) @@ -330,9 +330,9 @@ class VehicleControl(vehicle: Vehicle) .orElse { case VehicleControl.Deletion() => val zone = vehicle.Zone - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.UnloadVehicle(Service.defaultPlayerGUID, vehicle, vehicle.GUID) + VehicleAction.UnloadVehicle(vehicle, vehicle.GUID) ) zone.Transport.tell(Zone.Vehicle.Despawn(vehicle), zone.Transport) //notify target spawner that the vehicle has despawned if this is a VR Shooting Range zone @@ -451,7 +451,7 @@ class VehicleControl(vehicle: Vehicle) zone.actor ! ZoneActor.AddToBlockMap(player, vehicle.Position) } if (player.HasGUID) { - events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, unk2 = true, guid)) + events ! MessageEnvelope(zoneId, player.GUID, VehicleAction.KickPassenger(4, unk2 = true, guid)) } } } @@ -521,9 +521,9 @@ class VehicleControl(vehicle: Vehicle) val obj = ContainerObject obj.Find(item) match { case Some(slot) => - obj.Zone.AvatarEvents ! AvatarServiceMessage( + obj.Zone.AvatarEvents ! MessageEnvelope( self.toString, - AvatarAction.SendResponse(Service.defaultPlayerGUID, ObjectAttachMessage(obj.GUID, item.GUID, slot)) + SendResponse(ObjectAttachMessage(obj.GUID, item.GUID, slot)) ) case None => () } @@ -533,9 +533,9 @@ class VehicleControl(vehicle: Vehicle) def RemoveItemFromSlotCallback(item: Equipment, slot: Int): Unit = { val zone = ContainerObject.Zone - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( self.toString, - VehicleAction.UnstowEquipment(Service.defaultPlayerGUID, item.GUID) + VehicleAction.UnstowEquipment(item.GUID) ) } @@ -547,25 +547,22 @@ class VehicleControl(vehicle: Vehicle) val events = zone.VehicleEvents val iguid = item.GUID item.Faction = obj.Faction - events ! VehicleServiceMessage( + events ! MessageEnvelope( //TODO when a new weapon, the equipment slot ui goes blank, but the weapon functions; remount vehicle to correct it if (obj.VisibleSlots.contains(slot)) zone.id else channel, - VehicleAction.SendResponse( - Service.defaultPlayerGUID, - OCM.detailed(item, ObjectCreateMessageParent(oguid, slot)) - ) + SendResponse(OCM.detailed(item, ObjectCreateMessageParent(oguid, slot))) ) item match { case box: AmmoBox => - events ! VehicleServiceMessage( + events ! MessageEnvelope( channel, - VehicleAction.InventoryState2(Service.defaultPlayerGUID, iguid, oguid, box.Capacity) + VehicleAction.InventoryState2(iguid, oguid, box.Capacity) ) case weapon: Tool => weapon.AmmoSlots.map { slot => slot.Box }.foreach { box => - events ! VehicleServiceMessage( + events ! MessageEnvelope( channel, - VehicleAction.InventoryState2(Service.defaultPlayerGUID, box.GUID, iguid, box.Capacity) + VehicleAction.InventoryState2(box.GUID, iguid, box.Capacity) ) } case _ => () @@ -576,9 +573,9 @@ class VehicleControl(vehicle: Vehicle) val obj = ContainerObject val zone = obj.Zone val toChannel = if (obj.VisibleSlots.contains(fromSlot)) zone.id else self.toString - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( toChannel, - VehicleAction.ObjectDelete(item.GUID) + ObjectDelete(item.GUID) ) } @@ -639,9 +636,9 @@ class VehicleControl(vehicle: Vehicle) if (canChargeShields) { vehicle.LogActivity(ShieldCharge(amount, motivator)) vehicle.Shields = vehicle.Shields + amount - vehicle.Zone.VehicleEvents ! VehicleServiceMessage( + vehicle.Zone.VehicleEvents ! MessageEnvelope( s"${vehicle.Actor}", - VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), vehicle.GUID, vehicle.Definition.shieldUiAttribute, vehicle.Shields) + PlanetsideAttribute(vehicle.GUID, vehicle.Definition.shieldUiAttribute, vehicle.Shields) ) } } @@ -694,9 +691,10 @@ class VehicleControl(vehicle: Vehicle) case Some(allow) => val group = AccessPermissionGroup(attribute - 10) log.info(s"$dname changed ${vehicle.Definition.Name}'s access permission $group to $allow") - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.SeatPermissions(dguid, vguid, attribute, value) + dguid, + VehicleAction.SeatPermissions(vguid, attribute, value) ) //kick players who should not be seated in the vehicle due to permission changes if (allow == VehicleLockState.Locked) { //TODO only important permission atm @@ -707,9 +705,10 @@ class VehicleControl(vehicle: Vehicle) if (vehicle.SeatPermissionGroup(seatIndex).contains(group) && !tplayer.Name.equals(dname)) { //can not kick self seat.unmount(tplayer) tplayer.VehicleSeated = None - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( zone.id, - VehicleAction.KickPassenger(tplayer.GUID, 4, unk2 = false, vguid) + tplayer.GUID, + VehicleAction.KickPassenger(4, unk2 = false, vguid) ) } case _ => () // No player seated @@ -755,15 +754,10 @@ class VehicleControl(vehicle: Vehicle) def vehicleSubsystemMessages(messages: List[PlanetSideGamePacket]): Unit = { val zone = vehicle.Zone - val zoneid = zone.id - val events = zone.VehicleEvents - val guid0 = Service.defaultPlayerGUID - messages.foreach { pkt => - events ! VehicleServiceMessage( - zoneid, - VehicleAction.SendResponse(guid0, pkt) - ) - } + zone.VehicleEvents ! MessageEnvelope( + zone.id, + SendResponse(messages) + ) } override protected def canChangeVulnerability(state: Damageable.PersonalVulnerability): Boolean = { diff --git a/src/main/scala/net/psforever/objects/vehicles/interaction/WithEntranceInVehicle.scala b/src/main/scala/net/psforever/objects/vehicles/interaction/WithEntranceInVehicle.scala index 41eed6d99..10009f7dd 100644 --- a/src/main/scala/net/psforever/objects/vehicles/interaction/WithEntranceInVehicle.scala +++ b/src/main/scala/net/psforever/objects/vehicles/interaction/WithEntranceInVehicle.scala @@ -6,6 +6,8 @@ import net.psforever.objects.avatar.interaction.WithEntrance import net.psforever.objects.serverobject.doors.InteriorDoorPassage import net.psforever.objects.serverobject.environment.PieceOfEnvironment import net.psforever.objects.zones.interaction.InteractsWithZone +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse class WithEntranceInVehicle extends WithEntrance() { @@ -39,12 +41,10 @@ class WithEntranceInVehicle private def warnAboutProximity(obj: InteractsWithZone, msg: String): Unit = { import net.psforever.packet.game.ChatMsg - import net.psforever.services.Service - import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.types.ChatMessageType - obj.Zone.AvatarEvents ! AvatarServiceMessage( + obj.Zone.AvatarEvents ! MessageEnvelope( obj.Actor.toString(), - AvatarAction.SendResponse(Service.defaultPlayerGUID, ChatMsg(ChatMessageType.UNK_227, msg)) + SendResponse(ChatMsg(ChatMessageType.UNK_227, msg)) ) } } diff --git a/src/main/scala/net/psforever/objects/zones/MapInfo.scala b/src/main/scala/net/psforever/objects/zones/MapInfo.scala index 31e648aa3..2f85c696b 100644 --- a/src/main/scala/net/psforever/objects/zones/MapInfo.scala +++ b/src/main/scala/net/psforever/objects/zones/MapInfo.scala @@ -2,11 +2,10 @@ package net.psforever.objects.zones import enumeratum.values.{StringEnum, StringEnumEntry} import net.psforever.objects.{PlanetSideGameObject, Player, Vehicle} -import net.psforever.objects.serverobject.environment.{Pool, _} +import net.psforever.objects.serverobject.environment._ import net.psforever.packet.game.{ChatMsg, OffshoreVehicleMessage} -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse import net.psforever.types.{ChatMessageType, PlanetSideEmpire, PlanetSideGUID, Vector3} sealed abstract class MapInfo( @@ -687,23 +686,17 @@ object MapEnvironment { " will be executed for treason." //TODO for bops, eventually } val warning = s"Do not travel any further $trespass of the battlefield or you$punishment" - p.Zone.AvatarEvents ! AvatarServiceMessage( + p.Zone.AvatarEvents ! MessageEnvelope( p.Name, - AvatarAction.SendResponseTargeted( - Service.defaultPlayerGUID, - ChatMsg(ChatMessageType.CMT_QUIT, warning) - ) + SendResponse(ChatMsg(ChatMessageType.CMT_QUIT, warning)) ) case _ => ; } obj match { case v: Vehicle => - v.Zone.VehicleEvents ! VehicleServiceMessage( + v.Zone.VehicleEvents ! MessageEnvelope( v.Actor.toString(), - VehicleAction.SendResponse( - Service.defaultPlayerGUID, - OffshoreVehicleMessage(v.Seats(0).occupant.get.GUID, v.GUID, msg) - ) + SendResponse(OffshoreVehicleMessage(v.Seats(0).occupant.get.GUID, v.GUID, msg)) ) case _ => ; } diff --git a/src/main/scala/net/psforever/objects/zones/Zone.scala b/src/main/scala/net/psforever/objects/zones/Zone.scala index c01c41f05..4e237f06f 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 */ @@ -1613,10 +1624,9 @@ object Zone { if (zone.id.startsWith("tzsh")) { zone.npcPopulation = context.actorOf(Props(classOf[ShootingRangeTargetSpawnerActor], zone), s"$id-npcs") } - - zone.avatarEvents = context.actorOf(Props(classOf[AvatarService], zone), s"$id-avatar-events") - zone.localEvents = context.actorOf(Props(classOf[LocalService], zone), s"$id-local-events") - zone.vehicleEvents = context.actorOf(Props(classOf[VehicleService], zone), s"$id-vehicle-events") + zone.avatarEvents = context.actorOf(AvatarService(), s"$id-avatar-events") + zone.localEvents = context.actorOf(LocalService(zone), s"$id-local-events") + zone.vehicleEvents = context.actorOf(VehicleService(), s"$id-vehicle-events") zone.timeOfDayOrigin = System.currentTimeMillis() @@ -1707,14 +1717,14 @@ object Zone { .flatMap(_.Amenities.filter(_.Definition == GlobalDefinitions.resource_silo)) .collect { case silo: ResourceSilo => - silo.Actor ! Service.Startup() + silo.Actor ! Service.Startup } //some painfields need to look for their closest door buildings.values .flatMap(_.Amenities.filter(_.Definition.isInstanceOf[PainboxDefinition])) .collect { case painbox: Painbox => - painbox.Actor ! Service.Startup() + painbox.Actor ! Service.Startup } //the orbital_buildings in sanctuary zones have to establish their shuttle routes map.shuttleBays @@ -1722,7 +1732,7 @@ object Zone { guid(_) } .collect { case Some(obj: OrbitalShuttlePad) => - obj.Actor ! Service.Startup() + obj.Actor ! Service.Startup } //allocate soi information zone.soi ! SOI.Build() diff --git a/src/main/scala/net/psforever/objects/zones/ZoneDeployableActor.scala b/src/main/scala/net/psforever/objects/zones/ZoneDeployableActor.scala index f5c0197b7..431f67e14 100644 --- a/src/main/scala/net/psforever/objects/zones/ZoneDeployableActor.scala +++ b/src/main/scala/net/psforever/objects/zones/ZoneDeployableActor.scala @@ -10,7 +10,8 @@ import net.psforever.objects.sourcing.ObjectSource import net.psforever.objects.vehicles.MountedWeapons import net.psforever.objects.vital.SpawningActivity import net.psforever.packet.game.ChatMsg -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse import net.psforever.types.ChatMessageType import scala.annotation.tailrec @@ -107,9 +108,9 @@ object ZoneDeployableActor { val position = obj.Position deployableList.find(_ eq obj) match { case _ if Interference.Test(zone, obj).nonEmpty => - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( obj.OwnerName.getOrElse(""), - LocalAction.SendResponse(ChatMsg(ChatMessageType.UNK_227, "@nomove_intersecting")) + SendResponse(ChatMsg(ChatMessageType.UNK_227, "@nomove_intersecting")) ) //may not be the correct message but is sufficient at explaining why the deployable can not be built false case None => diff --git a/src/main/scala/net/psforever/objects/zones/ZoneGroundActor.scala b/src/main/scala/net/psforever/objects/zones/ZoneGroundActor.scala index cf41f29d7..84f44a3d0 100644 --- a/src/main/scala/net/psforever/objects/zones/ZoneGroundActor.scala +++ b/src/main/scala/net/psforever/objects/zones/ZoneGroundActor.scala @@ -4,9 +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.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction import scala.annotation.tailrec import scala.collection.mutable.ListBuffer @@ -31,10 +31,7 @@ class ZoneGroundActor(zone: Zone, equipmentOnGround: ListBuffer[Equipment]) exte equipmentOnGround += item item.Position = pos item.Orientation = orient - zone.AvatarEvents ! AvatarServiceMessage( - zone.id, - AvatarAction.DropItem(Service.defaultPlayerGUID, item) - ) + zone.AvatarEvents ! DropItemEnvelope(zone.id, AvatarAction.DropItem(item), zone) zone.actor ! ZoneActor.AddToBlockMap(item, pos) Zone.Ground.ItemOnGround(item, pos, orient) }) @@ -42,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 ! AvatarServiceMessage(zone.id, AvatarAction.PickupItem(Service.defaultPlayerGUID, item, 0)) + zone.AvatarEvents ! PickupItemEnvelope(zone.id, AvatarAction.PickupItem(item, 0), zone) zone.actor ! ZoneActor.RemoveFromBlockMap(item) Zone.Ground.ItemInHand(item) case None => @@ -54,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 ! AvatarServiceMessage(zone.id, AvatarAction.PickupItem(Service.defaultPlayerGUID, item, 0)) + zone.AvatarEvents ! PickupItemEnvelope(zone.id, AvatarAction.PickupItem(item, 0), zone) case None => ; } diff --git a/src/main/scala/net/psforever/objects/zones/ZoneHotSpotProjector.scala b/src/main/scala/net/psforever/objects/zones/ZoneHotSpotProjector.scala index a22b32314..eb4b69958 100644 --- a/src/main/scala/net/psforever/objects/zones/ZoneHotSpotProjector.scala +++ b/src/main/scala/net/psforever/objects/zones/ZoneHotSpotProjector.scala @@ -5,6 +5,9 @@ import akka.actor.{Actor, ActorRef, Cancellable, Props} import net.psforever.objects.Default import net.psforever.types.{PlanetSideEmpire, Vector3} import net.psforever.services.ServiceManager +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.galaxy.GalaxyAction + import scala.collection.mutable.ListBuffer import scala.concurrent.duration._ @@ -302,12 +305,13 @@ class ZoneHotSpotProjector(zone: Zone, hotspots: ListBuffer[HotSpotInfo], blanki def UpdateHotSpots(affectedFactions: Iterable[PlanetSideEmpire.Value], hotSpotInfos: Iterable[HotSpotInfo]): Unit = { val zoneNumber = zone.Number val hotSpotInfoList = hotSpotInfos.toList - affectedFactions.foreach(faction => - galaxy ! Zone.HotSpot.Update( - faction, - zoneNumber, - 1, - ZoneHotSpotProjector.SpecificHotSpotInfo(faction, hotSpotInfoList) + galaxy ! BundledEnvelope( + affectedFactions.map(faction => + MessageEnvelope(faction.toString, GalaxyAction.HotSpotUpdate( + zoneNumber, + 1, + ZoneHotSpotProjector.SpecificHotSpotInfo(faction, hotSpotInfoList) + )) ) ) } diff --git a/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala b/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala index e972808d6..c276cf0c3 100644 --- a/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala +++ b/src/main/scala/net/psforever/objects/zones/ZonePopulationActor.scala @@ -7,7 +7,9 @@ import net.psforever.objects.avatar.{AvatarBot, CorpseControl, PlayerControl} import net.psforever.objects.sourcing.PlayerSource import net.psforever.objects.vital.{InGameHistory, SpawningActivity} import net.psforever.objects.{Default, Player} -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.ObjectDelete import net.psforever.types.Vector3 import scala.collection.concurrent.TrieMap @@ -88,9 +90,9 @@ class ZonePopulationActor(zone: Zone, playerMap: TrieMap[Int, Option[Player]], b if (BotSpawn(bot, botList)) { bot.Zone = zone zone.actor ! ZoneActor.AddToBlockMap(bot, bot.Position) - zone.AvatarEvents ! AvatarServiceMessage( - zone.id, - AvatarAction.LoadPlayer(bot.GUID, bot.Definition.ObjectId, bot.GUID, bot.Definition.Packet.ConstructorData(bot).get, None) + zone.AvatarEvents ! MessageEnvelope( + zone.id, bot.GUID, + AvatarAction.LoadPlayer(bot.Definition.ObjectId, bot.GUID, bot.Definition.Packet.ConstructorData(bot).get, None) ) } @@ -99,9 +101,9 @@ class ZonePopulationActor(zone: Zone, playerMap: TrieMap[Int, Option[Player]], b if (bot.Actor != null) bot.Actor ! akka.actor.PoisonPill bot.Actor = Default.Actor zone.actor ! ZoneActor.RemoveFromBlockMap(bot) - zone.AvatarEvents ! AvatarServiceMessage( - zone.id, - AvatarAction.ObjectDelete(bot.GUID, bot.GUID, unk=0) + zone.AvatarEvents ! MessageEnvelope( + zone.id, bot.GUID, + ObjectDelete(bot.GUID, unk=0) ) } diff --git a/src/main/scala/net/psforever/objects/zones/ZoneProjectileActor.scala b/src/main/scala/net/psforever/objects/zones/ZoneProjectileActor.scala index 1fc6deb58..6d8e509e8 100644 --- a/src/main/scala/net/psforever/objects/zones/ZoneProjectileActor.scala +++ b/src/main/scala/net/psforever/objects/zones/ZoneProjectileActor.scala @@ -2,10 +2,12 @@ package net.psforever.objects.zones import akka.actor.{Actor, Cancellable} +import net.psforever.objects.Default import net.psforever.objects.ballistics.Projectile import net.psforever.objects.guid.{GUIDTask, StraightforwardTask, TaskBundle, TaskWorkflow} -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.ObjectDelete import net.psforever.types.PlanetSideGUID import scala.collection.mutable @@ -110,7 +112,7 @@ class ZoneProjectileActor( TaskBundle( reg.mainTask, TaskBundle( - reg.subTasks(0).mainTask, + reg.subTasks.head.mainTask, unregisterProjectile(obj) ) ) @@ -142,11 +144,11 @@ class ZoneProjectileActor( projectileList.addOne(projectile) val (clarifiedFilterGuid, duration) = if (definition.radiation_cloud) { zone.blockMap.addTo(projectile) - (Service.defaultPlayerGUID, projectile.profile.Lifespan seconds) + (Default.GUID0, projectile.profile.Lifespan seconds) } else if (definition.RemoteClientData == (0,0)) { //remote projectiles that are not radiation clouds have lifespans controlled by the controller (user) //this projectile has defaulted remote client data - (Service.defaultPlayerGUID, projectile.profile.Lifespan * 1.5f seconds) + (Default.GUID0, projectile.profile.Lifespan * 1.5f seconds) } else { //remote projectiles that are not radiation clouds have lifespans controlled by the controller (user) //if the controller fails, the projectile has a bit more than its normal lifespan before automatic clean up @@ -156,10 +158,10 @@ class ZoneProjectileActor( projectileGuid, context.system.scheduler.scheduleOnce(duration, self, ZoneProjectile.Remove(projectileGuid)) ) - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, + clarifiedFilterGuid, AvatarAction.LoadProjectile( - clarifiedFilterGuid, definition.ObjectId, projectileGuid, definition.Packet.ConstructorData(projectile).get @@ -188,19 +190,19 @@ class ZoneProjectileActor( projectileList.remove(projectileList.indexOf(projectile)) if (projectile.Definition.radiation_cloud) { zone.blockMap.removeFrom(projectile) - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.ObjectDelete(PlanetSideGUID(0), projectile_guid, 2) + ObjectDelete(projectile_guid, 2) ) } else if (projectile.Definition.RemoteClientData == (0,0)) { - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.ObjectDelete(PlanetSideGUID(0), projectile_guid, 2) + ObjectDelete(projectile_guid, 2) ) } else { - zone.AvatarEvents ! AvatarServiceMessage( + zone.AvatarEvents ! MessageEnvelope( zone.id, - AvatarAction.ProjectileExplodes(PlanetSideGUID(0), projectile_guid, projectile) + AvatarAction.ProjectileExplodes(projectile_guid, projectile) ) } } diff --git a/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala b/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala index 29dacff8d..eba37613a 100644 --- a/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala +++ b/src/main/scala/net/psforever/objects/zones/ZoneVehicleActor.scala @@ -9,8 +9,8 @@ import net.psforever.objects.serverobject.structures.WarpGate import net.psforever.objects.vital.InGameHistory import net.psforever.objects.{Default, GlobalDefinitions, Vehicle} import net.psforever.packet.game.ChatMsg -import net.psforever.services.Service -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse import net.psforever.types.{ChatMessageType, DriveState, PlanetSideEmpire, Vector3} import scala.annotation.tailrec @@ -204,9 +204,9 @@ object ZoneVehicleActor { else None } msgOpt.foreach { msg => - zone.VehicleEvents ! VehicleServiceMessage( + zone.VehicleEvents ! MessageEnvelope( vehicle.Seats.headOption.flatMap(_._2.occupant).map(_.Name).getOrElse(""), - VehicleAction.SendResponse(Service.defaultPlayerGUID, ChatMsg(ChatMessageType.UNK_227, msg)) + SendResponse(ChatMsg(ChatMessageType.UNK_227, msg)) ) } msgOpt.isDefined diff --git a/src/main/scala/net/psforever/objects/zones/exp/KillAssists.scala b/src/main/scala/net/psforever/objects/zones/exp/KillAssists.scala index 91a4d3a67..55b1b17be 100644 --- a/src/main/scala/net/psforever/objects/zones/exp/KillAssists.scala +++ b/src/main/scala/net/psforever/objects/zones/exp/KillAssists.scala @@ -6,7 +6,8 @@ import net.psforever.objects.avatar.scoring.{Assist, Death, KDAStat, Kill} import net.psforever.objects.sourcing.{PlayerSource, SourceEntry} import net.psforever.objects.vital.interaction.{Adversarial, DamageResult} import net.psforever.objects.vital.{DamagingActivity, HealingActivity, InGameActivity, RepairingActivity, RevivingActivity, SpawningActivity} -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} import net.psforever.types.PlanetSideEmpire import net.psforever.util.Config @@ -42,7 +43,6 @@ object KillAssists { * @param eventBus where to send the results of the experience determination(s) * @see `ActorRef` * @see `AvatarAction.UpdateKillsDeathsAssists` - * @see `AvatarServiceMessage` * @see `DamageResult` * @see `rewardThisPlayerDeath` */ @@ -52,9 +52,9 @@ object KillAssists { history: Iterable[InGameActivity], eventBus: ActorRef ): Unit = { - rewardThisPlayerDeath(victim, lastDamage, history).foreach { case (p, kda) => - eventBus ! AvatarServiceMessage(p.Name, AvatarAction.UpdateKillsDeathsAssists(p.CharId, kda)) - } + eventBus ! BundledEnvelope(rewardThisPlayerDeath(victim, lastDamage, history).map { case (p, kda) => + MessageEnvelope(p.Name, AvatarAction.UpdateKillsDeathsAssists(p.CharId, kda)) + }) } /** diff --git a/src/main/scala/net/psforever/objects/zones/exp/KillContributions.scala b/src/main/scala/net/psforever/objects/zones/exp/KillContributions.scala index 441d28f8c..25e79e3bf 100644 --- a/src/main/scala/net/psforever/objects/zones/exp/KillContributions.scala +++ b/src/main/scala/net/psforever/objects/zones/exp/KillContributions.scala @@ -8,7 +8,8 @@ import net.psforever.objects.sourcing.{BuildingSource, MountableEntry, PlayerSou import net.psforever.objects.vital.{Contribution, InGameActivity, RevivingActivity, TelepadUseActivity, TerminalUsedActivity, VehicleCargoDismountActivity, VehicleCargoMountActivity, DismountingActivity, MountingActivity} import net.psforever.objects.vital.projectile.ProjectileReason import net.psforever.objects.zones.exp.rec.{CombinedHealthAndArmorContributionProcess, MachineRecoveryExperienceContributionProcess} -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.types.{PlanetSideEmpire, Vector3} import net.psforever.util.Config @@ -73,7 +74,6 @@ object KillContributions { * @param eventBus where to send the results of the experience determination(s) * @see `ActorRef` * @see `AvatarAction.UpdateKillsDeathsAssists` - * @see `AvatarServiceMessage` * @see `rewardTheseSupporters` * @see `SupportActivity` */ @@ -88,7 +88,7 @@ object KillContributions { //take the output and transform that into contribution distribution data rewardTheseSupporters(target, history, kill, bep) .foreach { case (charId, ContributionStatsOutput(player, weapons, exp)) => - eventBus ! AvatarServiceMessage( + eventBus ! MessageEnvelope( player.Name, AvatarAction.UpdateKillsDeathsAssists(charId, SupportActivity(victim, weapons, exp.toLong)) ) @@ -108,7 +108,6 @@ object KillContributions { * @see `ActorRef` * @see `additionalContributionSources` * @see `AvatarAction.UpdateKillsDeathsAssists` - * @see `AvatarServiceMessage` * @see `CombinedHealthAndArmorContributionProcess` * @see `composeContributionOutput` * @see `initialScoring` diff --git a/src/main/scala/net/psforever/packet/game/HackMessage.scala b/src/main/scala/net/psforever/packet/game/HackMessage.scala index b5d0c7e7b..7a5ee8f38 100644 --- a/src/main/scala/net/psforever/packet/game/HackMessage.scala +++ b/src/main/scala/net/psforever/packet/game/HackMessage.scala @@ -129,18 +129,6 @@ final case class HackMessage( } object HackMessage extends Marshallable[HackMessage] { - def apply( - unk1: HackState1, - target_guid: PlanetSideGUID, - player_guid: PlanetSideGUID, - progress: Int, - unk5: Int, - hack_state: HackState, - unk7: HackState7 - ): HackMessage = { - new HackMessage(unk1, target_guid, player_guid, progress, unk5.toFloat, hack_state, unk7) - } - implicit val codec: Codec[HackMessage] = ( ("unk1" | HackState1.codec) :: ("object_guid" | PlanetSideGUID.codec) :: diff --git a/src/main/scala/net/psforever/services/CavernRotationService.scala b/src/main/scala/net/psforever/services/CavernRotationService.scala index 824a817b1..ef3c83d3b 100644 --- a/src/main/scala/net/psforever/services/CavernRotationService.scala +++ b/src/main/scala/net/psforever/services/CavernRotationService.scala @@ -12,7 +12,9 @@ import net.psforever.objects.Default import net.psforever.objects.serverobject.structures.{Building, WarpGate} import net.psforever.objects.zones.Zone import net.psforever.packet.game.ChatMsg -import net.psforever.services.galaxy.{GalaxyAction, GalaxyResponse, GalaxyServiceMessage, GalaxyServiceResponse} +import net.psforever.services.base.envelope.{BundledEnvelope, GenericResponseEnvelope, MessageEnvelope} +import net.psforever.services.base.message.SendResponse +import net.psforever.services.galaxy.{GalaxyAction, GalaxyStamp} import net.psforever.types.ChatMessageType import net.psforever.util.Config import net.psforever.zones.Zones @@ -89,7 +91,7 @@ object CavernRotationService { */ private def closedCavernWarning(zone: ZoneMonitor, counter: Int, galaxyService: ActorRef): Boolean = { if (!zone.locked) { - galaxyService ! GalaxyServiceMessage(GalaxyAction.SendResponse( + galaxyService ! MessageEnvelope("", SendResponse( ChatMsg(ChatMessageType.UNK_229, s"@cavern_closing_warning^@${zone.zone.id}~^@$counter~") )) true @@ -557,36 +559,35 @@ class CavernRotationService( val (lockedZones, unlockedZones) = managedZones.partition(_.locked) //borrow GalaxyService response structure, but send to the specific endpoint math.max(0, monitor.start + monitor.duration - curr) unlockedZones.foreach { monitor => - sendToSession ! GalaxyServiceResponse("", GalaxyResponse.UnlockedZoneUpdate(monitor.zone)) + val resp = GalaxyAction.UnlockedZoneUpdate(monitor.zone) + sendToSession ! GenericResponseEnvelope(GalaxyStamp, "", Default.GUID0, resp) } val sortedLocked = lockedZones.sortBy(z => z.start) sortedLocked.take(2).foreach { monitor => - sendToSession ! GalaxyServiceResponse( - "", - GalaxyResponse.LockedZoneUpdate(monitor.zone, math.max(0, monitor.start + monitor.duration - curr)) - ) + val resp = GalaxyAction.LockedZoneUpdate(monitor.zone, math.max(0, monitor.start + monitor.duration - curr)) + sendToSession ! GenericResponseEnvelope(GalaxyStamp, "", Default.GUID0, resp) } sortedLocked.takeRight(2).foreach { monitor => - sendToSession ! GalaxyServiceResponse( - "", - GalaxyResponse.LockedZoneUpdate(monitor.zone, 0L) - ) + val resp = GalaxyAction.LockedZoneUpdate(monitor.zone, 0L) + sendToSession ! GenericResponseEnvelope(GalaxyStamp, "", Default.GUID0, resp) } } def sendCavernRotationUpdatesToAll(galaxyService: ActorRef): Unit = { val curr = System.currentTimeMillis() val (lockedZones, unlockedZones) = managedZones.partition(_.locked) - unlockedZones.foreach { z => - galaxyService ! GalaxyServiceMessage(GalaxyAction.UnlockedZoneUpdate(z.zone)) - } val sortedLocked = lockedZones.sortBy(z => z.start) - sortedLocked.take(2).foreach { z => - galaxyService ! GalaxyServiceMessage(GalaxyAction.LockedZoneUpdate(z.zone, z.start + z.duration - curr)) - } - sortedLocked.takeRight(2).foreach { z => - galaxyService ! GalaxyServiceMessage(GalaxyAction.LockedZoneUpdate(z.zone, 0L)) - } + galaxyService ! BundledEnvelope( + unlockedZones.map { z => + MessageEnvelope("", GalaxyAction.UnlockedZoneUpdate(z.zone)) + } ++ + sortedLocked.take(2).map { z => + MessageEnvelope("", GalaxyAction.LockedZoneUpdate(z.zone, z.start + z.duration - curr)) + } ++ + sortedLocked.takeRight(2).map { z => + MessageEnvelope("", GalaxyAction.LockedZoneUpdate(z.zone, 0L)) + } + ) } /** @@ -654,8 +655,8 @@ class CavernRotationService( val unlocking = managedZones(nextToUnlock) val lockingZone = locking.zone val unlockingZone = unlocking.zone - val fullHoursBetweenRotationsAsHours = timeToCompleteAllRotationsHours.hours - val fullHoursBetweenRotationsAsMillis = fullHoursBetweenRotationsAsHours.toMillis + //val fullHoursBetweenRotationsAsHours = timeToCompleteAllRotationsHours.hours + //val fullHoursBetweenRotationsAsMillis = fullHoursBetweenRotationsAsHours.toMillis val hoursBetweenRotationsAsHours = timeBetweenRotationsHours.hours val prevToLock = nextToLock nextToLock = (nextToLock + 1) % managedZones.size @@ -670,7 +671,7 @@ class CavernRotationService( lockTimerToDisplayWarning(hoursBetweenRotationsAsHours - firstClosingWarningAtMinutes.minutes) //alert clients to change if (lockingZone ne unlockingZone) { - galaxyService ! GalaxyServiceMessage(GalaxyAction.SendResponse( + galaxyService ! MessageEnvelope("", SendResponse( ChatMsg(ChatMessageType.UNK_229, s"@cavern_switched^@${lockingZone.id}~^@${unlockingZone.id}") )) //change warp gate statuses to reflect zone lock state @@ -729,7 +730,7 @@ class CavernRotationService( advanceTimeBy: FiniteDuration, galaxyService: ActorRef ) : Unit = { - val curr = System.currentTimeMillis() + //val curr = System.currentTimeMillis() val advanceByTimeAsMillis = advanceTimeBy.toMillis managedZones.foreach { zone => zone.start = zone.start - advanceByTimeAsMillis diff --git a/src/main/scala/net/psforever/services/Service.scala b/src/main/scala/net/psforever/services/Service.scala index 48d7a2f53..68e5779ea 100644 --- a/src/main/scala/net/psforever/services/Service.scala +++ b/src/main/scala/net/psforever/services/Service.scala @@ -1,37 +1,20 @@ // Copyright (c) 2017 PSForever package net.psforever.services -import akka.event.{ActorEventBus, SubchannelClassification} -import akka.util.Subclassification -import net.psforever.types.PlanetSideGUID +import akka.actor.ActorRef object Service { - final val defaultPlayerGUID: PlanetSideGUID = PlanetSideGUID(0) + case object Startup - final case class Startup() + final case class Join(channel: String, sendJoinConfirmation: Boolean) - final case class Join(channel: String) - final case class Leave(channel: Option[String] = None) - final case class LeaveAll() -} - -trait GenericEventBusMsg { - def channel: String -} - -class GenericEventBus[A <: GenericEventBusMsg] extends ActorEventBus with SubchannelClassification { - type Event = A - type Classifier = String - - protected def classify(event: Event): Classifier = event.channel - - protected def subclassification = - new Subclassification[Classifier] { - def isEqual(x: Classifier, y: Classifier) = x == y - def isSubclass(x: Classifier, y: Classifier) = x.startsWith(y) - } - - protected def publish(event: Event, subscriber: Subscriber): Unit = { - subscriber ! event + object Join { + def apply(channel: String): Join = Join(channel, sendJoinConfirmation = false) } + + final case class JoinConfirmation(eventSystem: ActorRef, channel: String) + + final case class Leave(channel: String) + + case object LeaveAll } diff --git a/src/main/scala/net/psforever/services/account/AccountPersistenceService.scala b/src/main/scala/net/psforever/services/account/AccountPersistenceService.scala index 3ff3206af..6c452e91d 100644 --- a/src/main/scala/net/psforever/services/account/AccountPersistenceService.scala +++ b/src/main/scala/net/psforever/services/account/AccountPersistenceService.scala @@ -15,8 +15,10 @@ import net.psforever.objects.zones.Zone import net.psforever.persistence import net.psforever.types.Vector3 import net.psforever.services.{Service, ServiceManager} -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.ObjectDelete +import net.psforever.services.galaxy.GalaxyAction import net.psforever.zones.Zones import scala.util.Success @@ -367,7 +369,7 @@ class PersistenceMonitor( (inZone.Players.find(p => p.name == name), inZone.AllPlayers.find(p => p.Name == name)) match { case (Some(avatar), Some(player)) if player.VehicleSeated.nonEmpty => //in case the player is holding the llu and disconnects - player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.DropSpecialItem()) + player.Zone.AvatarEvents ! MessageEnvelope(player.Name, AvatarAction.DropSpecialItem()) //alive or dead in a vehicle //if the avatar is dead while in a vehicle, they haven't released yet AvatarActor.saveAvatarData(avatar) @@ -385,7 +387,7 @@ class PersistenceMonitor( case (Some(avatar), Some(player)) => //in case the player is holding the llu and disconnects - player.Zone.AvatarEvents ! AvatarServiceMessage(player.Name, AvatarAction.DropSpecialItem()) + player.Zone.AvatarEvents ! MessageEnvelope(player.Name, AvatarAction.DropSpecialItem()) //alive or dead, as standard Infantry AvatarActor.saveAvatarData(avatar) AvatarActor.finalSavePlayerData(player) @@ -414,8 +416,7 @@ class PersistenceMonitor( * As this persistence monitor is about to become invalid, * any messages sent in response to what we are sending are received by the monitor's parent. * @see `Avatar` - * @see `AvatarAction.ObjectDelete` - * @see `AvatarServiceMessage` + * @see `ObjectDelete` * @see `GUIDTask.UnregisterPlayer` * @see `Player` * @see `Zone.AvatarEvents` @@ -434,7 +435,7 @@ class PersistenceMonitor( case _ => ; } inZone.Population.tell(Zone.Population.Release(avatar), parent) - inZone.AvatarEvents.tell(AvatarServiceMessage(inZone.id, AvatarAction.ObjectDelete(pguid, pguid)), parent) + inZone.AvatarEvents.tell(MessageEnvelope(inZone.id, pguid, ObjectDelete(pguid)), parent) TaskWorkflow.execute(GUIDTask.unregisterPlayer(inZone.GUID, player)) //inZone.tasks.tell(GUIDTask.UnregisterPlayer(player)(inZone.GUID), parent) AvatarLogout(avatar) @@ -452,8 +453,8 @@ class PersistenceMonitor( */ def AvatarLogout(avatar: Avatar): Unit = { LivePlayerList.Remove(avatar.id) - squadService.tell(Service.Leave(Some(avatar.id.toString)), context.parent) - galaxyService.tell(GalaxyServiceMessage(GalaxyAction.LogStatusChange(avatar.name)), context.parent) + squadService.tell(Service.Leave(avatar.id.toString), context.parent) + galaxyService.tell(MessageEnvelope(GalaxyAction.LogStatusChange(avatar.name)), context.parent) Deployables.Disown(inZone, avatar, context.parent) inZone.Population.tell(Zone.Population.Leave(avatar), context.parent) TaskWorkflow.execute(GUIDTask.unregisterObject(inZone.GUID, avatar.locker)) diff --git a/src/main/scala/net/psforever/services/avatar/AvatarAction.scala b/src/main/scala/net/psforever/services/avatar/AvatarAction.scala new file mode 100644 index 000000000..593fd920d --- /dev/null +++ b/src/main/scala/net/psforever/services/avatar/AvatarAction.scala @@ -0,0 +1,205 @@ +// Copyright (c) 2017-2026 PSForever +package net.psforever.services.avatar + +import net.psforever.objects.{Player, Vehicle} +import net.psforever.objects.avatar.scoring.KDAStat +import net.psforever.objects.ballistics.Projectile +import net.psforever.objects.equipment.Equipment +import net.psforever.objects.inventory.InventoryItem +import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget +import net.psforever.objects.sourcing.{SourceEntry, UniquePlayer} +import net.psforever.objects.vital.interaction.DamageResult +import net.psforever.objects.zones.Zone +import net.psforever.packet.game.{ImplantAction, ObjectCreateMessage} +import net.psforever.packet.game.objectcreate.{ConstructorData, DroppedItemData, ObjectCreateMessageParent, PlacementData} +import net.psforever.services.base.message.{EventMessage, EventResponse, ObjectDelete, SelfRespondingEvent} +import net.psforever.types.{ExoSuitType, ExperienceType, PlanetSideGUID, TransactionType, Vector3} + +import scala.concurrent.duration.FiniteDuration + +object AvatarAction { + final case class ArmorChanged(suit: ExoSuitType.Value, subtype: Int) extends SelfRespondingEvent + + final case class AvatarImplant(action: ImplantAction.Value, implantSlot: Int, status: Int) extends SelfRespondingEvent + + final case class ChangeFireMode(item_guid: PlanetSideGUID, mode: Int) extends SelfRespondingEvent + + final case class EnvironmentalDamage(player_guid: PlanetSideGUID, source_guid: PlanetSideGUID, amount: Int) extends SelfRespondingEvent + + final case class DeactivateImplantSlot(player_guid: PlanetSideGUID, slot: Int) extends SelfRespondingEvent + + final case class ActivateImplantSlot(player_guid: PlanetSideGUID, slot: Int) extends SelfRespondingEvent + + final case class Destroy(victim: PlanetSideGUID, killer: PlanetSideGUID, weapon: PlanetSideGUID, pos: Vector3) extends SelfRespondingEvent + + final case class DestroyDisplay(killer: SourceEntry, victim: SourceEntry, method: Int, unk: Int = 121) extends SelfRespondingEvent + + final case class DropCreatedItem(packet: ObjectCreateMessage) extends EventResponse + + final case class DropItem(item: Equipment) extends EventMessage { + def response(): EventResponse = { + val definition = item.Definition + val objectData = DroppedItemData( + PlacementData(item.Position, item.Orientation), + definition.Packet.ConstructorData(item).get + ) + AvatarAction.DropCreatedItem(ObjectCreateMessage(definition.ObjectId, item.GUID, objectData)) + } + } + + final case class EquipmentCreatedInHand(packet: ObjectCreateMessage) extends EventResponse + + final case class EquipmentInHand(target_guid: PlanetSideGUID, slot: Int, item: Equipment) extends EventMessage { + def response(): EventResponse = { + val definition = item.Definition + val containerData = ObjectCreateMessageParent(target_guid, slot) + val objectData = definition.Packet.ConstructorData(item).get + AvatarAction.EquipmentCreatedInHand( + ObjectCreateMessage(definition.ObjectId, item.GUID, containerData, objectData) + ) + } + } + + final case class Killed(cause: DamageResult, mount_guid: Option[PlanetSideGUID]) extends SelfRespondingEvent + + final case class LoadCreatedPlayer(pkt: ObjectCreateMessage) extends EventResponse + + final case class LoadPlayer( + object_id: Int, + target_guid: PlanetSideGUID, + cdata: ConstructorData, + pdata: Option[ObjectCreateMessageParent] + ) extends EventMessage { + def response(): EventResponse = { + val pkt = pdata match { + case Some(data) => + ObjectCreateMessage(object_id, target_guid, data, cdata) + case None => + ObjectCreateMessage(object_id, target_guid, cdata) + } + LoadCreatedPlayer(pkt) + } + } + + final case class LoadCreatedProjectile(pkt: ObjectCreateMessage) extends EventResponse + + final case class LoadProjectile( + object_id: Int, + projectile_guid: PlanetSideGUID, + cdata: ConstructorData + ) extends EventMessage { + def response(): EventResponse = { + LoadCreatedProjectile(ObjectCreateMessage(object_id, projectile_guid, cdata)) + } + } + + final case class ObjectHeld(slot: Int, previousSLot: Int) extends SelfRespondingEvent + + final case class OxygenState(player: OxygenStateTarget, vehicle: Option[OxygenStateTarget]) extends SelfRespondingEvent + + final case class PlanetsideStringAttribute(attribute_type: Int, attribute_value: String) extends SelfRespondingEvent + + final case class PlayerState( + pos: Vector3, + vel: Option[Vector3], + facingYaw: Float, + facingPitch: Float, + facingYawUpper: Float, + timestamp: Int, + is_crouching: Boolean, + is_jumping: Boolean, + jump_thrust: Boolean, + is_cloaked: Boolean, + spectator: Boolean, + weaponInHand: Boolean + ) extends SelfRespondingEvent + + final case class PickupItem(item: Equipment, unk: Int = 0) extends EventMessage { + def response(): EventResponse = { + ObjectDelete(item.GUID, unk) + } + } + + final case class ProjectileAutoLockAwareness(mode: Int) extends SelfRespondingEvent + + final case class ProjectileExplodes(projectile_guid: PlanetSideGUID, projectile: Projectile) extends SelfRespondingEvent + + final case class ProjectileState( + projectile_guid: PlanetSideGUID, + shot_pos: Vector3, + shot_vel: Vector3, + shot_orient: Vector3, + sequence: Int, + end: Boolean, + hit_target: PlanetSideGUID + ) extends SelfRespondingEvent + + final case class PutDownFDU(player_guid: PlanetSideGUID) extends SelfRespondingEvent + + final case class ReleasePlayer(player: Player) extends EventResponse + + final case class Release(player: Player, zone: Zone, time: Option[FiniteDuration] = None) extends EventMessage { + def response(): EventResponse = { + ReleasePlayer(player) + } + } + + final case class Revive(target_guid: PlanetSideGUID) extends SelfRespondingEvent + + final case class StowEquipment(target_guid: PlanetSideGUID, slot: Int, item: Equipment) + extends SelfRespondingEvent + + final case class TerminalOrderResult(terminal_guid: PlanetSideGUID, action: TransactionType.Value, result: Boolean) + extends SelfRespondingEvent + + final case class ChangeExosuit( + target_guid: PlanetSideGUID, + armor: Int, + exosuit: ExoSuitType.Value, + subtype: Int, + last_drawn_slot: Int, + new_max_hand: Boolean, + old_holsters: List[(Equipment, PlanetSideGUID)], + holsters: List[InventoryItem], + old_inventory: List[(Equipment, PlanetSideGUID)], + inventory: List[InventoryItem], + drop: List[InventoryItem], + delete: List[(Equipment, PlanetSideGUID)] + ) extends SelfRespondingEvent + + final case class ChangeLoadout( + target_guid: PlanetSideGUID, + armor: Int, + exosuit: ExoSuitType.Value, + subtype: Int, + last_drawn_slot: Int, + new_max_hand: Boolean, + old_holsters: List[(Equipment, PlanetSideGUID)], + holsters: List[InventoryItem], + old_inventory: List[(Equipment, PlanetSideGUID)], + inventory: List[InventoryItem], + drop: List[InventoryItem] + ) extends SelfRespondingEvent + + final case class DropSpecialItem() extends SelfRespondingEvent + + final case class UseKit(kit_guid: PlanetSideGUID, kit_objid: Int) extends SelfRespondingEvent + + final case class KitNotUsed(kit_guid: PlanetSideGUID, msg: String) extends SelfRespondingEvent + + final case class UpdateKillsDeathsAssists(charId: Long, kda: KDAStat) extends SelfRespondingEvent + + final case class AwardBep(charId: Long, bep: Long, expType: ExperienceType) extends SelfRespondingEvent + + final case class AwardCep(charId: Long, bep: Long) extends SelfRespondingEvent + + final case class FacilityCaptureRewards(building_id: Int, zone_number: Int, exp: Long) extends SelfRespondingEvent + + final case class ShareKillExperienceWithSquad(killer: Player, exp: Long) extends SelfRespondingEvent + + final case class ShareAntExperienceWithSquad(owner: UniquePlayer, exp: Long, vehicle: Vehicle) extends SelfRespondingEvent + + final case class RemoveFromOutfitChat(outfit_id: Long) extends SelfRespondingEvent + + final case object TeardownConnection extends SelfRespondingEvent +} diff --git a/src/main/scala/net/psforever/services/avatar/AvatarService.scala b/src/main/scala/net/psforever/services/avatar/AvatarService.scala index bbdf1f99a..9c56bf9c0 100644 --- a/src/main/scala/net/psforever/services/avatar/AvatarService.scala +++ b/src/main/scala/net/psforever/services/avatar/AvatarService.scala @@ -1,521 +1,18 @@ // Copyright (c) 2017 PSForever package net.psforever.services.avatar -import akka.actor.{Actor, ActorRef, Props} -import net.psforever.objects.zones.Zone -import net.psforever.packet.game.ObjectCreateMessage -import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectCreateMessageParent, PlacementData} -import net.psforever.types.PlanetSideGUID -import net.psforever.services.avatar.support.{CorpseRemovalActor, DroppedItemRemover} -import net.psforever.services.{GenericEventBus, RemoverActor, Service} +import akka.actor.Props +import net.psforever.services.avatar.support.{CorpseRemovalSupport, LitterRemovalSupport} +import net.psforever.services.base.{EventSystemStamp, GenericEventServiceWithCacheAndSupport} -class AvatarService(zone: Zone) extends Actor { - private val undertaker: ActorRef = context.actorOf(Props[CorpseRemovalActor](), s"${zone.id}-corpse-removal-agent") - private val janitor = context.actorOf(Props[DroppedItemRemover](), s"${zone.id}-item-remover-agent") +case object AvatarStamp extends EventSystemStamp - private[this] val log = org.log4s.getLogger - - val AvatarEvents = new GenericEventBus[AvatarServiceResponse] //AvatarEventBus - - def receive: Receive = { - case Service.Join(channel) => - val path = s"/$channel/Avatar" - val who = sender() - AvatarEvents.subscribe(who, path) - - case Service.Leave(None) => - AvatarEvents.unsubscribe(sender()) - - case Service.Leave(Some(channel)) => - val path = s"/$channel/Avatar" - AvatarEvents.unsubscribe(sender(), path) - - case Service.LeaveAll() => - AvatarEvents.unsubscribe(sender()) - - case AvatarServiceMessage(forChannel, action) => - action match { - case AvatarAction.ArmorChanged(player_guid, suit, subtype) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ArmorChanged(suit, subtype)) - ) - case AvatarAction.AvatarImplant(player_guid, action, implantSlot, status) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.AvatarImplant(action, implantSlot, status)) - ) - case AvatarAction.ChangeAmmo( - player_guid, - weapon_guid, - weapon_slot, - old_ammo_guid, - ammo_id, - ammo_guid, - ammo_data - ) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - player_guid, - AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, old_ammo_guid, ammo_id, ammo_guid, ammo_data) - ) - ) - case AvatarAction.ChangeFireMode(player_guid, item_guid, mode) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ChangeFireMode(item_guid, mode)) - ) - case AvatarAction.ChangeFireState_Start(player_guid, weapon_guid) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - player_guid, - AvatarResponse.ChangeFireState_Start(weapon_guid) - ) - ) - case AvatarAction.ChangeFireState_Stop(player_guid, weapon_guid) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ChangeFireState_Stop(weapon_guid)) - ) - case AvatarAction.ConcealPlayer(player_guid) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ConcealPlayer()) - ) - case AvatarAction.EnvironmentalDamage(player_guid, source_guid, amount) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - player_guid, - AvatarResponse.EnvironmentalDamage(player_guid, source_guid, amount) - ) - ) - case AvatarAction.Destroy(victim, killer, weapon, pos) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", victim, AvatarResponse.Destroy(victim, killer, weapon, pos)) - ) - case AvatarAction.DestroyDisplay(killer, victim, method, unk) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - Service.defaultPlayerGUID, - AvatarResponse.DestroyDisplay(killer, victim, method, unk) - ) - ) - case AvatarAction.DropItem(player_guid, item) => - val definition = item.Definition - val objectData = DroppedItemData( - PlacementData(item.Position, item.Orientation), - definition.Packet.ConstructorData(item).get - ) - janitor forward RemoverActor.AddTask(item, zone) - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - player_guid, - AvatarResponse.DropItem(ObjectCreateMessage(definition.ObjectId, item.GUID, objectData)) - ) - ) - case AvatarAction.EquipmentInHand(player_guid, target_guid, slot, item) => - val definition = item.Definition - val containerData = ObjectCreateMessageParent(target_guid, slot) - val objectData = definition.Packet.ConstructorData(item).get - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - player_guid, - AvatarResponse.EquipmentInHand( - ObjectCreateMessage(definition.ObjectId, item.GUID, containerData, objectData) - ) - ) - ) - case AvatarAction.GenericObjectAction(player_guid, object_guid, action_code) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - player_guid, - AvatarResponse.GenericObjectAction(object_guid, action_code) - ) - ) - case AvatarAction.HitHint(source_guid, player_guid) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.HitHint(source_guid)) - ) - case AvatarAction.Killed(player_guid, cause, mount_guid) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Killed(cause, mount_guid)) - ) - case AvatarAction.LoadPlayer(player_guid, object_id, target_guid, cdata, pdata) => - val pkt = pdata match { - case Some(data) => - ObjectCreateMessage(object_id, target_guid, data, cdata) - case None => - ObjectCreateMessage(object_id, target_guid, cdata) - } - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.LoadPlayer(pkt)) - ) - case AvatarAction.LoadProjectile(player_guid, object_id, object_guid, cdata) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - player_guid, - AvatarResponse.LoadProjectile( - ObjectCreateMessage(object_id, object_guid, cdata) - ) - ) - ) - case AvatarAction.ObjectDelete(player_guid, item_guid, unk) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ObjectDelete(item_guid, unk)) - ) - case AvatarAction.ObjectHeld(player_guid, slot, previousSlot) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ObjectHeld(slot, previousSlot)) - ) - case AvatarAction.OxygenState(player, vehicle) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player.guid, AvatarResponse.OxygenState(player, vehicle)) - ) - case AvatarAction.PlanetsideAttribute(guid, attribute_type, attribute_value) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - guid, - AvatarResponse.PlanetsideAttribute(attribute_type, attribute_value) - ) - ) - case AvatarAction.PlanetsideAttributeToAll(guid, attribute_type, attribute_value) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - guid, - AvatarResponse.PlanetsideAttributeToAll(attribute_type, attribute_value) - ) - ) - case AvatarAction.PlanetsideAttributeSelf(guid, attribute_type, attribute_value) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - guid, - AvatarResponse.PlanetsideAttributeSelf(attribute_type, attribute_value) - ) - ) - case AvatarAction.PlanetsideStringAttribute(guid, attribute_type, attribute_value) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - guid, - AvatarResponse.PlanetsideStringAttribute(attribute_type, attribute_value) - ) - ) - case AvatarAction.PlayerState( - guid, - pos, - vel, - yaw, - pitch, - yaw_upper, - seq_time, - is_crouching, - is_jumping, - jump_thrust, - is_cloaking, - spectating, - weaponInHand - ) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - guid, - AvatarResponse.PlayerState( - pos, - vel, - yaw, - pitch, - yaw_upper, - seq_time, - is_crouching, - is_jumping, - jump_thrust, - is_cloaking, - spectating, - weaponInHand - ) - ) - ) - case AvatarAction.ProjectileAutoLockAwareness(mode) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - PlanetSideGUID(0), - AvatarResponse.ProjectileAutoLockAwareness(mode) - ) - ) - case AvatarAction.ProjectileExplodes(player_guid, projectile_guid, projectile) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - player_guid, - AvatarResponse.ProjectileExplodes(projectile_guid, projectile) - ) - ) - case AvatarAction.ProjectileState( - player_guid, - projectile_guid, - shot_pos, - shot_vel, - shot_orient, - sequence, - end, - target - ) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - player_guid, - AvatarResponse.ProjectileState(projectile_guid, shot_pos, shot_vel, shot_orient, sequence, end, target) - ) - ) - case AvatarAction.PickupItem(player_guid, item, unk) => - janitor forward RemoverActor.ClearSpecific(List(item), zone) - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ObjectDelete(item.GUID, unk)) - ) - case AvatarAction.PutDownFDU(player_guid) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", Service.defaultPlayerGUID, AvatarResponse.PutDownFDU(player_guid)) - ) - case AvatarAction.Release(player, _, time) => - undertaker forward RemoverActor.AddTask(player, zone, time) - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player.GUID, AvatarResponse.Release(player)) - ) - case AvatarAction.Reload(player_guid, weapon_guid) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Reload(weapon_guid)) - ) - case AvatarAction.SetEmpire(player_guid, target_guid, faction) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.SetEmpire(target_guid, faction)) - ) - case AvatarAction.StowEquipment(player_guid, target_guid, slot, obj) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - player_guid, - AvatarResponse.StowEquipment(target_guid, slot, obj) - ) - ) - case AvatarAction.WeaponDryFire(player_guid, weapon_guid) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.WeaponDryFire(weapon_guid)) - ) - case AvatarAction.SendResponse(player_guid, msg) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.SendResponse(msg)) - ) - case AvatarAction.SendResponseTargeted(target_guid, msg) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - target_guid, - AvatarResponse.SendResponseTargeted(target_guid, msg) - ) - ) - case AvatarAction.Revive(target_guid) => - AvatarEvents.publish( - AvatarServiceResponse(s"/$forChannel/Avatar", target_guid, AvatarResponse.Revive(target_guid)) - ) - - case AvatarAction.TeardownConnection() => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - Service.defaultPlayerGUID, - AvatarResponse.TeardownConnection() - ) - ) - - case AvatarAction.TerminalOrderResult(terminal, term_action, result) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - Service.defaultPlayerGUID, - AvatarResponse.TerminalOrderResult(terminal, term_action, result) - ) - ) - case AvatarAction.ChangeExosuit( - target, - armor, - exosuit, - subtype, - slot, - maxhand, - old_holsters, - holsters, - old_inventory, - inventory, - drop, - delete - ) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - Service.defaultPlayerGUID, - AvatarResponse.ChangeExosuit( - target, - armor, - exosuit, - subtype, - slot, - maxhand, - old_holsters, - holsters, - old_inventory, - inventory, - drop, - delete - ) - ) - ) - case AvatarAction.ChangeLoadout( - target, - armor, - exosuit, - subtype, - slot, - maxhand, - old_holsters, - holsters, - old_inventory, - inventory, - drop - ) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - Service.defaultPlayerGUID, - AvatarResponse.ChangeLoadout( - target, - armor, - exosuit, - subtype, - slot, - maxhand, - old_holsters, - holsters, - old_inventory, - inventory, - drop - ) - ) - ) - - case AvatarAction.DropSpecialItem() => - AvatarEvents.publish(AvatarServiceResponse(s"/$forChannel/Avatar", Service.defaultPlayerGUID, AvatarResponse.DropSpecialItem())) - - case AvatarAction.UseKit(kit_guid, kit_objid) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - Service.defaultPlayerGUID, - AvatarResponse.UseKit(kit_guid, kit_objid) - ) - ) - - case AvatarAction.KitNotUsed(kit_guid, msg) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - Service.defaultPlayerGUID, - AvatarResponse.KitNotUsed(kit_guid, msg) - ) - ) - - case AvatarAction.UpdateKillsDeathsAssists(charId, stat) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - Service.defaultPlayerGUID, - AvatarResponse.UpdateKillsDeathsAssists(charId, stat) - ) - ) - - case AvatarAction.AwardBep(charId, bep, expType) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - Service.defaultPlayerGUID, - AvatarResponse.AwardBep(charId, bep, expType) - ) - ) - - case AvatarAction.AwardCep(charId, bep) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - Service.defaultPlayerGUID, - AvatarResponse.AwardCep(charId, bep) - ) - ) - - case AvatarAction.FacilityCaptureRewards(building_id, zone_number, exp) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - Service.defaultPlayerGUID, - AvatarResponse.FacilityCaptureRewards(building_id, zone_number, exp) - ) - ) - - case AvatarAction.ShareKillExperienceWithSquad(killer, exp) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - Service.defaultPlayerGUID, - AvatarResponse.ShareKillExperienceWithSquad(killer, exp) - ) - ) - - case AvatarAction.ShareAntExperienceWithSquad(owner, exp, vehicle) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - Service.defaultPlayerGUID, - AvatarResponse.ShareAntExperienceWithSquad(owner, exp, vehicle) - ) - ) - - case AvatarAction.RemoveFromOutfitChat(outfit_id) => - AvatarEvents.publish( - AvatarServiceResponse( - s"/$forChannel/Avatar", - Service.defaultPlayerGUID, - AvatarResponse.RemoveFromOutfitChat(outfit_id) - ) - ) - - case _ => () - } - - //message to Undertaker - case AvatarServiceMessage.Corpse(msg) => - undertaker forward msg - - //message to Janitor - case AvatarServiceMessage.Ground(msg) => - janitor forward msg - - /* - case AvatarService.PlayerStateShift(killer, guid) => - val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(guid) - if (playerOpt.isDefined) { - val player: PlayerAvatar = playerOpt.get - AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, guid, - AvatarServiceReply.PlayerStateShift(killer) - )) - } - */ - - case msg => - log.warn(s"Unhandled message $msg from ${sender()}") +object AvatarService { + def apply(): Props = { + Props( + classOf[GenericEventServiceWithCacheAndSupport], + AvatarStamp, + List(CorpseRemovalSupport, LitterRemovalSupport) + ) } } diff --git a/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala b/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala deleted file mode 100644 index e17cfe0aa..000000000 --- a/src/main/scala/net/psforever/services/avatar/AvatarServiceMessage.scala +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright (c) 2017 PSForever -package net.psforever.services.avatar - -import net.psforever.objects.{Player, Vehicle} -import net.psforever.objects.avatar.scoring.KDAStat -import net.psforever.objects.ballistics.Projectile -import net.psforever.objects.equipment.Equipment -import net.psforever.objects.inventory.InventoryItem -import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget -import net.psforever.objects.sourcing.{SourceEntry, UniquePlayer} -import net.psforever.objects.vital.interaction.DamageResult -import net.psforever.objects.zones.Zone -import net.psforever.packet.PlanetSideGamePacket -import net.psforever.packet.game.ImplantAction -import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectCreateMessageParent} -import net.psforever.types.{ExoSuitType, ExperienceType, PlanetSideEmpire, PlanetSideGUID, TransactionType, Vector3} - -import scala.concurrent.duration.FiniteDuration - -final case class AvatarServiceMessage(forChannel: String, actionMessage: AvatarAction.Action) - -object AvatarServiceMessage { - final case class Corpse(msg: Any) - final case class Ground(msg: Any) -} - -object AvatarAction { - sealed trait Action - - final case class ArmorChanged(player_guid: PlanetSideGUID, suit: ExoSuitType.Value, subtype: Int) extends Action - final case class AvatarImplant(player_guid: PlanetSideGUID, action: ImplantAction.Value, implantSlot: Int, status: Int) extends Action - final case class ChangeAmmo( - player_guid: PlanetSideGUID, - weapon_guid: PlanetSideGUID, - weapon_slot: Int, - old_ammo_guid: PlanetSideGUID, - ammo_id: Int, - ammo_guid: PlanetSideGUID, - ammo_data: ConstructorData - ) extends Action - final case class ChangeFireMode(player_guid: PlanetSideGUID, item_guid: PlanetSideGUID, mode: Int) extends Action - final case class ChangeFireState_Start(player_guid: PlanetSideGUID, weapon_guid: PlanetSideGUID) extends Action - final case class ChangeFireState_Stop(player_guid: PlanetSideGUID, weapon_guid: PlanetSideGUID) extends Action - final case class ConcealPlayer(player_guid: PlanetSideGUID) extends Action - final case class EnvironmentalDamage(player_guid: PlanetSideGUID, source_guid: PlanetSideGUID, amount: Int) - extends Action - final case class DeactivateImplantSlot(player_guid: PlanetSideGUID, slot: Int) extends Action - final case class ActivateImplantSlot(player_guid: PlanetSideGUID, slot: Int) extends Action - final case class Destroy(victim: PlanetSideGUID, killer: PlanetSideGUID, weapon: PlanetSideGUID, pos: Vector3) - extends Action - final case class DestroyDisplay(killer: SourceEntry, victim: SourceEntry, method: Int, unk: Int = 121) extends Action - final case class DropItem(player_guid: PlanetSideGUID, item: Equipment) extends Action - final case class EquipmentInHand(player_guid: PlanetSideGUID, target_guid: PlanetSideGUID, slot: Int, item: Equipment) - extends Action - final case class GenericObjectAction(player_guid: PlanetSideGUID, object_guid: PlanetSideGUID, action_code: Int) - extends Action - final case class HitHint(source_guid: PlanetSideGUID, player_guid: PlanetSideGUID) extends Action - final case class Killed(player_guid: PlanetSideGUID, cause: DamageResult, mount_guid: Option[PlanetSideGUID]) extends Action - final case class LoadPlayer( - player_guid: PlanetSideGUID, - object_id: Int, - target_guid: PlanetSideGUID, - cdata: ConstructorData, - pdata: Option[ObjectCreateMessageParent] - ) extends Action - final case class LoadProjectile( - player_guid: PlanetSideGUID, - object_id: Int, - projectile_guid: PlanetSideGUID, - cdata: ConstructorData - ) extends Action - final case class ObjectDelete(player_guid: PlanetSideGUID, item_guid: PlanetSideGUID, unk: Int = 0) extends Action - final case class ObjectHeld(player_guid: PlanetSideGUID, slot: Int, previousSLot: Int) extends Action - final case class OxygenState(player: OxygenStateTarget, vehicle: Option[OxygenStateTarget]) extends Action - final case class PlanetsideAttribute(player_guid: PlanetSideGUID, attribute_type: Int, attribute_value: Long) - extends Action - final case class PlanetsideAttributeToAll(player_guid: PlanetSideGUID, attribute_type: Int, attribute_value: Long) - extends Action - final case class PlanetsideAttributeSelf(player_guid: PlanetSideGUID, attribute_type: Int, attribute_value: Long) - extends Action - final case class PlanetsideStringAttribute(player_guid: PlanetSideGUID, attribute_type: Int, attribute_value: String) - extends Action - final case class PlayerState( - player_guid: PlanetSideGUID, - pos: Vector3, - vel: Option[Vector3], - facingYaw: Float, - facingPitch: Float, - facingYawUpper: Float, - timestamp: Int, - is_crouching: Boolean, - is_jumping: Boolean, - jump_thrust: Boolean, - is_cloaked: Boolean, - spectator: Boolean, - weaponInHand: Boolean - ) extends Action - final case class PickupItem(player_guid: PlanetSideGUID, item: Equipment, unk: Int = 0) extends Action - final case class ProjectileAutoLockAwareness(mode: Int) extends Action - final case class ProjectileExplodes( - player_guid: PlanetSideGUID, - projectile_guid: PlanetSideGUID, - projectile: Projectile - ) extends Action - final case class ProjectileState( - player_guid: PlanetSideGUID, - projectile_guid: PlanetSideGUID, - shot_pos: Vector3, - shot_vel: Vector3, - shot_orient: Vector3, - sequence: Int, - end: Boolean, - hit_target: PlanetSideGUID - ) extends Action - final case class PutDownFDU(player_guid: PlanetSideGUID) extends Action - final case class Release(player: Player, zone: Zone, time: Option[FiniteDuration] = None) extends Action - final case class Revive(target_guid: PlanetSideGUID) extends Action - final case class Reload(player_guid: PlanetSideGUID, weapon_guid: PlanetSideGUID) extends Action - final case class SetEmpire(player_guid: PlanetSideGUID, object_guid: PlanetSideGUID, faction: PlanetSideEmpire.Value) - extends Action - final case class StowEquipment(player_guid: PlanetSideGUID, target_guid: PlanetSideGUID, slot: Int, item: Equipment) - extends Action - final case class WeaponDryFire(player_guid: PlanetSideGUID, weapon_guid: PlanetSideGUID) extends Action - - final case class SendResponse(player_guid: PlanetSideGUID, msg: PlanetSideGamePacket) extends Action - final case class SendResponseTargeted(target_guid: PlanetSideGUID, msg: PlanetSideGamePacket) extends Action - - final case class TerminalOrderResult(terminal_guid: PlanetSideGUID, action: TransactionType.Value, result: Boolean) - extends Action - final case class ChangeExosuit( - target_guid: PlanetSideGUID, - armor: Int, - exosuit: ExoSuitType.Value, - subtype: Int, - last_drawn_slot: Int, - new_max_hand: Boolean, - old_holsters: List[(Equipment, PlanetSideGUID)], - holsters: List[InventoryItem], - old_inventory: List[(Equipment, PlanetSideGUID)], - inventory: List[InventoryItem], - drop: List[InventoryItem], - delete: List[(Equipment, PlanetSideGUID)] - ) extends Action - final case class ChangeLoadout( - target_guid: PlanetSideGUID, - armor: Int, - exosuit: ExoSuitType.Value, - subtype: Int, - last_drawn_slot: Int, - new_max_hand: Boolean, - old_holsters: List[(Equipment, PlanetSideGUID)], - holsters: List[InventoryItem], - old_inventory: List[(Equipment, PlanetSideGUID)], - inventory: List[InventoryItem], - drop: List[InventoryItem] - ) extends Action - final case class DropSpecialItem() extends Action - final case class UseKit(kit_guid: PlanetSideGUID, kit_objid: Int) extends Action - final case class KitNotUsed(kit_guid: PlanetSideGUID, msg: String) extends Action - - final case class UpdateKillsDeathsAssists(charId: Long, kda: KDAStat) extends Action - final case class AwardBep(charId: Long, bep: Long, expType: ExperienceType) extends Action - final case class AwardCep(charId: Long, bep: Long) extends Action - final case class FacilityCaptureRewards(building_id: Int, zone_number: Int, exp: Long) extends Action - final case class ShareKillExperienceWithSquad(killer: Player, exp: Long) extends Action - final case class ShareAntExperienceWithSquad(owner: UniquePlayer, exp: Long, vehicle: Vehicle) extends Action - final case class RemoveFromOutfitChat(outfit_id: Long) extends Action - - final case class TeardownConnection() extends Action - // final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action - // final case class DestroyDisplay(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action -} diff --git a/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala b/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala deleted file mode 100644 index 87560d293..000000000 --- a/src/main/scala/net/psforever/services/avatar/AvatarServiceResponse.scala +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) 2017 PSForever -package net.psforever.services.avatar - -import net.psforever.objects.{Player, Vehicle} -import net.psforever.objects.avatar.scoring.KDAStat -import net.psforever.objects.ballistics.Projectile -import net.psforever.objects.equipment.Equipment -import net.psforever.objects.inventory.InventoryItem -import net.psforever.objects.serverobject.environment.interaction.common.Watery.OxygenStateTarget -import net.psforever.objects.sourcing.{SourceEntry, UniquePlayer} -import net.psforever.objects.vital.interaction.DamageResult -import net.psforever.packet.PlanetSideGamePacket -import net.psforever.packet.game.objectcreate.ConstructorData -import net.psforever.packet.game.{ImplantAction, ObjectCreateMessage} -import net.psforever.types.{ExoSuitType, ExperienceType, PlanetSideEmpire, PlanetSideGUID, TransactionType, Vector3} -import net.psforever.services.GenericEventBusMsg - -final case class AvatarServiceResponse( - channel: String, - avatar_guid: PlanetSideGUID, - replyMessage: AvatarResponse.Response -) extends GenericEventBusMsg - -object AvatarResponse { - sealed trait Response - - final case class ArmorChanged(suit: ExoSuitType.Value, subtype: Int) extends Response - final case class AvatarImplant(action: ImplantAction.Value, implantSlot: Int, status: Int) extends Response - final case class ChangeAmmo( - weapon_guid: PlanetSideGUID, - weapon_slot: Int, - old_ammo_guid: PlanetSideGUID, - ammo_id: Int, - ammo_guid: PlanetSideGUID, - ammo_data: ConstructorData - ) extends Response - final case class ChangeFireMode(item_guid: PlanetSideGUID, mode: Int) extends Response - final case class ChangeFireState_Start(weapon_guid: PlanetSideGUID) extends Response - final case class ChangeFireState_Stop(weapon_guid: PlanetSideGUID) extends Response - final case class ConcealPlayer() extends Response - final case class EnvironmentalDamage(target: PlanetSideGUID, source_guid: PlanetSideGUID, amount: Int) - extends Response - final case class Destroy(victim: PlanetSideGUID, killer: PlanetSideGUID, weapon: PlanetSideGUID, pos: Vector3) - extends Response - final case class DestroyDisplay(killer: SourceEntry, victim: SourceEntry, method: Int, unk: Int) extends Response - final case class DropItem(pkt: ObjectCreateMessage) extends Response - final case class EquipmentInHand(pkt: ObjectCreateMessage) extends Response - final case class GenericObjectAction(object_guid: PlanetSideGUID, action_code: Int) extends Response - final case class HitHint(source_guid: PlanetSideGUID) extends Response - final case class Killed(cause: DamageResult, mount_guid: Option[PlanetSideGUID]) extends Response - final case class LoadPlayer(pkt: ObjectCreateMessage) extends Response - final case class LoadProjectile(pkt: ObjectCreateMessage) extends Response - final case class ObjectDelete(item_guid: PlanetSideGUID, unk: Int) extends Response - final case class ObjectHeld(slot: Int, previousSLot: Int) extends Response - final case class OxygenState(player: OxygenStateTarget, vehicle: Option[OxygenStateTarget]) extends Response - final case class PlanetsideAttribute(attribute_type: Int, attribute_value: Long) extends Response - final case class PlanetsideAttributeToAll(attribute_type: Int, attribute_value: Long) extends Response - final case class PlanetsideAttributeSelf(attribute_type: Int, attribute_value: Long) extends Response - final case class PlanetsideStringAttribute(attribute_type: Int, attribute_value: String) extends Response - final case class PlayerState( - pos: Vector3, - vel: Option[Vector3], - facingYaw: Float, - facingPitch: Float, - facingYawUpper: Float, - timestamp: Int, - is_crouching: Boolean, - is_jumping: Boolean, - jump_thrust: Boolean, - is_cloaked: Boolean, - spectator: Boolean, - weaponInHand: Boolean - ) extends Response - final case class ProjectileAutoLockAwareness(mode: Int) extends Response - final case class ProjectileExplodes(projectile_guid: PlanetSideGUID, projectile: Projectile) extends Response - final case class ProjectileState( - projectile_guid: PlanetSideGUID, - shot_pos: Vector3, - shot_vel: Vector3, - shot_orient: Vector3, - sequence: Int, - end: Boolean, - hit_target: PlanetSideGUID - ) extends Response - final case class PutDownFDU(target_guid: PlanetSideGUID) extends Response - final case class Release(player: Player) extends Response - final case class Reload(weapon_guid: PlanetSideGUID) extends Response - final case class Revive(target_guid: PlanetSideGUID) extends Response - final case class SetEmpire(object_guid: PlanetSideGUID, faction: PlanetSideEmpire.Value) extends Response - final case class StowEquipment(target_guid: PlanetSideGUID, slot: Int, item: Equipment) extends Response - final case class WeaponDryFire(weapon_guid: PlanetSideGUID) extends Response - - final case class SendResponse(msg: PlanetSideGamePacket) extends Response - final case class SendResponseTargeted(target_guid: PlanetSideGUID, msg: PlanetSideGamePacket) extends Response - - final case class TerminalOrderResult(terminal_guid: PlanetSideGUID, action: TransactionType.Value, result: Boolean) - extends Response - final case class ChangeExosuit( - target_guid: PlanetSideGUID, - armor: Int, - exosuit: ExoSuitType.Value, - subtype: Int, - last_drawn_slot: Int, - new_max_hand: Boolean, - old_holsters: List[(Equipment, PlanetSideGUID)], - holsters: List[InventoryItem], - old_inventory: List[(Equipment, PlanetSideGUID)], - inventory: List[InventoryItem], - drop: List[InventoryItem], - delete: List[(Equipment, PlanetSideGUID)] - ) extends Response - final case class ChangeLoadout( - target_guid: PlanetSideGUID, - armor: Int, - exosuit: ExoSuitType.Value, - subtype: Int, - last_drawn_slot: Int, - new_max_hand: Boolean, - old_holsters: List[(Equipment, PlanetSideGUID)], - holsters: List[InventoryItem], - old_inventory: List[(Equipment, PlanetSideGUID)], - inventory: List[InventoryItem], - drop: List[InventoryItem] - ) extends Response - final case class DropSpecialItem() extends Response - - final case class TeardownConnection() extends Response - // final case class PlayerStateShift(itemID : PlanetSideGUID) extends Response - final case class UseKit(kit_guid: PlanetSideGUID, kit_objid: Int) extends Response - final case class KitNotUsed(kit_guid: PlanetSideGUID, msg: String) extends Response - - final case class UpdateKillsDeathsAssists(charId: Long, kda: KDAStat) extends Response - final case class AwardBep(charId: Long, bep: Long, expType: ExperienceType) extends Response - final case class AwardCep(charId: Long, bep: Long) extends Response - final case class FacilityCaptureRewards(building_id: Int, zone_number: Int, exp: Long) extends Response - final case class ShareKillExperienceWithSquad(killer: Player, exp: Long) extends Response - final case class ShareAntExperienceWithSquad(owner: UniquePlayer, exp: Long, vehicle: Vehicle) extends Response - final case class RemoveFromOutfitChat(outfit_id: Long) extends Response -} 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 974f0b687..1f23e6022 100644 --- a/src/main/scala/net/psforever/services/avatar/support/CorpseRemovalActor.scala +++ b/src/main/scala/net/psforever/services/avatar/support/CorpseRemovalActor.scala @@ -1,14 +1,49 @@ // Copyright (c) 2017 PSForever package net.psforever.services.avatar.support +import akka.actor.{ActorContext, ActorRef, Props} import net.psforever.objects.guid.{GUIDTask, TaskBundle} import net.psforever.objects.Player -import net.psforever.types.ExoSuitType -import net.psforever.services.{RemoverActor, Service} -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.types.{ExoSuitType, PlanetSideGUID} +import net.psforever.services.avatar.AvatarAction.Release +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.{EventServiceSupport, GenericSupportEnvelope, GenericSupportEnvelopeOnly} +import net.psforever.services.base.message.ObjectDelete +import net.psforever.services.base.support.RemoverActor import scala.concurrent.duration._ +case object CorpseRemovalSupport + extends EventServiceSupport { + def label: String = "undertaker" + def constructor(context: ActorContext): ActorRef = { + context.actorOf(Props[CorpseRemovalActor](), name = "CorpseRemoval") + } +} + +final case class ReleaseEnvelope( + channel: 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, actionMessage.player.GUID, actionMessage) +} + +final case class CorpseEnvelope(supportMessage: Any) + extends GenericSupportEnvelopeOnly { + def supportLabel: String = "undertaker" +} + class CorpseRemovalActor extends RemoverActor() { final val FirstStandardDuration: FiniteDuration = 1 minute @@ -23,9 +58,9 @@ class CorpseRemovalActor extends RemoverActor() { def FirstJob(entry: RemoverActor.Entry): Unit = { import net.psforever.objects.zones.Zone entry.zone.Population ! Zone.Corpse.Remove(entry.obj.asInstanceOf[Player]) - context.parent ! AvatarServiceMessage( + context.parent ! MessageEnvelope( entry.zone.id, - AvatarAction.ObjectDelete(Service.defaultPlayerGUID, entry.obj.GUID, unk=1) + ObjectDelete(entry.obj.GUID, unk=1) ) } 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 80a0ce879..32d12bd55 100644 --- a/src/main/scala/net/psforever/services/avatar/support/DroppedItemRemover.scala +++ b/src/main/scala/net/psforever/services/avatar/support/DroppedItemRemover.scala @@ -1,13 +1,71 @@ // Copyright (c) 2017 PSForever package net.psforever.services.avatar.support +import akka.actor.{ActorContext, ActorRef, Props} +import net.psforever.objects.Default import net.psforever.objects.equipment.Equipment import net.psforever.objects.guid.{GUIDTask, TaskBundle} -import net.psforever.services.{RemoverActor, Service} -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.objects.zones.Zone +import net.psforever.services.avatar.AvatarAction.{DropItem, PickupItem} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.{EventServiceSupport, GenericSupportEnvelope, GenericSupportEnvelopeOnly} +import net.psforever.services.base.message.ObjectDelete +import net.psforever.services.base.support.RemoverActor +import net.psforever.types.PlanetSideGUID import scala.concurrent.duration._ +case object LitterRemovalSupport + extends EventServiceSupport { + def label: String = "janitor" + def constructor(context: ActorContext): ActorRef = { + context.actorOf(Props[DroppedItemRemover](), name = "DroppedItemRemover") + } +} + +final case class PickupItemEnvelope( + channel: 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, Default.GUID0, actionMessage, zone) +} + +final case class DropItemEnvelope( + channel: 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, Default.GUID0, actionMessage, zone) +} + +final case class GroundEnvelope(supportMessage: Any) + extends GenericSupportEnvelopeOnly { + def supportLabel: String = "janitor" +} + class DroppedItemRemover extends RemoverActor() { final val FirstStandardDuration: FiniteDuration = 3 minutes @@ -22,9 +80,9 @@ class DroppedItemRemover extends RemoverActor() { def FirstJob(entry: RemoverActor.Entry): Unit = { import net.psforever.objects.zones.Zone entry.zone.Ground ! Zone.Ground.RemoveItem(entry.obj.GUID) - context.parent ! AvatarServiceMessage( + context.parent ! MessageEnvelope( entry.zone.id, - AvatarAction.ObjectDelete(Service.defaultPlayerGUID, entry.obj.GUID) + ObjectDelete(entry.obj.GUID) ) } diff --git a/src/main/scala/net/psforever/services/base/GenericEventService.scala b/src/main/scala/net/psforever/services/base/GenericEventService.scala new file mode 100644 index 000000000..ac098ca68 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/GenericEventService.scala @@ -0,0 +1,104 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base + +import akka.actor.Actor +import net.psforever.services.Service +import net.psforever.services.base.bus.GenericEventBus +import net.psforever.services.base.envelope.{BundledEnvelope, GenericMessageEnvelope} +import org.log4s.Logger + +/** + * A distinct tag associated with an event system. + * The stamp is intended to demonstrate that the input message has been interpreted into an output response + * through actual use of an event system + * and that the response has not been fabricated and is not fraudulent. + * While the word "stamp" more probably calls to mind the concept of a postage stamp, + * the purpose of this artifact is closer to that of the routing information + * stamped onto an envelope over the postage stamp area of a letter. + */ +trait EventSystemStamp { + /* + Example: + The channels are "foo", "foo.fizz", and "foo.buzz" + In general, Classifier channels will perform left-pattern matching + Publishing to channel "foo" will allocate Classifiers "foo", "foo.fizz", and "foo.buzz" + To isolate "foo", one must distinguish it with a right-pattern such as "out" + Publishing to channel "foo.out" no longer publishes to "foo.fizz.out" or to "foo.buzz.out" + */ + /** + * Take an input channel and produce the publishing output channel. + * @param channel publishing channel + * @return appended publishing channel + */ + def routing(channel: String): String = s"/$channel/out" +} + +/** + * Basic opt-in event response relay system. + * Remembers "subscribers" (`ActorRef`) to "channels" (`String`); + * accepts "messages" (`GenericMessageEnvelope`) and interprets the message as a response (`GenericResponseEnvelope`); + * and, dispatches the response to all subscribers associated with the channel provided in the message. + * @param stamp distinct tag associated with an event system + */ +class GenericEventService(stamp: EventSystemStamp) + extends Actor { + protected lazy val log: Logger = org.log4s.getLogger(getClass.getSimpleName) + + protected val eventBus: GenericEventBus = new GenericEventBus + + /** + * Add subscription handling. + */ + private def commonJoinBehavior: Receive = { + case Service.Join(channel, true) => + val path = stamp.routing(channel) + val who = sender() + eventBus.subscribe(who, path) + who ! Service.JoinConfirmation(self, channel) + + case Service.Join(channel, _) => + val path = stamp.routing(channel) + val who = sender() + eventBus.subscribe(who, path) + } + + /** + * Remove subscription handling. + */ + private def commonLeaveBehavior: Receive = { + case Service.LeaveAll => + eventBus.unsubscribe(sender()) + + case Service.Leave(channel) => + val path = stamp.routing(channel) + eventBus.unsubscribe(sender(), path) + } + + /** + * Accept and handle designated messages. + */ + protected def commonBehavior: Receive = { + case bundle: BundledEnvelope => + bundle.msgs.foreach(commonBehavior.apply) + + case msg: GenericMessageEnvelope => + handleMessage(msg) + } + + def receive: Receive = commonJoinBehavior + .orElse(commonLeaveBehavior) + .orElse(commonBehavior) + .orElse { + case msg => + log.warn(s"Unhandled message $msg from ${sender()}") + } + + /** + * Handle designated messages. + * Interpret the input message as an output response and publish that response. + * @param event event system message + */ + protected def handleMessage(event: GenericMessageEnvelope): Unit = { + eventBus.publish(event.response(stamp)) + } +} diff --git a/src/main/scala/net/psforever/services/base/GenericEventServiceWithCacheAndSupport.scala b/src/main/scala/net/psforever/services/base/GenericEventServiceWithCacheAndSupport.scala new file mode 100644 index 000000000..1f284af76 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/GenericEventServiceWithCacheAndSupport.scala @@ -0,0 +1,245 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base + +import akka.actor.Cancellable +import net.psforever.objects.Default +import net.psforever.services.base.envelope.{BundledEnvelope, GenericMessageEnvelope, GenericResponseEnvelope, MessageEnvelope, MessageTransformationBehavior} +import net.psforever.services.base.message.EventMessage +import net.psforever.types.PlanetSideGUID +import net.psforever.util.Config + +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.DurationLong + +/* +Adapted from the rating limiting code in PSForever fork https://github.com/Pinapse/giant with permission + */ + +/** + * A framework for flagged messages to be cached + * based on designation of a target globally unique identifier. + * Suggestion: this identifier be associated with the source. + */ +trait CachedGenericEventEnvelope + extends MessageTransformationBehavior { + /** cache designator */ + def guid: PlanetSideGUID +} + +final case class CachedEnvelope( + guid: PlanetSideGUID, + channel: String, + filter: PlanetSideGUID, + msg: EventMessage + ) extends CachedGenericEventEnvelope { + assert(guid != Default.GUID0, "can not cache message under default GUID") +} + +object CachedEnvelope { + /** + * If the filter is specified, but not the cache target, treat the filter like the cache target. + * Treat invalid values as a reason to downgrade from a cached message envelope to a traditional message envelope. + * Warn of this downgrade.e + * @param channel set of subscribers on an event system bus the envelope should reach + * @param filter specific subscriber endpoint to be excluded (the subscriber should filter themselves) + * @param msg input payload transported by this envelope + * @return either a `CacheEnvelope` or a `MessageEnvelope`, depending on cache-readiness of the `filter` value + */ + def apply(channel: String, filter: PlanetSideGUID, msg: EventMessage): GenericMessageEnvelope = { + if (filter == Default.GUID0) { + org.log4s.getLogger("CachedEnvelope").warn("(1) cached message envelope downgraded to normal message envelope") + MessageEnvelope(channel, filter, msg) + } else { + CachedEnvelope(filter, channel, filter, msg) + } + } + + /** + * If the cache target is specified, but is not valid, + * downgrade from a cached message envelope to a traditional message envelope. + * Warn of this downgrade. + * If valid, the filter becomes its defaulted value in the corresponding message + * @param guid cache designator + * @param channel set of subscribers on an event system bus the envelope should reach + * @param msg input payload transported by this envelope + * @return either a `CacheEnvelope` or a `MessageEnvelope`, depending on cache-readiness of the target + */ + def apply(guid: PlanetSideGUID, channel: String, msg: EventMessage): GenericMessageEnvelope = { + if (guid == Default.GUID0) { + org.log4s.getLogger("CachedEnvelope").warn("(2) cached message envelope downgraded to normal message envelope") + MessageEnvelope(channel, guid, msg) + } else { + CachedEnvelope(guid, channel, Default.GUID0, msg) + } + } +} + +/** + * An internal message for the purpose of forcing cached messages to be flushed. + * All of it's fields default to harmless values because it is not intended to be processed by an event system + * but it must maintain the trappings of a message envelope to be processed. + * @see `NoMessage` + * @see `NoResponseEnvelope` + */ +private case object FlushCachedMessages extends GenericMessageEnvelope { + def originalChannel: String = "" + def msg: EventMessage = NoMessage + def response(stamp: EventSystemStamp): GenericResponseEnvelope = NoResponseEnvelope + def channel: String = "" + def filter: PlanetSideGUID = Default.GUID0 +} + +class GenericEventServiceWithCacheAndSupport +( + stamp: EventSystemStamp, + eventSupportServices: List[EventServiceSupport] +) extends GenericEventServiceWithSupport(stamp, eventSupportServices) { + private val flushCacheDelay: Long = Config.app.network.eventCaching.flushCacheDelay + private val flushCacheMaxDelay: Long = Config.app.network.eventCaching.flushCacheMaxDelay + private var hasCachedMessages: Int = 0 + private var lastCachedMessages: Int = 0 + private val messageThreshold: Long = Config.app.network.eventCaching.messageTrafficThreshold + 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 + + override def postStop(): Unit = { + flushCache() + super.postStop() + } + + private def adjustedFlushDelay(): Long = { + // flushCacheWait <= t <= emergencyFlushMaxDelay + math.min( + flushCacheDelay + math.max(0, lastCachedMessages - messageThreshold), + flushCacheMaxDelay + ) + } + + /** + * If there were previously no messages in the cache, + * prepare to flush the cache after the intended interval passes, + * whether the passing of that interval is detected by a new incoming message and the cache gets flushed naturally, + * or if a safety timer expires and the cache is flushed in precaution. + */ + private def tryRetimeFlushCache(): Unit = { + hasCachedMessages += 1 + if (hasCachedMessages == 1) { + val flushDelay = adjustedFlushDelay() + nextTimeToFlushCache = System.currentTimeMillis() + flushDelay + emergencyFlush = context.system.scheduler.scheduleOnce(delay = (1.25f * flushDelay).toLong milliseconds, self, FlushCachedMessages) + } + } + + /** + * Add messages to the cache. + * @param event event system message + */ + private def pushToCache(event: CachedGenericEventEnvelope): Unit = { + pushToCache(event, event.msg.getClass.getName, event.guid) + } + /** + * Add messages to the cache. + * @param event event system message + * @param eventGuid event system message filter + */ + private def pushToCache(event: GenericMessageEnvelope, eventGuid: PlanetSideGUID): Unit = { + pushToCache(event, event.msg.getClass.getName, eventGuid) + } + /** + * Add messages to the cache + * based on their channel, then their type, then their cache target identifier. + * Messages that arrive with the same cache profile as a previous message, + * but before that previous message was dispatched, + * will overwrite the previous message without fanfare or warning. + * @param event event system message + * @param eventClassName event system message identifier + * @param eventGuid event system message filter + */ + private def pushToCache(event: GenericMessageEnvelope, eventClassName: String, eventGuid: PlanetSideGUID): Unit = { + val updateBranch = cache + .getOrElseUpdate(event.channel, new ConcurrentHashMap[String, CMap[PlanetSideGUID, GenericMessageEnvelope]]().asScala) + .getOrElseUpdate(eventClassName, new ConcurrentHashMap[PlanetSideGUID, GenericMessageEnvelope]().asScala) + updateBranch.updateWith(eventGuid) { _ => Some(event) } + tryRetimeFlushCache() + } + + /** + * If the cache has messages and the current time exceeds the anticipated flush time, + * flush the cache messages to the normal event system bus. + */ + private def tryFlushCache(): Boolean = { + val willFlush = hasCachedMessages > 0 && nextTimeToFlushCache < System.currentTimeMillis() + if (willFlush) { + flushCache() + } + willFlush + } + + /** + * Flush the cache messages to the normal event system bus. + * Clear old messages then reset all flags that would force the messages to be flushed. + */ + private def flushCache(): Unit = { + cache.foreachEntry { (_, map) => + map.foreachEntry { (_, map) => + map.foreachEntry { (_, event) => + super.handleMessage(event) + } + map.clear() + } + } + lastCachedMessages = hasCachedMessages + hasCachedMessages = 0 + emergencyFlush.cancel() + emergencyFlush = Default.Cancellable + } + + /** + * If the cache will be flushed after this message, flush the cache and then pass the message for processing. + * If the cache will not be flushed, add the message to the cache. + * In any case, procedure should always be ready to flush the cache when a message is received. + * If there is a support actor involved with a cached message, it is resolved when the message is flushed. + * @param event event system message that may be cached + */ + override protected def handleMessage(event: GenericMessageEnvelope): Unit = { + event match { + case FlushCachedMessages => + flushCache() + case bundle: BundledEnvelope => + handleMessageBundled(bundle) + case _: CachedGenericEventEnvelope if tryFlushCache() => + super.handleMessage(event) + case envelope: CachedGenericEventEnvelope => + pushToCache(envelope) + case _ => + tryFlushCache() + super.handleMessage(event) + } + } + + /** + * If even one message in a bundle is to be cached, the contents of the whole bundle should be cached. + * Otherwise, just handle things normally. + * @param bundle event system message that may be cached + */ + private def handleMessageBundled(bundle: BundledEnvelope): Unit = { + val messages = bundle.msgs + messages.find(_.isInstanceOf[CachedGenericEventEnvelope]) match { + case Some(cache: CachedGenericEventEnvelope) if messages.size == 1 => + pushToCache(messages.head, cache.guid) + tryFlushCache() + case Some(cache: CachedGenericEventEnvelope) => + val guid = cache.guid + messages.foreach(msg => pushToCache(msg, guid)) + tryFlushCache() + case _ => + messages.foreach(handleMessage) + } + } +} diff --git a/src/main/scala/net/psforever/services/base/GenericEventServiceWithSupport.scala b/src/main/scala/net/psforever/services/base/GenericEventServiceWithSupport.scala new file mode 100644 index 000000000..e10b152f1 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/GenericEventServiceWithSupport.scala @@ -0,0 +1,128 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base + +import akka.actor.{ActorContext, ActorRef} +import net.psforever.objects.Default +import net.psforever.services.base.envelope.{GenericMessageEnvelope, GenericResponseEnvelope, MessageTransformationBehavior, NoReply, Undelivered} +import net.psforever.services.base.message.{EventMessage, EventResponse, SelfRespondingEvent} +import net.psforever.types.PlanetSideGUID + +import scala.annotation.unused + +/** + * A message when there should be none, but a message is required to be defined anyway. + * @see `GenericSupportEnvelopeOnly` + */ +case object NoMessage extends SelfRespondingEvent + +/** + * A response when there should be none, but a response is required to be defined anyway. + * @see `GenericSupportEnvelopeOnly` + * @see `NoReply` + * @see `Undelivered` + */ +case object NoResponseEnvelope extends GenericResponseEnvelope { + def reply: EventResponse = NoReply + def stamp: EventSystemStamp = Undelivered + def channel: String = "" + def filter: PlanetSideGUID = Default.GUID0 +} + +/** + * A framework for how support actors are to be submitted to an event system. + * The bare bones are a label by which the support actor is identified for message routing, + * and a function that constructs the support actor within the context of the event system. + * @see `ActorContext` + */ +trait EventServiceSupport { + def label: String + def constructor(@unused context: ActorContext): ActorRef +} + +/** + * A framework for communicating messages to support actors within an event system. + * The bare bones are a label by which the support actor is identified for message routing, + * and the message payload for the support actor to process. + * @see `ActorContext` + */ +trait GenericMessageToSupport { + def supportLabel: String + def supportMessage: Any +} + +/** + * An envelope framework for communicating messages to support actors within an event system + * and also interacting with the event system directly. + */ +trait GenericSupportEnvelope + extends GenericMessageToSupport + with MessageTransformationBehavior + +/** + * An envelope framework for communicating messages to support actors within an event system only. + */ +trait GenericSupportEnvelopeOnly + extends GenericMessageToSupport + with GenericMessageEnvelope { + def originalChannel: String = "" + def channel: String = "" + def filter: PlanetSideGUID = Default.GUID0 + def msg: EventMessage = NoMessage + + def response(@unused stamp: EventSystemStamp): GenericResponseEnvelope = NoResponseEnvelope +} + +/** + * Advanced opt-in event response relay system. + * Includes a system of specialized child actors that serve specific repeatable operations + * whose behaviors are intentionally synchronized on this event pipeline + * or whose collective operational overhead can be streamlined by reliance on a singular pipeline. + * @param stamp distinct tag associated with an event system + * @param eventSupportServices list of support actors to initialize + */ +class GenericEventServiceWithSupport +( + stamp: EventSystemStamp, + eventSupportServices: List[EventServiceSupport] +) extends GenericEventService(stamp) { + + private val supportServices: Map[String, ActorRef] = + eventSupportServices + .map { supportService => (supportService.label, supportService.constructor(context)) } + .toMap[String, ActorRef] + + /** + * Use the label assigned to a support actor + * to locate that support actor on this event system + * and forward to it a payload message. + * @param msg event system message carrying an additional message for a support actor + */ + private def forwardToSupport(msg: GenericMessageToSupport): Unit = { + supportServices + .get(msg.supportLabel) + .map { support => + support.forward(msg.supportMessage) + msg + } + .getOrElse { + log.error(s"support service ${msg.supportLabel} was not found - check message routing or service params") + } + } + + /** + * If the message involves the support actor subsystem, forward the message to it for additional processing. + * Afterwards, or if not, merely call up to previously-established message handling if warranted. + * @param event event system message that may be carrying an additional message for a support actor + */ + override protected def handleMessage(event: GenericMessageEnvelope): Unit = { + event match { + case msg: GenericSupportEnvelopeOnly => + forwardToSupport(msg) + case msg: GenericSupportEnvelope => + forwardToSupport(msg) + super.handleMessage(event) + case event => + super.handleMessage(event) + } + } +} diff --git a/src/main/scala/net/psforever/services/base/bus/GenericEventBus.scala b/src/main/scala/net/psforever/services/base/bus/GenericEventBus.scala new file mode 100644 index 000000000..57454399d --- /dev/null +++ b/src/main/scala/net/psforever/services/base/bus/GenericEventBus.scala @@ -0,0 +1,45 @@ +// Copyright (c) 2017 PSForever +package net.psforever.services.base.bus + +import akka.event.{ActorEventBus, SubchannelClassification} +import akka.util.Subclassification +import net.psforever.services.base.envelope.GenericResponseEnvelope + +/** + * Maintain a list of endpoints that have subscribed to a specific perspective + * internally called a `Classifier` and externally referred to as a "channel". + * When an `Event` - externally referred colloquially as a "message" - is received, + * to be published, + * match the "channel" to a list of known `Classifier` endpoints. + * Each of these `Subscribers` should receive the message. + * @see `GenericResponseEnvelope` + * @see `ActorEventBus.Subscriber` + */ +class GenericEventBus + extends ActorEventBus with SubchannelClassification { + type Event = GenericResponseEnvelope + type Classifier = String + + protected def classify(event: Event): Classifier = event.outChannel + + /* + Example: + The channels are "foo", "foo.fizz", and "foo.buzz" + In general, Classifier channels will perform left-pattern matching + Publishing to channel "foo" will allocate Classifiers "foo", "foo.fizz", and "foo.buzz" + See `GenericResponseEnvelope.outChannel` to determine how this is applied + */ + protected def subclassification: Subclassification[String] = new Subclassification[Classifier] { + def isEqual(x: Classifier, y: Classifier): Boolean = x.equals(y) + + def isSubclass(x: Classifier, y: Classifier): Boolean = x.startsWith(y) + } + + override def publish(event: Event): Unit = { + super[SubchannelClassification].publish(event) + } + + protected def publish(event: Event, subscriber: Subscriber): Unit = { + subscriber ! event + } +} diff --git a/src/main/scala/net/psforever/services/base/envelope/AllEnvelopes.scala b/src/main/scala/net/psforever/services/base/envelope/AllEnvelopes.scala new file mode 100644 index 000000000..d60860743 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/envelope/AllEnvelopes.scala @@ -0,0 +1,17 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.envelope + +import net.psforever.types.PlanetSideGUID + +/** + * Base of all envelope classes. + * Defines a channel and a filter, both of which server the purpose of routing the message to its destination. + */ +trait AllEnvelopes { + /** set of subscribers on an event system bus that the envelope should reach */ + def channel: String + /** specific subscriber endpoint to be excluded (the subscriber should filter themselves upon receipt) */ + def filter: PlanetSideGUID + + val time: Long = System.currentTimeMillis() +} diff --git a/src/main/scala/net/psforever/services/base/envelope/BundledEnvelope.scala b/src/main/scala/net/psforever/services/base/envelope/BundledEnvelope.scala new file mode 100644 index 000000000..9c831b014 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/envelope/BundledEnvelope.scala @@ -0,0 +1,82 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.envelope + +import net.psforever.objects.Default +import net.psforever.services.base.EventSystemStamp +import net.psforever.services.base.message.{EventMessage, EventResponse} +import net.psforever.types.PlanetSideGUID + +import scala.annotation.tailrec + +/** + * A message when there should be none, but a message is required to be defined anyway. + */ +case object NoMessage extends EventMessage { + override def response(): EventResponse = NoReply +} + +/** + * A response envelope when there should be none, but an envelope for messaging product is required to be defined anyway. + */ +case object NoResponse extends GenericResponseEnvelope { + override def reply: EventResponse = NoReply + override def stamp: EventSystemStamp = Undelivered + override def channel: String = "" + override def filter: PlanetSideGUID = Default.GUID0 +} + +/** + * A collection of messaging envelopes to be dispatched to an event system at the same time + * within the conditions of event system synchronization between different messages. + * All messages contained within the bundling are to be processed at the time of processing by the bundling. + * The order of the bundled message envelopes should be considered important. + * @see `SendResponse(Seq[PlanetSideGamePacket])` + * @param msgs list of message envelopes + */ +final case class BundledEnvelope(msgs: Iterable[GenericMessageEnvelope]) + extends GenericMessageEnvelope { + assert(msgs.size == BundledEnvelope.unwind(msgs).size, "do not nest bundled event system envelopes") + + override def msg: EventMessage = NoMessage + override def response(stamp: EventSystemStamp): GenericResponseEnvelope = NoResponse + override def channel: String = "" + override def filter: PlanetSideGUID = Default.GUID0 +} + +object BundledEnvelope { + /** + * Overloaded constructor for `BundledEnvelope` objects. + * The entities are separated between "the first" and "any others" to distinguish from + * the `case class` constructor that accepts any number of message envelopes + * including no message envelopes at all. + * @param first single, required `GenericMessageEnvelope` entity for bundling + * @param msgs any other `GenericMessageEnvelope` entity for bundling + * @return a `BundledEnvelope` object + */ + def apply(first: GenericMessageEnvelope, msgs: GenericMessageEnvelope*): BundledEnvelope = { + new BundledEnvelope(unwind(first +: msgs)) + } + + /** + * Input sanitization method that unpacks `BundledEnvelope` message envelopes + * producing a single-dimensional list of `GenericMessageEnvelope` entities. + * An assertion in the constructor of `BundledEnvelope` aborts object creation + * if a `BundledEnvelope` entity is within that `BundledEnvelope` entity. + * @param in list of `GenericMessageEnvelope` entities to be processed + * @param out list of `GenericMessageEnvelope` entities that have been processed + * @return list of `GenericMessageEnvelope` entities that have been processed + */ + @tailrec + private def unwind(in: Iterable[GenericMessageEnvelope], out: List[GenericMessageEnvelope] = Nil): List[GenericMessageEnvelope] = { + if (in.isEmpty) { + out + } else { + in.head match { + case bundle: BundledEnvelope => + unwind(bundle.msgs ++ in.tail, out) + case first => + unwind(in.tail, out :+ first) + } + } + } +} diff --git a/src/main/scala/net/psforever/services/base/envelope/GenericMessageEnvelope.scala b/src/main/scala/net/psforever/services/base/envelope/GenericMessageEnvelope.scala new file mode 100644 index 000000000..ae4c411e4 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/envelope/GenericMessageEnvelope.scala @@ -0,0 +1,28 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.envelope + +import net.psforever.services.base.EventSystemStamp +import net.psforever.services.base.message.EventMessage +import net.psforever.types.PlanetSideGUID + +trait GenericMessageEnvelope + extends AllEnvelopes { + /** input payload transported by this envelope */ + def msg: EventMessage + /** method that counts as "processing" the envelope by an event system; + * the event system supplies their stamp and converts the message envelope into a response envelope; + * the input message is converted into a response message */ + def response(stamp: EventSystemStamp): GenericResponseEnvelope +} + +object GenericMessageEnvelope { + /** + * The extracted data from a message envelope resembles the data from, + * including the filter and the original channel information. + * @param obj response envelope + * @return a tuple containing the channel, filter, and reply message + */ + def unapply(obj: GenericMessageEnvelope): Option[(String, PlanetSideGUID, EventMessage)] = { + Some((obj.channel, obj.filter, obj.msg)) + } +} diff --git a/src/main/scala/net/psforever/services/base/envelope/GenericResponseEnvelope.scala b/src/main/scala/net/psforever/services/base/envelope/GenericResponseEnvelope.scala new file mode 100644 index 000000000..5f5a39420 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/envelope/GenericResponseEnvelope.scala @@ -0,0 +1,72 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.envelope + +import net.psforever.services.base.EventSystemStamp +import net.psforever.services.base.message.{EventMessage, EventResponse, SelfRespondingEvent} +import net.psforever.types.PlanetSideGUID + +/** + * The framework of an event system envelope that is treated as the processed complement of a message envelope. + */ +trait GenericResponseEnvelope + extends AllEnvelopes { + /** result of converting the message envelope input payload into output payload */ + def reply: EventResponse + /** marker indicating the routing through which the original message was processed */ + def stamp: EventSystemStamp + /** channel information tailored to the event system */ + final def outChannel: String = stamp.routing(channel) +} + +object GenericResponseEnvelope { + /** + * Fake a response envelope, as if processed by a specific event system. + * @param stamp marker indicating the routing through which the original message was processed + * @param channel set of subscribers on an event system bus the envelope should reach + * @param filter a specific subscriber endpoint to be excluded + * @param msg output payload transported by this envelope + * @return a faked but typically acceptable response envelope + */ + def apply(stamp: EventSystemStamp, channel: String, filter: PlanetSideGUID, msg: EventMessage): GenericResponseEnvelope = { + val envelope = MessageEnvelope(channel, filter, msg) + envelope.response(stamp) + } + + /** + * Fake a response envelope, as if processed by a specific event system. + * @param _stamp marker indicating the routing through which the original message was processed + * @param _channel set of subscribers on an event system bus the envelope should reach + * @param _filter a specific subscriber endpoint to be excluded + * @param _reply input payload transported by this envelope + * @return a faked but typically acceptable response envelope + */ + def apply(_stamp: EventSystemStamp, _channel: String, _filter: PlanetSideGUID, _reply: SelfRespondingEvent): GenericResponseEnvelope = { + apply(_stamp, _channel, _filter, _reply.asInstanceOf[EventResponse]) + } + + /** + * Fake a response envelope, as if processed by a specific event system. + * @param _stamp marker indicating the routing through which the original message was processed + * @param _channel set of subscribers on an event system bus the envelope should reach + * @param _filter a specific subscriber endpoint to be excluded + * @param _reply input payload transported by this envelope + * @return a faked but typically acceptable response envelope + */ + def apply(_stamp: EventSystemStamp, _channel: String, _filter: PlanetSideGUID, _reply: EventResponse): GenericResponseEnvelope = { + new GenericResponseEnvelope { + def channel: String = _channel + def filter: PlanetSideGUID = _filter + def reply: EventResponse = _reply + def stamp: EventSystemStamp = _stamp + } + } + + /** + * The `unapply`ed data from a response envelope resembles the data from includes the filter and the channel information. + * @param obj response envelope + * @return a tuple containing the channel, filter, and reply message + */ + def unapply(obj: GenericResponseEnvelope): Option[(String, PlanetSideGUID, EventResponse)] = { + Some((obj.channel, obj.filter, obj.reply)) + } +} diff --git a/src/main/scala/net/psforever/services/base/envelope/MessageEnvelope.scala b/src/main/scala/net/psforever/services/base/envelope/MessageEnvelope.scala new file mode 100644 index 000000000..58373b521 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/envelope/MessageEnvelope.scala @@ -0,0 +1,65 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.envelope + +import net.psforever.objects.Default +import net.psforever.services.base.EventSystemStamp +import net.psforever.services.base.message.{EventMessage, EventResponse} +import net.psforever.types.PlanetSideGUID + +/** + * A response when there should be none, but a reply is required to be defined anyway. + */ +case object NoReply extends EventResponse + +/** + * A stamp that represents not having been processed by an event system. + * Should never been given out to an event system. + */ +case object Undelivered extends EventSystemStamp { + override def routing(channel: String): String = "" +} + +/** + * The mechanics of a proper event system envelope. + * The envelope has two forms: the input message envelope and the output response envelope. + * The event system uses its stamp to mark that the message form converts into the response form when being processed. + * In terms of proper metaphor, + * it makes no sense for a post office take a letter out of its original envelope, + * write a completely different envelope, + * write a completely different letter, + * package the new letter in the new envelope, + * and deliver it is place of the originals. + * That would be considered fraud. + */ +trait MessageTransformationBehavior + extends GenericMessageEnvelope + with GenericResponseEnvelope { + private var outputStamp: EventSystemStamp = Undelivered + private var outputReply: EventResponse = NoReply + + // satisfies GenericMessageEnvelope + def response(stamp: EventSystemStamp): GenericResponseEnvelope = { + outputStamp = stamp + outputReply = msg.response() + this + } + + // satisfies GenericResponseEnvelope + def stamp: EventSystemStamp = outputStamp + + def reply: EventResponse = outputReply +} + +object MessageEnvelope { + def apply(msg: EventMessage): MessageEnvelope = + MessageEnvelope("", Default.GUID0, msg) + + def apply(channel: String, msg: EventMessage): MessageEnvelope = + MessageEnvelope(channel, Default.GUID0, msg) +} + +/** + * A proper event system envelope. + */ +case class MessageEnvelope(channel: String, filter: PlanetSideGUID, msg: EventMessage) + extends MessageTransformationBehavior diff --git a/src/main/scala/net/psforever/services/base/message/ChangeAmmo.scala b/src/main/scala/net/psforever/services/base/message/ChangeAmmo.scala new file mode 100644 index 000000000..101a37cc7 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/message/ChangeAmmo.scala @@ -0,0 +1,14 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.message + +import net.psforever.packet.game.objectcreate.ConstructorData +import net.psforever.types.PlanetSideGUID + +final case class ChangeAmmo( + weapon_guid: PlanetSideGUID, + weapon_slot: Int, + old_ammo_guid: PlanetSideGUID, + ammo_id: Int, + ammo_guid: PlanetSideGUID, + ammo_data: ConstructorData + ) extends SelfRespondingEvent diff --git a/src/main/scala/net/psforever/services/base/message/ChangeFireState_Start.scala b/src/main/scala/net/psforever/services/base/message/ChangeFireState_Start.scala new file mode 100644 index 000000000..59d1bb142 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/message/ChangeFireState_Start.scala @@ -0,0 +1,6 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.message + +import net.psforever.types.PlanetSideGUID + +final case class ChangeFireState_Start(weapon_guid: PlanetSideGUID) extends SelfRespondingEvent diff --git a/src/main/scala/net/psforever/services/base/message/ChangeFireState_Stop.scala b/src/main/scala/net/psforever/services/base/message/ChangeFireState_Stop.scala new file mode 100644 index 000000000..5aeb30c11 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/message/ChangeFireState_Stop.scala @@ -0,0 +1,6 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.message + +import net.psforever.types.PlanetSideGUID + +final case class ChangeFireState_Stop(weapon_guid: PlanetSideGUID) extends SelfRespondingEvent diff --git a/src/main/scala/net/psforever/services/base/message/ConcealPlayer.scala b/src/main/scala/net/psforever/services/base/message/ConcealPlayer.scala new file mode 100644 index 000000000..1a642c72c --- /dev/null +++ b/src/main/scala/net/psforever/services/base/message/ConcealPlayer.scala @@ -0,0 +1,6 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.message + +import net.psforever.types.PlanetSideGUID + +final case class ConcealPlayer(player_guid: PlanetSideGUID) extends SelfRespondingEvent diff --git a/src/main/scala/net/psforever/services/base/message/EventExchange.scala b/src/main/scala/net/psforever/services/base/message/EventExchange.scala new file mode 100644 index 000000000..3331927cb --- /dev/null +++ b/src/main/scala/net/psforever/services/base/message/EventExchange.scala @@ -0,0 +1,14 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.message + +trait EventResponse + +trait EventMessage { + def response(): EventResponse +} + +trait SelfRespondingEvent + extends EventMessage + with EventResponse { + def response(): EventResponse = this +} diff --git a/src/main/scala/net/psforever/services/base/message/GenericObjectAction.scala b/src/main/scala/net/psforever/services/base/message/GenericObjectAction.scala new file mode 100644 index 000000000..a62a467f6 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/message/GenericObjectAction.scala @@ -0,0 +1,6 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.message + +import net.psforever.types.PlanetSideGUID + +final case class GenericObjectAction(object_guid: PlanetSideGUID, action_code: Int) extends SelfRespondingEvent diff --git a/src/main/scala/net/psforever/services/base/message/HintsAtAttacker.scala b/src/main/scala/net/psforever/services/base/message/HintsAtAttacker.scala new file mode 100644 index 000000000..dbb835448 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/message/HintsAtAttacker.scala @@ -0,0 +1,7 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.message + +import net.psforever.types.PlanetSideGUID + +//analogue for HitHint +final case class HintsAtAttacker(source_guid: PlanetSideGUID) extends SelfRespondingEvent diff --git a/src/main/scala/net/psforever/services/base/message/ObjectDelete.scala b/src/main/scala/net/psforever/services/base/message/ObjectDelete.scala new file mode 100644 index 000000000..43fb1949e --- /dev/null +++ b/src/main/scala/net/psforever/services/base/message/ObjectDelete.scala @@ -0,0 +1,6 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.message + +import net.psforever.types.PlanetSideGUID + +final case class ObjectDelete(obj_guid: PlanetSideGUID, unk: Int = 0) extends SelfRespondingEvent diff --git a/src/main/scala/net/psforever/services/base/message/PlanetsideAttribute.scala b/src/main/scala/net/psforever/services/base/message/PlanetsideAttribute.scala new file mode 100644 index 000000000..6bf3af0b4 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/message/PlanetsideAttribute.scala @@ -0,0 +1,10 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.message + +import net.psforever.types.PlanetSideGUID + +final case class PlanetsideAttribute( + target_guid: PlanetSideGUID, + attribute_type: Int, + attribute_value: Long + ) extends SelfRespondingEvent diff --git a/src/main/scala/net/psforever/services/base/message/ReloadTool.scala b/src/main/scala/net/psforever/services/base/message/ReloadTool.scala new file mode 100644 index 000000000..e4b8aee48 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/message/ReloadTool.scala @@ -0,0 +1,6 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.message + +import net.psforever.types.PlanetSideGUID + +final case class ReloadTool(weapon_guid: PlanetSideGUID) extends SelfRespondingEvent diff --git a/src/main/scala/net/psforever/services/base/message/SendResponse.scala b/src/main/scala/net/psforever/services/base/message/SendResponse.scala new file mode 100644 index 000000000..cb3f452a5 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/message/SendResponse.scala @@ -0,0 +1,12 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.message + +import net.psforever.packet.PlanetSideGamePacket + +final case class SendResponse(pkts: Seq[PlanetSideGamePacket]) extends SelfRespondingEvent + +object SendResponse { + def apply(pkt: PlanetSideGamePacket): SendResponse = SendResponse(Seq(pkt)) + + def apply(first: PlanetSideGamePacket, msgs: PlanetSideGamePacket*): SendResponse = SendResponse(first +: msgs) +} diff --git a/src/main/scala/net/psforever/services/base/message/SetEmpire.scala b/src/main/scala/net/psforever/services/base/message/SetEmpire.scala new file mode 100644 index 000000000..7e2315372 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/message/SetEmpire.scala @@ -0,0 +1,6 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.message + +import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} + +final case class SetEmpire(object_guid: PlanetSideGUID, faction: PlanetSideEmpire.Value) extends SelfRespondingEvent diff --git a/src/main/scala/net/psforever/services/base/message/WeaponDryFire.scala b/src/main/scala/net/psforever/services/base/message/WeaponDryFire.scala new file mode 100644 index 000000000..a011bb209 --- /dev/null +++ b/src/main/scala/net/psforever/services/base/message/WeaponDryFire.scala @@ -0,0 +1,6 @@ +// Copyright (c) 2026 PSForever +package net.psforever.services.base.message + +import net.psforever.types.PlanetSideGUID + +final case class WeaponDryFire(weapon_guid: PlanetSideGUID) extends SelfRespondingEvent diff --git a/src/main/scala/net/psforever/services/RemoverActor.scala b/src/main/scala/net/psforever/services/base/support/RemoverActor.scala similarity index 99% rename from src/main/scala/net/psforever/services/RemoverActor.scala rename to src/main/scala/net/psforever/services/base/support/RemoverActor.scala index d6118dccc..2de317768 100644 --- a/src/main/scala/net/psforever/services/RemoverActor.scala +++ b/src/main/scala/net/psforever/services/base/support/RemoverActor.scala @@ -1,12 +1,11 @@ // Copyright (c) 2017 PSForever -package net.psforever.services +package net.psforever.services.base.support import akka.actor.Cancellable import net.psforever.objects.guid.{StraightforwardTask, TaskBundle, TaskWorkflow} import net.psforever.objects.zones.Zone import net.psforever.objects.{Default, PlanetSideGameObject} import net.psforever.types.Vector3 -import net.psforever.services.support.{SimilarityComparator, SupportActor, SupportActorCaseConversions} import scala.concurrent.Future import scala.concurrent.duration._ diff --git a/src/main/scala/net/psforever/services/support/SimilarityComparator.scala b/src/main/scala/net/psforever/services/base/support/SimilarityComparator.scala similarity index 87% rename from src/main/scala/net/psforever/services/support/SimilarityComparator.scala rename to src/main/scala/net/psforever/services/base/support/SimilarityComparator.scala index d9ea16a1b..f840500cb 100644 --- a/src/main/scala/net/psforever/services/support/SimilarityComparator.scala +++ b/src/main/scala/net/psforever/services/base/support/SimilarityComparator.scala @@ -1,5 +1,5 @@ // Copyright (c) 2017 PSForever -package net.psforever.services.support +package net.psforever.services.base.support abstract class SimilarityComparator[A <: SupportActor.Entry] { diff --git a/src/main/scala/net/psforever/services/support/SupportActor.scala b/src/main/scala/net/psforever/services/base/support/SupportActor.scala similarity index 99% rename from src/main/scala/net/psforever/services/support/SupportActor.scala rename to src/main/scala/net/psforever/services/base/support/SupportActor.scala index bb52e1f0b..07503ad17 100644 --- a/src/main/scala/net/psforever/services/support/SupportActor.scala +++ b/src/main/scala/net/psforever/services/base/support/SupportActor.scala @@ -1,5 +1,5 @@ // Copyright (c) 2017 PSForever -package net.psforever.services.support +package net.psforever.services.base.support import akka.actor.Actor import net.psforever.objects.PlanetSideGameObject diff --git a/src/main/scala/net/psforever/services/support/SupportActorCaseConversions.scala b/src/main/scala/net/psforever/services/base/support/SupportActorCaseConversions.scala similarity index 96% rename from src/main/scala/net/psforever/services/support/SupportActorCaseConversions.scala rename to src/main/scala/net/psforever/services/base/support/SupportActorCaseConversions.scala index 4a1fedd77..3640cceff 100644 --- a/src/main/scala/net/psforever/services/support/SupportActorCaseConversions.scala +++ b/src/main/scala/net/psforever/services/base/support/SupportActorCaseConversions.scala @@ -1,5 +1,5 @@ // Copyright (c) 2017 PSForever -package net.psforever.services.support +package net.psforever.services.base.support import net.psforever.objects.PlanetSideGameObject import net.psforever.objects.zones.Zone diff --git a/src/main/scala/net/psforever/services/chat/ChatService.scala b/src/main/scala/net/psforever/services/chat/ChatService.scala index 68fc0edbb..5d72ebf71 100644 --- a/src/main/scala/net/psforever/services/chat/ChatService.scala +++ b/src/main/scala/net/psforever/services/chat/ChatService.scala @@ -6,6 +6,7 @@ import akka.actor.typed.{ActorRef, Behavior} import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors} import net.psforever.objects.{Session, SessionSource} import net.psforever.packet.game.ChatMsg +import net.psforever.services.base.message.{EventMessage, EventResponse} import net.psforever.types.{ChatMessageType, PlanetSideEmpire} object ChatService { @@ -17,14 +18,16 @@ object ChatService { new ChatService(context) } - sealed trait Command + sealed trait Command extends EventMessage { + def response(): EventResponse = null + } final case class JoinChannel(actor: ActorRef[MessageResponse], sessionSource: SessionSource, channel: ChatChannel) extends Command final case class LeaveChannel(actor: ActorRef[MessageResponse], channel: ChatChannel) extends Command final case class LeaveAllChannels(actor: ActorRef[MessageResponse]) extends Command final case class Message(session: Session, message: ChatMsg, channel: ChatChannel) extends Command - final case class MessageResponse(session: Session, message: ChatMsg, channel: ChatChannel) + final case class MessageResponse(session: Session, message: ChatMsg, channel: ChatChannel) extends EventResponse } class ChatService(context: ActorContext[ChatService.Command]) extends AbstractBehavior[ChatService.Command](context) { diff --git a/src/main/scala/net/psforever/services/galaxy/GalaxyAction.scala b/src/main/scala/net/psforever/services/galaxy/GalaxyAction.scala new file mode 100644 index 000000000..96015b6ad --- /dev/null +++ b/src/main/scala/net/psforever/services/galaxy/GalaxyAction.scala @@ -0,0 +1,38 @@ +// Copyright (c) 2017-2026 PSForever +package net.psforever.services.galaxy + +import net.psforever.objects.Vehicle +import net.psforever.objects.vehicles.VehicleManifest +import net.psforever.objects.zones.{HotSpotInfo, Zone} +import net.psforever.packet.game.{BuildingInfoUpdateMessage, CaptureFlagUpdateMessage} +import net.psforever.services.base.message.SelfRespondingEvent +import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} + +object GalaxyAction { + final case class HotSpotUpdate(zone_id: Int, priority: Int, host_spot_info: List[HotSpotInfo]) extends SelfRespondingEvent + + final case class MapUpdate(msg: BuildingInfoUpdateMessage) extends SelfRespondingEvent + + final case class FlagMapUpdate(msg: CaptureFlagUpdateMessage) extends SelfRespondingEvent + + final case class TransferPassenger( + player_guid: PlanetSideGUID, + temp_channel: String, + vehicle: Vehicle, + vehicle_to_delete: PlanetSideGUID, + manifest: VehicleManifest + ) extends SelfRespondingEvent + + final case class UpdateBroadcastPrivileges( + zoneId: Int, + gateMapId: Int, + fromFactions: Set[PlanetSideEmpire.Value], + toFactions: Set[PlanetSideEmpire.Value] + ) extends SelfRespondingEvent + + final case class LockedZoneUpdate(zone: Zone, timeUntilUnlock: Long) extends SelfRespondingEvent + + final case class UnlockedZoneUpdate(zone: Zone) extends SelfRespondingEvent + + final case class LogStatusChange(name: String) extends SelfRespondingEvent +} diff --git a/src/main/scala/net/psforever/services/galaxy/GalaxyService.scala b/src/main/scala/net/psforever/services/galaxy/GalaxyService.scala index a526db02e..79569a4e0 100644 --- a/src/main/scala/net/psforever/services/galaxy/GalaxyService.scala +++ b/src/main/scala/net/psforever/services/galaxy/GalaxyService.scala @@ -1,107 +1,21 @@ -// Copyright (c) 2017 PSForever +// Copyright (c) 2017-2026 PSForever package net.psforever.services.galaxy -import akka.actor.Actor -import net.psforever.objects.zones.Zone -import net.psforever.packet.game.BuildingInfoUpdateMessage -import net.psforever.services.{GenericEventBus, Service} +import akka.actor.Props +import net.psforever.services.base.{EventSystemStamp, GenericEventService} -class GalaxyService extends Actor { - private[this] val log = org.log4s.getLogger - - val GalaxyEvents = new GenericEventBus[GalaxyServiceResponse] - - def receive: Receive = { - case Service.Join(faction) if "TRNCVS".containsSlice(faction) => - val path = s"/$faction/Galaxy" - GalaxyEvents.subscribe(sender(), path) - - case Service.Join("galaxy") => - val path = s"/Galaxy" - GalaxyEvents.subscribe(sender(), path) - - case Service.Join(channel) => - val path = s"/$channel/Galaxy" - GalaxyEvents.subscribe(sender(), path) - - case Service.Leave(None) => - GalaxyEvents.unsubscribe(sender()) - - case Service.Leave(Some(channel)) => - val path = s"/$channel/Galaxy" - GalaxyEvents.unsubscribe(sender(), path) - - case Service.LeaveAll() => - GalaxyEvents.unsubscribe(sender()) - - case GalaxyServiceMessage(forChannel, action) => - action match { - case GalaxyAction.MapUpdate(msg: BuildingInfoUpdateMessage) => - GalaxyEvents.publish( - GalaxyServiceResponse(s"/Galaxy", GalaxyResponse.MapUpdate(msg)) - ) - - case GalaxyAction.UpdateBroadcastPrivileges(zoneId, gateMapId, fromFactions, toFactions) => - GalaxyEvents.publish( - GalaxyServiceResponse( - s"/$forChannel/Galaxy", - GalaxyResponse.UpdateBroadcastPrivileges(zoneId, gateMapId, fromFactions, toFactions) - ) - ) - - case GalaxyAction.FlagMapUpdate(msg) => - GalaxyEvents.publish( - GalaxyServiceResponse(s"/Galaxy", GalaxyResponse.FlagMapUpdate(msg)) - ) - - case GalaxyAction.TransferPassenger(_, temp_channel, vehicle, vehicle_to_delete, manifest) => - GalaxyEvents.publish( - GalaxyServiceResponse( - s"/$forChannel/Galaxy", - GalaxyResponse.TransferPassenger(temp_channel, vehicle, vehicle_to_delete, manifest) - ) - ) - - case GalaxyAction.LockedZoneUpdate(zone, time) => - GalaxyEvents.publish( - GalaxyServiceResponse( - s"/Galaxy", - GalaxyResponse.LockedZoneUpdate(zone, time) - ) - ) - - case GalaxyAction.UnlockedZoneUpdate(zone) => - GalaxyEvents.publish( - GalaxyServiceResponse( - s"/Galaxy", - GalaxyResponse.UnlockedZoneUpdate(zone) - ) - ) - - case GalaxyAction.LogStatusChange(name) => - GalaxyEvents.publish( - GalaxyServiceResponse( - s"/Galaxy", - GalaxyResponse.LogStatusChange(name) - ) - ) - - case GalaxyAction.SendResponse(msg) => - GalaxyEvents.publish( - GalaxyServiceResponse( - s"/Galaxy", - GalaxyResponse.SendResponse(msg) - ) - ) - case _ => ; - } - - case Zone.HotSpot.Update(faction, zone_num, priority, info) => - GalaxyEvents.publish( - GalaxyServiceResponse(s"/$faction/Galaxy", GalaxyResponse.HotSpotUpdate(zone_num, priority, info)) - ) - - case msg => - log.warn(s"Unhandled message $msg from ${sender()}") +case object GalaxyStamp extends EventSystemStamp { + override def routing(channel: String): String = { + if (channel.trim.isEmpty) { + "/out" + } else { + super.routing(channel) + } + } +} + +object GalaxyService { + def apply(): Props = { + Props(classOf[GenericEventService], GalaxyStamp) } } diff --git a/src/main/scala/net/psforever/services/galaxy/GalaxyServiceMessage.scala b/src/main/scala/net/psforever/services/galaxy/GalaxyServiceMessage.scala deleted file mode 100644 index dcdaebfc2..000000000 --- a/src/main/scala/net/psforever/services/galaxy/GalaxyServiceMessage.scala +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2017 PSForever -package net.psforever.services.galaxy - -import net.psforever.objects.Vehicle -import net.psforever.objects.vehicles.VehicleManifest -import net.psforever.objects.zones.Zone -import net.psforever.packet.PlanetSideGamePacket -import net.psforever.packet.game.{BuildingInfoUpdateMessage, CaptureFlagUpdateMessage} -import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} - -final case class GalaxyServiceMessage(forChannel: String, actionMessage: GalaxyAction.Action) - -object GalaxyServiceMessage { - def apply(actionMessage: GalaxyAction.Action): GalaxyServiceMessage = GalaxyServiceMessage("", actionMessage) -} - -object GalaxyAction { - trait Action - - final case class MapUpdate(msg: BuildingInfoUpdateMessage) extends Action - final case class FlagMapUpdate(msg: CaptureFlagUpdateMessage) extends Action - - final case class TransferPassenger( - player_guid: PlanetSideGUID, - temp_channel: String, - vehicle: Vehicle, - vehicle_to_delete: PlanetSideGUID, - manifest: VehicleManifest - ) extends Action - - final case class UpdateBroadcastPrivileges( - zoneId: Int, - gateMapId: Int, - fromFactions: Set[PlanetSideEmpire.Value], - toFactions: Set[PlanetSideEmpire.Value] - ) extends Action - - final case class LockedZoneUpdate(zone: Zone, timeUntilUnlock: Long) extends Action - - final case class UnlockedZoneUpdate(zone: Zone) extends Action - - final case class LogStatusChange(name: String) extends Action - - final case class SendResponse(msg: PlanetSideGamePacket) extends Action -} diff --git a/src/main/scala/net/psforever/services/galaxy/GalaxyServiceResponse.scala b/src/main/scala/net/psforever/services/galaxy/GalaxyServiceResponse.scala deleted file mode 100644 index dcaf93817..000000000 --- a/src/main/scala/net/psforever/services/galaxy/GalaxyServiceResponse.scala +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2017 PSForever -package net.psforever.services.galaxy - -import net.psforever.objects.Vehicle -import net.psforever.objects.vehicles.VehicleManifest -import net.psforever.objects.zones.{HotSpotInfo, Zone} -import net.psforever.packet.PlanetSideGamePacket -import net.psforever.packet.game.{BuildingInfoUpdateMessage, CaptureFlagUpdateMessage} -import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} -import net.psforever.services.GenericEventBusMsg - -final case class GalaxyServiceResponse(channel: String, replyMessage: GalaxyResponse.Response) - extends GenericEventBusMsg - -object GalaxyResponse { - trait Response - - final case class HotSpotUpdate(zone_id: Int, priority: Int, host_spot_info: List[HotSpotInfo]) extends Response - final case class MapUpdate(msg: BuildingInfoUpdateMessage) extends Response - final case class FlagMapUpdate(msg: CaptureFlagUpdateMessage) extends Response - - - final case class TransferPassenger( - temp_channel: String, - vehicle: Vehicle, - vehicle_to_delete: PlanetSideGUID, - manifest: VehicleManifest - ) extends Response - - final case class UpdateBroadcastPrivileges( - zoneId: Int, - gateMapId: Int, - fromFactions: Set[PlanetSideEmpire.Value], - toFactions: Set[PlanetSideEmpire.Value] - ) extends Response - - final case class LockedZoneUpdate(zone: Zone, timeUntilUnlock: Long) extends Response - - final case class UnlockedZoneUpdate(zone: Zone) extends Response - - final case class LogStatusChange(name: String) extends Response - - final case class SendResponse(msg: PlanetSideGamePacket) extends Response -} diff --git a/src/main/scala/net/psforever/services/hart/HartTimer.scala b/src/main/scala/net/psforever/services/hart/HartTimer.scala index 3de884fe0..576eadcfe 100644 --- a/src/main/scala/net/psforever/services/hart/HartTimer.scala +++ b/src/main/scala/net/psforever/services/hart/HartTimer.scala @@ -4,8 +4,11 @@ package net.psforever.services.hart import akka.actor.{Actor, ActorRef, Cancellable} import net.psforever.objects.Default import net.psforever.objects.zones.Zone -import net.psforever.services.local.{LocalAction, LocalServiceMessage} -import net.psforever.services.{GenericEventBus, GenericEventBusMsg} +import net.psforever.services.base.EventSystemStamp +import net.psforever.services.base.bus.GenericEventBus +import net.psforever.services.base.envelope.{GenericResponseEnvelope, MessageEnvelope, NoReply} +import net.psforever.services.base.message.EventResponse +import net.psforever.services.local.LocalAction import net.psforever.types.{HartSequence, PlanetSideGUID} import scala.concurrent.duration._ @@ -20,7 +23,7 @@ import scala.concurrent.ExecutionContext.Implicits.global */ class HartTimer(zone: Zone) extends Actor { /** since the system is zone-locked, caching this value is fine */ - val zoneId = zone.id + val zoneId: String = zone.id /** all of the paired HART facility amenities and the shuttle housed in that facility (in that order) */ var padAndShuttlePairs: List[(PlanetSideGUID, PlanetSideGUID)] = List() @@ -43,10 +46,10 @@ class HartTimer(zone: Zone) extends Actor { var timer: Cancellable = Default.Cancellable /** a message bus to which all associated orbital shuttle pads are subscribed */ - val padEvents = new GenericEventBus[HartTimer.Command] + val padEvents = new GenericEventBus /** cache common messages */ - val shuttleDockedInThisZone = HartTimer.ShuttleDocked(zoneId) - val shuttleFreeFromDockInThisZone = HartTimer.ShuttleFreeFromDock(zoneId) + val shuttleDockedInThisZone: HartTimer.ShuttleDocked = HartTimer.ShuttleDocked(zoneId) + val shuttleFreeFromDockInThisZone: HartTimer.ShuttleFreeFromDock = HartTimer.ShuttleFreeFromDock(zoneId) /** the behaviors common to both the inert and active operations of the hart */ val commonBehavior: Receive = { @@ -105,7 +108,7 @@ class HartTimer(zone: Zone) extends Actor { event.prerequisiteUpdate match { case Some(fields) => val times = event.timeFields(time) - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( forChannel, LocalAction.ShuttleEvent(HartTimer.OrbitalShuttleEvent( fields.u1, fields.u2, times.t1, times.t2, times.t3, padAndShuttlePairs zip Seq(20, 20, 20) @@ -113,7 +116,7 @@ class HartTimer(zone: Zone) extends Actor { ) case None => ; } - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( forChannel, LocalAction.ShuttleEvent( HartTimer.analyzeEvent(event, padAndShuttlePairs, time) @@ -153,17 +156,17 @@ class HartTimer(zone: Zone) extends Actor { val evt = HartTimer.analyzeEvent(event, padAndShuttlePairs) event.docked match { case Some(true) if currEvent.docked.isEmpty => - zone.LocalEvents ! LocalServiceMessage(zoneId, LocalAction.ShuttleEvent(evt)) + zone.LocalEvents ! MessageEnvelope(zoneId, LocalAction.ShuttleEvent(evt)) padEvents.publish( shuttleDockedInThisZone ) case Some(false) if currEvent.docked.contains(true) => padEvents.publish( shuttleFreeFromDockInThisZone ) context.system.scheduler.scheduleOnce( delay = 10 milliseconds, zone.LocalEvents, - LocalServiceMessage(zoneId, LocalAction.ShuttleEvent(evt)) + MessageEnvelope(zoneId, LocalAction.ShuttleEvent(evt)) ) case _ => - zone.LocalEvents ! LocalServiceMessage(zoneId, LocalAction.ShuttleEvent(evt)) + zone.LocalEvents ! MessageEnvelope(zoneId, LocalAction.ShuttleEvent(evt)) } if (currEvent.lockedDoors != event.lockedDoors) { padEvents.publish( if(event.lockedDoors) HartTimer.LockDoors else HartTimer.UnlockDoors ) @@ -254,14 +257,22 @@ object HartTimer { pairs: List[((PlanetSideGUID, PlanetSideGUID), Int)] ) + case object HartStamp extends EventSystemStamp + /** * Design for the envelop for the message bus * to relay instructions back to the individual facility amenity portions of this HART system. * The channel is blank because it does not need special designation. */ - trait Command extends GenericEventBusMsg { def channel: String = "" } + trait Command extends GenericResponseEnvelope { + def originalChannel: String = "" + def channel: String = "" + def filter: PlanetSideGUID = Default.GUID0 + def stamp: EventSystemStamp = HartStamp + def reply: EventResponse = NoReply + } /** - * Forbid entry through the boartding gantry doors. + * Forbid entry through the boarding gantry doors. */ case object LockDoors extends Command /** diff --git a/src/main/scala/net/psforever/services/hart/HartTimerActions.scala b/src/main/scala/net/psforever/services/hart/HartTimerActions.scala index 291c4bbc3..442f60c4b 100644 --- a/src/main/scala/net/psforever/services/hart/HartTimerActions.scala +++ b/src/main/scala/net/psforever/services/hart/HartTimerActions.scala @@ -3,7 +3,8 @@ package net.psforever.services.hart import net.psforever.objects.Vehicle import net.psforever.objects.serverobject.shuttle.OrbitalShuttlePad -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.local.LocalAction object HartTimerActions { /** @@ -17,7 +18,7 @@ object HartTimerActions { if(toChannel.equals(zone.id)) { shuttle.MountedIn = pad.GUID } - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( toChannel, LocalAction.ShuttleDock(pad.GUID, shuttle.GUID, 3) ) @@ -34,7 +35,7 @@ object HartTimerActions { if(toChannel.equals(zone.id)) { shuttle.MountedIn = None } - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( toChannel, LocalAction.ShuttleUndock(pad.GUID, shuttle.GUID, shuttle.Position, shuttle.Orientation) ) @@ -51,7 +52,7 @@ object HartTimerActions { if(toChannel.equals(zone.id)) { shuttle.Flying = state } - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( toChannel, LocalAction.ShuttleState(shuttle.GUID, shuttle.Position, shuttle.Orientation, state) ) diff --git a/src/main/scala/net/psforever/services/local/LocalAction.scala b/src/main/scala/net/psforever/services/local/LocalAction.scala new file mode 100644 index 000000000..4e42bca59 --- /dev/null +++ b/src/main/scala/net/psforever/services/local/LocalAction.scala @@ -0,0 +1,151 @@ +// Copyright (c) 2017-2026 PSForever +package net.psforever.services.local + +import net.psforever.objects.{Default, PlanetSideGameObject, TelepadDeployable, Vehicle} +import net.psforever.objects.ce.{Deployable, DeployedItem} +import net.psforever.objects.serverobject.doors.Door +import net.psforever.objects.serverobject.llu.CaptureFlag +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.base.message.{EventMessage, EventResponse, SelfRespondingEvent, SendResponse} +import net.psforever.services.hart.HartTimer.OrbitalShuttleEvent +import net.psforever.types.{PlanetSideGUID, Vector3} + +object LocalAction { + sealed trait IsADoorMessage extends SelfRespondingEvent + + sealed trait IsAHackMessage extends SelfRespondingEvent + + final case class DeployItem(item: Deployable) extends EventMessage { + def response(): EventResponse = { + val definition = item.Definition + val objectData = definition.Packet.ConstructorData(item).get + SendResponse(ObjectCreateMessage(definition.ObjectId, item.GUID, objectData)) + } + } + + final case class DeployableMapIcon(behavior: DeploymentAction.Value, deployInfo: DeployableInfo) extends SelfRespondingEvent + + final case class DeployableUIFor(obj: DeployedItem.Value) extends SelfRespondingEvent + + final case class Detonate(guid: PlanetSideGUID, obj: PlanetSideGameObject) extends SelfRespondingEvent + + final case class DoorOpens(continent: Zone, door: Door) extends IsADoorMessage + + final case class DoorCloses(door_guid: PlanetSideGUID) extends IsADoorMessage + + final case class EliminateDeployable( + obj: Deployable, + object_guid: PlanetSideGUID, + pos: Vector3, + deletionEffect: Int + ) extends SelfRespondingEvent + + final case class HackClear( + target_guid: PlanetSideGUID, + unk1: Long, + unk2: HackState7 = HackState7.Unk8 + ) extends IsAHackMessage + + final case class HackObject(target_guid: PlanetSideGUID, unk1: Long, unk2: HackState7) extends IsAHackMessage + + final case class LluSpawned(llu: CaptureFlag) extends SelfRespondingEvent + + final case class LluDespawned( + guid: PlanetSideGUID, + position: Vector3 + ) extends SelfRespondingEvent + + final case class GenericActionMessage(action_num: GenericAction) extends SelfRespondingEvent + + final case class ProximityTerminalAction( + terminal: Terminal with ProximityUnit, + target: PlanetSideGameObject + ) extends EventResponse + + final case class ProximityTerminalEffect( + object_guid: PlanetSideGUID, + effectState: Boolean + ) extends SelfRespondingEvent + + final case class RouterTelepadMessage(msg: String) extends SelfRespondingEvent + + final case class RouterTelepadTransport( + passenger_guid: PlanetSideGUID, + src_guid: PlanetSideGUID, + dest_guid: PlanetSideGUID + ) extends SelfRespondingEvent + + final case class ShuttleDock(pad_guid: PlanetSideGUID, shuttle_guid: PlanetSideGUID, toSlot: Int) extends SelfRespondingEvent + + final case class ShuttleUndock( + pad_guid: PlanetSideGUID, + shuttle_guid: PlanetSideGUID, + pos: Vector3, orient: Vector3 + ) extends SelfRespondingEvent + + final case class ShuttleEvent(ev: OrbitalShuttleEvent) extends SelfRespondingEvent + + final case class ShuttleState(guid: PlanetSideGUID, pos: Vector3, orientation: Vector3, state: Int) extends SelfRespondingEvent + + final case class StartRouterInternalTelepad( + router_guid: PlanetSideGUID, + obj_guid: PlanetSideGUID, + obj: Utility.InternalTelepad + ) extends SelfRespondingEvent + + final case class ToggleTeleportSystem( + router: Vehicle, + systemPlan: Option[(Utility.InternalTelepad, TelepadDeployable)] + ) extends SelfRespondingEvent + + final case class TriggerEffectAtLocation( + target: PlanetSideGUID, + effect: String, + effectInfo: Option[TriggeredEffect] = None, + triggeredLocation: Option[TriggeredEffectLocation] = None + ) extends EventResponse + + final case class TriggerEffect(effect: String, target: PlanetSideGUID) extends EventMessage { + def response(): EventResponse = { + TriggerEffectAtLocation(target, effect) + } + } + + final case class TriggerEffectInfo(target: PlanetSideGUID, effect: String, unk1: Boolean, unk2: Long) extends EventMessage { + def response(): EventResponse = { + TriggerEffectAtLocation(target, effect, Some(TriggeredEffect(unk1, unk2))) + } + } + + final case class TriggerEffectLocation( + effect: String, + pos: Vector3, + orient: Vector3 + ) extends EventMessage { + def response(): EventResponse = { + TriggerEffectAtLocation(Default.GUID0, effect, None, Some(TriggeredEffectLocation(pos, orient))) + } + } + + final case class TriggerSound( + sound: TriggeredSound.Value, + pos: Vector3, + unk: Int, + volume: Float + ) extends SelfRespondingEvent + + final case class UpdateForceDomeStatus( + building_guid: PlanetSideGUID, + activated: Boolean + ) extends SelfRespondingEvent + + final case class RechargeVehicleWeapon( + mountable_guid: PlanetSideGUID, + weapon_guid: PlanetSideGUID + ) extends SelfRespondingEvent + + final case class ForceZoneChange(zone: Zone) extends SelfRespondingEvent +} diff --git a/src/main/scala/net/psforever/services/local/LocalService.scala b/src/main/scala/net/psforever/services/local/LocalService.scala index 751dce97b..e6aad046f 100644 --- a/src/main/scala/net/psforever/services/local/LocalService.scala +++ b/src/main/scala/net/psforever/services/local/LocalService.scala @@ -1,362 +1,19 @@ // Copyright (c) 2017 PSForever package net.psforever.services.local -import akka.actor.{Actor, Props} -import net.psforever.objects.serverobject.terminals.Terminal +import akka.actor.Props import net.psforever.objects.zones.Zone -import net.psforever.packet.game.{ObjectCreateMessage, TriggeredEffect, TriggeredEffectLocation} -import net.psforever.services.local.support.CaptureFlagManager -import net.psforever.types.PlanetSideGUID import net.psforever.services.local.support._ -import net.psforever.services.{GenericEventBus, Service} -import net.psforever.services.support.SupportActor +import net.psforever.services.base.{EventSystemStamp, GenericEventServiceWithSupport} -class LocalService(zone: Zone) extends Actor { - private val doorCloser = context.actorOf( - Props[DoorCloseActor](), s"${zone.id}-local-door-closer" - ) - private val hackClearer = context.actorOf( - Props[HackClearActor](), s"${zone.id}-local-hack-clearer" - ) - private val hackCapturer = context.actorOf( - Props[HackCaptureActor](), s"${zone.id}-local-hack-capturer" - ) - private val captureFlagManager = context.actorOf( - Props(classOf[CaptureFlagManager], zone), s"${zone.id}-local-capture-flag-manager" - ) - private[this] val log = org.log4s.getLogger +case object LocalStamp extends EventSystemStamp - val LocalEvents = new GenericEventBus[LocalServiceResponse] - - def receive: Receive = { - case Service.Join(channel) => - val path = s"/$channel/Local" - LocalEvents.subscribe(sender(), path) - - case Service.Leave(None) => - LocalEvents.unsubscribe(sender()) - - case Service.Leave(Some(channel)) => - val path = s"/$channel/Local" - LocalEvents.unsubscribe(sender(), path) - - case Service.LeaveAll() => - LocalEvents.unsubscribe(sender()) - - case LocalServiceMessage(forChannel, action) => - action match { - case LocalAction.DeployItem(item) => - val definition = item.Definition - val objectData = definition.Packet.ConstructorData(item).get - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - Service.defaultPlayerGUID, - LocalResponse.SendResponse(ObjectCreateMessage(definition.ObjectId, item.GUID, objectData)) - ) - ) - case LocalAction.DeployableMapIcon(player_guid, behavior, deployInfo) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - player_guid, - LocalResponse.DeployableMapIcon(behavior, deployInfo) - ) - ) - case LocalAction.DeployableUIFor(item) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - Service.defaultPlayerGUID, - LocalResponse.DeployableUIFor(item) - ) - ) - case LocalAction.Detonate(guid, obj) => - LocalEvents.publish( - LocalServiceResponse(s"/$forChannel/Local", Service.defaultPlayerGUID, LocalResponse.Detonate(guid, obj)) - ) - case LocalAction.DoorOpens(player_guid, _, door) => - doorCloser ! DoorCloseActor.DoorIsOpen(door, zone) - LocalEvents.publish( - LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.DoorOpens(door.GUID)) - ) - case LocalAction.DoorCloses(player_guid, door_guid) => - LocalEvents.publish( - LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.DoorCloses(door_guid)) - ) - case LocalAction.DoorSlamsShut(door) => - val door_guid = door.GUID - doorCloser ! SupportActor.HurrySpecific(List(door), zone) - LocalEvents.publish( - LocalServiceResponse(s"/$forChannel/Local", Service.defaultPlayerGUID, LocalResponse.DoorCloses(door_guid)) - ) - case LocalAction.EliminateDeployable(obj, guid, pos, effect) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - Service.defaultPlayerGUID, - LocalResponse.EliminateDeployable(obj, guid, pos, effect) - ) - ) - case LocalAction.HackClear(player_guid, target, unk1, unk2) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - player_guid, - LocalResponse.SendHackMessageHackCleared(target.GUID, unk1, unk2) - ) - ) - case LocalAction.HackTemporarily(player_guid, _, target, hackValue, hackClear, duration, unk2) => - hackClearer ! HackClearActor.ObjectIsHacked(target, zone, hackClear, unk2, duration) - LocalEvents.publish( - LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackObject(target.GUID, hackValue, unk2)) - ) - case LocalAction.ClearTemporaryHack(_, target) => - hackClearer ! HackClearActor.ObjectIsResecured(target) - - case LocalAction.ResecureCaptureTerminal(target, hacker) => - hackCapturer ! HackCaptureActor.ResecureCaptureTerminal(target, zone, hacker) - case LocalAction.StartCaptureTerminalHack(target) => - hackCapturer ! HackCaptureActor.StartCaptureTerminalHack(target, zone, 0, 8L) - case LocalAction.LluCaptured(llu) => - hackCapturer ! HackCaptureActor.FlagCaptured(llu) - case LocalAction.LluLost(llu) => - hackCapturer ! HackCaptureActor.FlagLost(llu) - - case LocalAction.LluSpawned(player_guid, llu) => - // Forward to all clients to create object locally - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - player_guid, - LocalResponse.LluSpawned(llu) - ) - ) - - case LocalAction.LluDespawned(player_guid, guid, position) => - // Forward to all clients to destroy object locally - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - player_guid, - LocalResponse.LluDespawned(guid, position) - ) - ) - - case LocalAction.SendPacket(packet) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - PlanetSideGUID(-1), - LocalResponse.SendPacket(packet) - ) - ) - - case LocalAction.SendPlanetsideAttributeMessage(player_guid, target_guid, attribute_number, attribute_value) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - player_guid, - LocalResponse.PlanetsideAttribute(target_guid, attribute_number, attribute_value) - ) - ) - case LocalAction.SendGenericObjectActionMessage(player_guid, target_guid, action_number) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - player_guid, - LocalResponse.GenericObjectAction(target_guid, action_number) - ) - ) - - case LocalAction.SendChatMsg(player_guid, msg) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - player_guid, - LocalResponse.ChatMessage(msg) - ) - ) - - case LocalAction.SendGenericActionMessage(player_guid, action_number) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - player_guid, - LocalResponse.GenericActionMessage(action_number) - ) - ) - case LocalAction.RouterTelepadMessage(msg) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - Service.defaultPlayerGUID, - LocalResponse.RouterTelepadMessage(msg) - ) - ) - case LocalAction.RouterTelepadTransport(player_guid, passenger_guid, src_guid, dest_guid) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - player_guid, - LocalResponse.RouterTelepadTransport(passenger_guid, src_guid, dest_guid) - ) - ) - case LocalAction.SendResponse(pkt) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - Service.defaultPlayerGUID, - LocalResponse.SendResponse(pkt) - ) - ) - case LocalAction.SetEmpire(object_guid, empire) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - Service.defaultPlayerGUID, - LocalResponse.SetEmpire(object_guid, empire) - ) - ) - case LocalAction.ShuttleDock(pad, shuttle, slot) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - Service.defaultPlayerGUID, - LocalResponse.ShuttleDock(pad, shuttle, slot) - ) - ) - case LocalAction.ShuttleUndock(pad, shuttle, pos, orient) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - Service.defaultPlayerGUID, - LocalResponse.ShuttleUndock(pad, shuttle, pos, orient) - ) - ) - case LocalAction.ShuttleEvent(ev) => - LocalEvents.publish( - LocalServiceResponse(s"/$forChannel/Local", Service.defaultPlayerGUID, LocalResponse.ShuttleEvent(ev)) - ) - case LocalAction.ShuttleState(guid, pos, orient, state) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - Service.defaultPlayerGUID, - LocalResponse.ShuttleState(guid, pos, orient, state) - ) - ) - case LocalAction.StartRouterInternalTelepad(router_guid, obj_guid, obj) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - Service.defaultPlayerGUID, - LocalResponse.StartRouterInternalTelepad(router_guid, obj_guid, obj) - ) - ) - case LocalAction.ToggleTeleportSystem(player_guid, router, system_plan) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - player_guid, - LocalResponse.ToggleTeleportSystem(router, system_plan) - ) - ) - case LocalAction.TriggerEffect(player_guid, effect, target) => - LocalEvents.publish( - LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.TriggerEffect(target, effect)) - ) - case LocalAction.TriggerEffectLocation(player_guid, effect, pos, orient) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - player_guid, - LocalResponse.TriggerEffect(PlanetSideGUID(0), effect, None, Some(TriggeredEffectLocation(pos, orient))) - ) - ) - case LocalAction.TriggerEffectInfo(player_guid, effect, target, unk1, unk2) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - player_guid, - LocalResponse.TriggerEffect(target, effect, Some(TriggeredEffect(unk1, unk2))) - ) - ) - case LocalAction.TriggerSound(player_guid, sound, pos, unk, volume) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - player_guid, - LocalResponse.TriggerSound(sound, pos, unk, volume) - ) - ) - case LocalAction.UpdateForceDomeStatus(player_guid, building_guid, activated) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - player_guid, - LocalResponse.UpdateForceDomeStatus(building_guid, activated) - ) - ) - case LocalAction.RechargeVehicleWeapon(player_guid, vehicle_guid, weapon_guid) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - player_guid, - LocalResponse.RechargeVehicleWeapon(vehicle_guid, weapon_guid) - ) - ) - case LocalAction.ForceZoneChange(zone) => - LocalEvents.publish( - LocalServiceResponse( - s"/$forChannel/Local", - Service.defaultPlayerGUID, - LocalResponse.ForceZoneChange(zone) - ) - ) - case _ => ; - } - - //response from DoorCloseActor - case DoorCloseActor.CloseTheDoor(door_guid, _) => - LocalEvents.publish( - LocalServiceResponse(s"/${zone.id}/Local", Service.defaultPlayerGUID, LocalResponse.DoorCloses(door_guid)) - ) - - //response from HackClearActor - case HackClearActor.SendHackMessageHackCleared(target_guid, _, unk1, unk2) => - log.info(s"Clearing hack for $target_guid") - LocalEvents.publish( - LocalServiceResponse( - s"/${zone.id}/Local", - Service.defaultPlayerGUID, - LocalResponse.SendHackMessageHackCleared(target_guid, unk1, unk2) - ) - ) - - //message from ProximityTerminalControl - case Terminal.StartProximityEffect(terminal) => - LocalEvents.publish( - LocalServiceResponse( - s"/${zone.id}/Local", - PlanetSideGUID(0), - LocalResponse.ProximityTerminalEffect(terminal.GUID, effectState = true) - ) - ) - case Terminal.StopProximityEffect(terminal) => - LocalEvents.publish( - LocalServiceResponse( - s"/${zone.id}/Local", - PlanetSideGUID(0), - LocalResponse.ProximityTerminalEffect(terminal.GUID, effectState = false) - ) - ) - - // Forward all CaptureFlagManager messages - case msg: CaptureFlagManager.Command => - captureFlagManager.forward(msg) - - case msg => - log.warn(s"Unhandled message $msg from ${sender()}") +object LocalService { + def apply(zone: Zone): Props = { + Props( + classOf[GenericEventServiceWithSupport], + LocalStamp, + List(DoorCloserSupport, HackClearSupport, HackCaptureSupport, CaptureFlagSupport(zone)) + ) } } diff --git a/src/main/scala/net/psforever/services/local/LocalServiceMessage.scala b/src/main/scala/net/psforever/services/local/LocalServiceMessage.scala deleted file mode 100644 index ced629458..000000000 --- a/src/main/scala/net/psforever/services/local/LocalServiceMessage.scala +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) 2017 PSForever -package net.psforever.services.local - -import net.psforever.objects.ce.{Deployable, DeployedItem} -import net.psforever.objects.serverobject.PlanetSideServerObject -import net.psforever.objects.serverobject.doors.Door -import net.psforever.objects.serverobject.hackable.Hackable -import net.psforever.objects.serverobject.llu.CaptureFlag -import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal -import net.psforever.objects.sourcing.PlayerSource -import net.psforever.objects.vehicles.Utility -import net.psforever.objects.zones.Zone -import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle} -import net.psforever.packet.PlanetSideGamePacket -import net.psforever.packet.game.GenericObjectActionEnum.GenericObjectActionEnum -import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum -import net.psforever.packet.game.{ChatMsg, DeployableInfo, DeploymentAction, GenericAction, HackState7, TriggeredSound} -import net.psforever.services.hart.HartTimer.OrbitalShuttleEvent -import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3} - -final case class LocalServiceMessage(forChannel: String, actionMessage: LocalAction.Action) - -object LocalServiceMessage { - final case class Deployables(msg: Any) -} - -object LocalAction { - trait Action - - final case class DeployItem(item: Deployable) extends Action - final case class DeployableMapIcon( - player_guid: PlanetSideGUID, - behavior: DeploymentAction.Value, - deployInfo: DeployableInfo - ) extends Action - final case class DeployableUIFor(obj: DeployedItem.Value) extends Action - final case class Detonate(guid: PlanetSideGUID, obj: PlanetSideGameObject) extends Action - final case class DoorOpens(player_guid: PlanetSideGUID, continent: Zone, door: Door) extends Action - final case class DoorCloses(player_guid: PlanetSideGUID, door_guid: PlanetSideGUID) extends Action - final case class DoorSlamsShut(door: Door) extends Action - final case class EliminateDeployable( - obj: Deployable, - object_guid: PlanetSideGUID, - pos: Vector3, - deletionEffect: Int - ) extends Action - final case class HackClear(player_guid: PlanetSideGUID, target: PlanetSideServerObject, unk1: Long, unk2: HackState7 = HackState7.Unk8) - extends Action - final case class HackTemporarily( - player_guid: PlanetSideGUID, - continent: Zone, - target: PlanetSideServerObject, - hackValue: Long, - hackClearValue: Long, - duration: Int, - unk2: HackState7 = HackState7.Unk8 - ) extends Action - final case class ClearTemporaryHack(player_guid: PlanetSideGUID, target: PlanetSideServerObject with Hackable) - extends Action - - final case class ResecureCaptureTerminal(target: CaptureTerminal, hacker: PlayerSource) extends Action - final case class StartCaptureTerminalHack(target: CaptureTerminal) extends Action - final case class LluCaptured(llu: CaptureFlag) extends Action - final case class LluLost(llu: CaptureFlag) extends Action - final case class LluSpawned(player_guid: PlanetSideGUID, llu: CaptureFlag) extends Action - final case class LluDespawned(player_guid: PlanetSideGUID, guid: PlanetSideGUID, position: Vector3) extends Action - - final case class SendPacket(packet: PlanetSideGamePacket) extends Action - final case class SendPlanetsideAttributeMessage( - player_guid: PlanetSideGUID, - target: PlanetSideGUID, - attribute_number: PlanetsideAttributeEnum, - attribute_value: Long - ) extends Action - final case class SendGenericObjectActionMessage( - player_guid: PlanetSideGUID, - target: PlanetSideGUID, - action_number: GenericObjectActionEnum - ) extends Action - - final case class SendChatMsg( - player_guid: PlanetSideGUID, - msg: ChatMsg - ) extends Action - - final case class SendGenericActionMessage( - player_guid: PlanetSideGUID, - action_number: GenericAction - ) extends Action - final case class RouterTelepadMessage(msg: String) extends Action - final case class RouterTelepadTransport( - player_guid: PlanetSideGUID, - passenger_guid: PlanetSideGUID, - src_guid: PlanetSideGUID, - dest_guid: PlanetSideGUID - ) extends Action - final case class SendResponse(pkt: PlanetSideGamePacket) extends Action - final case class SetEmpire(object_guid: PlanetSideGUID, empire: PlanetSideEmpire.Value) extends Action - final case class ShuttleDock(pad_guid: PlanetSideGUID, shuttle_guid: PlanetSideGUID, toSlot: Int) extends Action - final case class ShuttleUndock( - pad_guid: PlanetSideGUID, - shuttle_guid: PlanetSideGUID, - pos: Vector3, orient: Vector3 - ) extends Action - final case class ShuttleEvent(ev: OrbitalShuttleEvent) extends Action - final case class ShuttleState(guid: PlanetSideGUID, pos: Vector3, orientation: Vector3, state: Int) extends Action - final case class StartRouterInternalTelepad( - router_guid: PlanetSideGUID, - obj_guid: PlanetSideGUID, - obj: Utility.InternalTelepad - ) extends Action - final case class ToggleTeleportSystem( - player_guid: PlanetSideGUID, - router: Vehicle, - systemPlan: Option[(Utility.InternalTelepad, TelepadDeployable)] - ) extends Action - final case class TriggerEffect(player_guid: PlanetSideGUID, effect: String, target: PlanetSideGUID) extends Action - final case class TriggerEffectInfo( - player_guid: PlanetSideGUID, - effect: String, - target: PlanetSideGUID, - unk1: Boolean, - unk2: Long - ) extends Action - final case class TriggerEffectLocation(player_guid: PlanetSideGUID, effect: String, pos: Vector3, orient: Vector3) - extends Action - final case class TriggerSound( - player_guid: PlanetSideGUID, - sound: TriggeredSound.Value, - pos: Vector3, - unk: Int, - volume: Float - ) extends Action - final case class UpdateForceDomeStatus(player_guid: PlanetSideGUID, building_guid: PlanetSideGUID, activated: Boolean) - extends Action - final case class RechargeVehicleWeapon( - player_guid: PlanetSideGUID, - mountable_guid: PlanetSideGUID, - weapon_guid: PlanetSideGUID - ) extends Action - final case class ForceZoneChange(zone: Zone) extends Action -} diff --git a/src/main/scala/net/psforever/services/local/LocalServiceResponse.scala b/src/main/scala/net/psforever/services/local/LocalServiceResponse.scala deleted file mode 100644 index 8834ab197..000000000 --- a/src/main/scala/net/psforever/services/local/LocalServiceResponse.scala +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) 2017 PSForever -package net.psforever.services.local - -import net.psforever.objects.serverobject.llu.CaptureFlag -import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle} -import net.psforever.objects.ce.{Deployable, DeployedItem} -import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal} -import net.psforever.objects.vehicles.Utility -import net.psforever.objects.zones.Zone -import net.psforever.packet.game.GenericObjectActionEnum.GenericObjectActionEnum -import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum -import net.psforever.packet.PlanetSideGamePacket -import net.psforever.packet.game._ -import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3} -import net.psforever.services.GenericEventBusMsg -import net.psforever.services.hart.HartTimer.OrbitalShuttleEvent - -final case class LocalServiceResponse( - channel: String, - avatar_guid: PlanetSideGUID, - replyMessage: LocalResponse.Response -) extends GenericEventBusMsg - -object LocalResponse { - trait Response - - final case class DeployableMapIcon(action: DeploymentAction.Value, deployInfo: DeployableInfo) extends Response - final case class DeployableUIFor(obj: DeployedItem.Value) extends Response - final case class Detonate(guid: PlanetSideGUID, obj: PlanetSideGameObject) extends Response - final case class DoorOpens(door_guid: PlanetSideGUID) extends Response - final case class DoorCloses(door_guid: PlanetSideGUID) extends Response - final case class EliminateDeployable( - obj: Deployable, - object_guid: PlanetSideGUID, - pos: Vector3, - deletionEffect: Int - ) extends Response - final case class SendHackMessageHackCleared(target_guid: PlanetSideGUID, unk1: Long, unk2: HackState7) extends Response - final case class HackObject(target_guid: PlanetSideGUID, unk1: Long, unk2: HackState7) extends Response - - final case class SendPacket(packet: PlanetSideGamePacket) extends Response - final case class PlanetsideAttribute(target_guid: PlanetSideGUID, attribute_number: PlanetsideAttributeEnum, attribute_value: Long) - extends Response - final case class GenericObjectAction(target_guid: PlanetSideGUID, action_number: GenericObjectActionEnum) - extends Response - final case class ChatMessage(msg: ChatMsg) extends Response - final case class GenericActionMessage(action_num: GenericAction) extends Response - - final case class LluSpawned(llu: CaptureFlag) extends Response - final case class LluDespawned(guid: PlanetSideGUID, position: Vector3) extends Response - - final case class ObjectDelete(item_guid: PlanetSideGUID, unk: Int) extends Response - final case class ProximityTerminalAction(terminal: Terminal with ProximityUnit, target: PlanetSideGameObject) - extends Response - final case class ProximityTerminalEffect(object_guid: PlanetSideGUID, effectState: Boolean) extends Response - final case class RouterTelepadMessage(msg: String) extends Response - final case class RouterTelepadTransport( - passenger_guid: PlanetSideGUID, - src_guid: PlanetSideGUID, - dest_guid: PlanetSideGUID - ) extends Response - final case class SendResponse(pkt: PlanetSideGamePacket) extends Response - final case class SetEmpire(object_guid: PlanetSideGUID, empire: PlanetSideEmpire.Value) extends Response - final case class ShuttleDock(pad_guid: PlanetSideGUID, shuttle_guid: PlanetSideGUID, toSlot: Int) extends Response - final case class ShuttleUndock( - pad_guid: PlanetSideGUID, - shuttle_guid: PlanetSideGUID, - pos: Vector3, orient: Vector3 - ) extends Response - final case class ShuttleEvent(ev: OrbitalShuttleEvent) extends Response - final case class ShuttleState(guid: PlanetSideGUID, pos: Vector3, orientation: Vector3, state: Int) extends Response - final case class StartRouterInternalTelepad( - router_guid: PlanetSideGUID, - obj_guid: PlanetSideGUID, - obj: Utility.InternalTelepad - ) extends Response - final case class ToggleTeleportSystem( - router: Vehicle, - systemPlan: Option[(Utility.InternalTelepad, TelepadDeployable)] - ) extends Response - final case class TriggerEffect( - target: PlanetSideGUID, - effect: String, - effectInfo: Option[TriggeredEffect] = None, - triggeredLocation: Option[TriggeredEffectLocation] = None - ) extends Response - final case class TriggerSound(sound: TriggeredSound.Value, pos: Vector3, unk: Int, volume: Float) extends Response - final case class UpdateForceDomeStatus(building_guid: PlanetSideGUID, activated: Boolean) extends Response - final case class RechargeVehicleWeapon(mountable_guid: PlanetSideGUID, weapon_guid: PlanetSideGUID) extends Response - final case class ForceZoneChange(zone: Zone) extends Response -} 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 980a0f756..38616a0ea 100644 --- a/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala +++ b/src/main/scala/net/psforever/services/local/support/CaptureFlagManager.scala @@ -1,7 +1,7 @@ // Copyright (c) 2021 PSForever package net.psforever.services.local.support -import akka.actor.{Actor, ActorRef, Cancellable} +import akka.actor.{Actor, ActorContext, ActorRef, Cancellable, Props} import net.psforever.login.WorldSession import net.psforever.objects.{Default, PlanetSideGameObject, Player} import net.psforever.objects.guid.{GUIDTask, TaskWorkflow} @@ -13,14 +13,30 @@ import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal import net.psforever.objects.zones.Zone import net.psforever.objects.zones.interaction.InteractsWithZone import net.psforever.packet.game._ -import net.psforever.services.{Service, ServiceManager} +import net.psforever.services.ServiceManager import net.psforever.services.ServiceManager.{Lookup, LookupResult} -import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.{EventServiceSupport, GenericSupportEnvelopeOnly} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{GenericObjectAction, SendResponse} +import net.psforever.services.galaxy.GalaxyAction +import net.psforever.services.local.LocalAction import net.psforever.types.{ChatMessageType, PlanetSideEmpire, PlanetSideGUID, Vector3} import scala.concurrent.duration.DurationInt +case class CaptureFlagSupport(zone: Zone) + extends EventServiceSupport { + def label: String = "captureFlagManager" + def constructor(context: ActorContext): ActorRef = { + context.actorOf(Props(classOf[CaptureFlagManager], zone), name = "CaptureFlagManager") + } +} + +final case class FlagEnvelope(supportMessage: CaptureFlagManager.Command) + extends GenericSupportEnvelopeOnly { + def supportLabel: String = "captureFlagManager" +} + /** * Responsible for handling capture flag related lifecycles */ @@ -45,12 +61,11 @@ class CaptureFlagManager(zone: Zone) extends Actor { case CaptureFlagManager.SpawnCaptureFlag(capture_terminal, target, hackingFaction) => val socket = capture_terminal.Owner.asInstanceOf[Building].GetFlagSocket.get // Override CC message when looked at - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.SendGenericObjectActionMessage( - PlanetSideGUID(-1), + GenericObjectAction( capture_terminal.GUID, - GenericObjectActionEnum.FlagSpawned + GenericObjectActionEnum.FlagSpawned.id ) ) // Register LLU object create task and callback to create on clients @@ -67,9 +82,9 @@ class CaptureFlagManager(zone: Zone) extends Actor { TaskWorkflow.execute(WorldSession.CallBackForTask( GUIDTask.registerObject(zone.GUID, flag), zone.LocalEvents, - LocalServiceMessage( + MessageEnvelope( zone.id, - LocalAction.LluSpawned(Service.defaultPlayerGUID, flag) + LocalAction.LluSpawned(flag) ) )) // Broadcast chat message for LLU spawn @@ -82,7 +97,11 @@ class CaptureFlagManager(zone: Zone) extends Actor { case None => "A soldier" } // Trigger Install sound - zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.TriggerSound(PlanetSideGUID(-1), TriggeredSound.LLUInstall, flag.Target.CaptureTerminal.get.Position, 20, 0.8000001f)) + zone.LocalEvents ! MessageEnvelope( + zone.id, + PlanetSideGUID(-1), + LocalAction.TriggerSound(TriggeredSound.LLUInstall, flag.Target.CaptureTerminal.get.Position, 20, 0.8000001f) + ) // Broadcast capture chat message CaptureFlagManager.ChatBroadcast(zone, CaptureFlagChatMessageStrings.CTF_Success(name, flag.Faction, flag.Owner.asInstanceOf[Building].Name)) // Despawn flag @@ -115,8 +134,16 @@ class CaptureFlagManager(zone: Zone) extends Actor { case CaptureFlagManager.PickupFlag(flag: CaptureFlag, player: Player) => flag.Carrier = Some(player) - zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SendPacket(ObjectAttachMessage(player.GUID, flag.GUID, 252))) - zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.TriggerSound(PlanetSideGUID(-1), TriggeredSound.LLUPickup, player.Position, 15, volume = 0.8f)) + zone.LocalEvents ! MessageEnvelope( + zone.id, + PlanetSideGUID(-1), + SendResponse(ObjectAttachMessage(player.GUID, flag.GUID, 252)) + ) + zone.LocalEvents ! MessageEnvelope( + zone.id, + PlanetSideGUID(-1), + LocalAction.TriggerSound(TriggeredSound.LLUPickup, player.Position, 15, volume = 0.8f) + ) CaptureFlagManager.ChatBroadcast( zone, CaptureFlagChatMessageStrings.CTF_FlagPickedUp(player.Name, player.Faction, flag.Owner.asInstanceOf[Building].Name), @@ -132,7 +159,11 @@ class CaptureFlagManager(zone: Zone) extends Actor { // Remove attached player from flag flag.Carrier = None // Send drop packet - zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.SendPacket(ObjectDetachMessage(player.GUID, flag.GUID, player.Position, 0, 0, 0))) + zone.LocalEvents ! MessageEnvelope( + zone.id, + PlanetSideGUID(-1), + SendResponse(ObjectDetachMessage(player.GUID, flag.GUID, player.Position, 0, 0, 0)) + ) // Send dropped chat message CaptureFlagManager.ChatBroadcast( zone, @@ -155,9 +186,9 @@ class CaptureFlagManager(zone: Zone) extends Actor { TaskWorkflow.execute(WorldSession.CallBackForTask( GUIDTask.registerObject(zone.GUID, replacementLlu), zone.LocalEvents, - LocalServiceMessage( + MessageEnvelope( zone.id, - LocalAction.LluSpawned(Service.defaultPlayerGUID, replacementLlu) + LocalAction.LluSpawned(replacementLlu) ) )) case _ => @@ -189,7 +220,7 @@ class CaptureFlagManager(zone: Zone) extends Actor { is_monolith_unit = false ) } - galaxyService ! GalaxyServiceMessage(GalaxyAction.FlagMapUpdate(CaptureFlagUpdateMessage(zone.Number, flagInfo))) + galaxyService ! MessageEnvelope("", GalaxyAction.FlagMapUpdate(CaptureFlagUpdateMessage(zone.Number, flagInfo))) } private def TrackFlag(flag: CaptureFlag): Unit = { @@ -216,7 +247,7 @@ class CaptureFlagManager(zone: Zone) extends Actor { flag.Owner.asInstanceOf[Building].GetFlagSocket.get.captureFlag = None UntrackFlag(flag) // Unregister LLU from clients, - zone.LocalEvents ! LocalServiceMessage(zone.id, LocalAction.LluDespawned(PlanetSideGUID(-1), flag.GUID, flag.Position)) + zone.LocalEvents ! MessageEnvelope(zone.id, PlanetSideGUID(-1), LocalAction.LluDespawned(flag.GUID, flag.Position)) // Then unregister it from the GUID pool TaskWorkflow.execute(GUIDTask.unregisterObject(zone.GUID, flag)) } @@ -246,12 +277,10 @@ object CaptureFlagManager { } else { ChatMessageType.UNK_229 } - zone.LocalEvents ! LocalServiceMessage( + zone.LocalEvents ! MessageEnvelope( zone.id, - LocalAction.SendChatMsg( - PlanetSideGUID(-1), - ChatMsg(messageType, wideContents = true, "", message, None) - ) + PlanetSideGUID(-1), + SendResponse(ChatMsg(messageType, wideContents = true, "", message, None)) ) } @@ -288,7 +317,7 @@ object CaptureFlagManager { if LoseFlagViolentlyToEnvironment(target, Set(EnvironmentAttribute.Water, EnvironmentAttribute.Lava, EnvironmentAttribute.Death)) /*|| LoseFlagViolentlyToWarpGateEnvelope(zone, target)*/ => flag.Destroyed = true - zone.LocalEvents ! LocalServiceMessage("", LocalAction.LluLost(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 115924267..e3ca9a6e4 100644 --- a/src/main/scala/net/psforever/services/local/support/DoorCloseActor.scala +++ b/src/main/scala/net/psforever/services/local/support/DoorCloseActor.scala @@ -1,15 +1,36 @@ // Copyright (c) 2017 PSForever package net.psforever.services.local.support -import akka.actor.{Actor, Cancellable} +import akka.actor.{Actor, ActorContext, ActorRef, Cancellable, Props} import net.psforever.objects.{Default, Doors} import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.zones.Zone +import net.psforever.services.base.{EventServiceSupport, GenericSupportEnvelope} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.local.LocalAction.IsADoorMessage +import net.psforever.services.local.LocalAction import net.psforever.types.PlanetSideGUID import scala.annotation.tailrec import scala.concurrent.duration._ +case object DoorCloserSupport + extends EventServiceSupport { + def label: String = "doorCloser" + def constructor(context: ActorContext): ActorRef = { + context.actorOf(Props[DoorCloseActor](), name = "DoorCloser") + } +} + +final case class DoorMessage( + channel: String, + msg: IsADoorMessage, + supportMessage: Any + ) extends GenericSupportEnvelope { + def filter: PlanetSideGUID = Default.GUID0 + 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. @@ -43,11 +64,13 @@ class DoorCloseActor() extends Actor { doorsLeftOpen1 ++ doorsLeftOpen2.map(entry => DoorCloseActor.DoorEntry(entry.door, entry.zone, now)) ).sortBy(_.time) - doorsToClose2.foreach(entry => { - entry.door.Open = None //permissible break from synchronization - context.parent ! DoorCloseActor.CloseTheDoor(entry.door.GUID, entry.zone.id) //call up to the main event system - }) - + doorsToClose2 + .map { case DoorCloseActor.DoorEntry(door, zone, _) => + door.Open = None //permissible break from synchronization + (zone, MessageEnvelope(zone.id, LocalAction.DoorCloses(door.GUID))) //call up to the main event system + } + .groupBy(_._1) + .foreach { case (zone, list) => zone.LocalEvents ! BundledEnvelope(list.map(_._2)) } if (openDoors.nonEmpty) { val short_timeout: FiniteDuration = math.max(1, DoorCloseActor.timeout_time - (now - openDoors.head.time)).milliseconds import scala.concurrent.ExecutionContext.Implicits.global 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 ffe816566..dc579b915 100644 --- a/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala +++ b/src/main/scala/net/psforever/services/local/support/HackCaptureActor.scala @@ -1,7 +1,7 @@ // Copyright (c) 2021 PSForever package net.psforever.services.local.support -import akka.actor.{Actor, Cancellable} +import akka.actor.{Actor, ActorContext, ActorRef, Cancellable, Props} import net.psforever.actors.zone.{BuildingActor, ZoneActor} import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.hackable.Hackable @@ -13,16 +13,32 @@ 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.Service +import net.psforever.services.base.{EventServiceSupport, GenericSupportEnvelopeOnly} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.message.PlanetsideAttribute import net.psforever.services.local.support.HackCaptureActor.GetHackingFaction -import net.psforever.services.local.{LocalAction, LocalServiceMessage} -import net.psforever.types.{ChatMessageType, PlanetSideEmpire, PlanetSideGUID} +import net.psforever.services.local.LocalAction +import net.psforever.types.{ChatMessageType, PlanetSideEmpire} import java.util.concurrent.{Executors, TimeUnit} import scala.collection.Seq +import scala.collection.mutable.ArrayBuffer import scala.concurrent.duration.{FiniteDuration, _} import scala.util.Random +case object HackCaptureSupport + extends EventServiceSupport { + def label: String = "hackCapturer" + def constructor(context: ActorContext): ActorRef = { + context.actorOf(Props[HackCaptureActor](), name = "HackCapturer") + } +} + +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. */ @@ -156,24 +172,19 @@ class HackCaptureActor extends Actor { case HackCaptureActor.FlagLost(flag) => val owner = flag.Owner.asInstanceOf[Building] val guid = owner.GUID - val terminalOpt = owner.CaptureTerminal - hackedObjects - .find(entry => guid == entry.target.Owner.GUID) - .collect { entry => - val terminal = terminalOpt.get - hackedObjects = hackedObjects.filterNot(x => x eq entry) - log.info(s"FlagLost: ${flag.Carrier.map(_.Name).getOrElse("")} the flag carrier screwed up the capture for ${flag.Target.Name} and the LLU has been lost") - terminal.Actor ! CommonMessages.ClearHack() - NotifyHackStateChange(terminal, isResecured = true) - // If there's hacked objects left in the list restart the timer with the shortest hack time left - RestartTimer() - entry - } - .orElse{ - log.warn(s"FlagLost: flag data does not match to an entry in the hacked objects list") - None - } - context.parent ! CaptureFlagManager.Lost(flag, CaptureFlagLostReasonEnum.FlagLost) + val (found, remaining) = hackedObjects.partition(_.target.Owner.GUID == guid) + hackedObjects = remaining + found.collectFirst { _ => + val terminal = owner.CaptureTerminal.get + log.info(s"FlagLost: ${flag.Carrier.map(_.Name).getOrElse("")} the flag carrier screwed up the capture for ${flag.Target.Name} and the LLU has been lost") + terminal.Actor ! CommonMessages.ClearHack() + NotifyHackStateChange(terminal, isResecured = true) + RestartTimer() // If there's hacked objects left in the list restart the timer with the shortest hack time left + } + if (found.isEmpty) { + log.warn(s"FlagLost: flag data does not match to an entry in the hacked objects list") + } + owner.Zone.LocalEvents ! FlagEnvelope(CaptureFlagManager.Lost(flag, CaptureFlagLostReasonEnum.FlagLost)) case _ => () } @@ -195,7 +206,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 ! CaptureFlagManager.Lost(flag, CaptureFlagLostReasonEnum.Ended) + terminal.Zone.LocalEvents ! FlagEnvelope(CaptureFlagManager.Lost(flag, CaptureFlagLostReasonEnum.Ended)) NotifyHackStateChange(terminal, isResecured = false) RestartTimer() spawnCaptureFlag(neighbours, terminal, hackingFaction) @@ -209,7 +220,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 ! 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") @@ -225,7 +236,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 ! CaptureFlagManager.SpawnCaptureFlag(terminal, targetBase, hackingFaction) + terminal.Zone.LocalEvents ! FlagEnvelope(CaptureFlagManager.SpawnCaptureFlag(terminal, targetBase, hackingFaction)) } private def NotifyHackStateChange( @@ -234,12 +245,11 @@ class HackCaptureActor extends Actor { ): Unit = { val attributeValue = HackCaptureActor.GetHackUpdateAttributeValue(terminal, isResecured) // Notify all clients that CC has had its hack state changed - terminal.Zone.LocalEvents ! LocalServiceMessage( + terminal.Zone.LocalEvents ! MessageEnvelope( terminal.Zone.id, - LocalAction.SendPlanetsideAttributeMessage( - PlanetSideGUID(-1), + PlanetsideAttribute( terminal.GUID, - PlanetsideAttributeEnum.ControlConsoleHackUpdate, + PlanetsideAttributeEnum.ControlConsoleHackUpdate.id, attributeValue ) ) @@ -267,18 +277,18 @@ class HackCaptureActor extends Actor { private def HackCompleted(terminal: CaptureTerminal with Hackable, hackedByFaction: PlanetSideEmpire.Value): Unit = { val building = terminal.Owner.asInstanceOf[Building] + val zone = building.Zone + val events = zone.LocalEvents + val messages: ArrayBuffer[MessageEnvelope] = ArrayBuffer() if (building.NtuLevel > 0) { building.virusId = 8 building.virusInstalledBy = None log.info(s"Setting base ${building.GUID} / MapId: ${building.MapId} as owned by $hackedByFaction") //dispatch to players aligned with the capturing faction within the SOI - val events = building.Zone.LocalEvents - val msg = LocalAction.SendGenericActionMessage(Service.defaultPlayerGUID, GenericAction.FacilityCaptureFanfare) - building - .PlayersInSOI - .collect { case p if p.Faction == hackedByFaction => - events ! LocalServiceMessage(p.Name, msg) - } + val msg = LocalAction.GenericActionMessage(GenericAction.FacilityCaptureFanfare) + messages.appendAll(building.PlayersInSOI.collect { case p if p.Faction == hackedByFaction => + MessageEnvelope(p.Name, msg) + }) val buildings = building.Zone.Buildings.values val hackedBaseId = building.GUID val facilities = if (building.Zone.id.startsWith("c")) { @@ -312,7 +322,8 @@ class HackCaptureActor extends Actor { } NotifyHackStateChange(terminal, isResecured = true) // todo: this appears to be the way to reset the base warning lights after the hack finishes but it doesn't seem to work. - context.parent ! HackClearActor.SendHackMessageHackCleared(building.GUID, terminal.Zone.id, 3212836864L, HackState7.Unk8) //call up + messages.append(MessageEnvelope(zone.id, LocalAction.HackClear(building.GUID, 3212836864L, HackState7.Unk8))) + events ! BundledEnvelope(messages) } private def RestartTimer(): Unit = { 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 c93ae2696..429cb59c3 100644 --- a/src/main/scala/net/psforever/services/local/support/HackClearActor.scala +++ b/src/main/scala/net/psforever/services/local/support/HackClearActor.scala @@ -2,17 +2,44 @@ package net.psforever.services.local.support import java.util.concurrent.TimeUnit -import akka.actor.{Actor, Cancellable} +import akka.actor.{Actor, ActorContext, ActorRef, Cancellable, Props} import net.psforever.objects.{Default, GlobalDefinitions} 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.envelope.{BundledEnvelope, MessageEnvelope} +import net.psforever.services.base.{EventServiceSupport, GenericSupportEnvelope, GenericSupportEnvelopeOnly} +import net.psforever.services.base.message.GenericObjectAction +import net.psforever.services.local.LocalAction.IsAHackMessage +import net.psforever.services.local.LocalAction import net.psforever.types.PlanetSideGUID import scala.annotation.tailrec import scala.concurrent.duration._ +case object HackClearSupport + extends EventServiceSupport { + def label: String = "hackClearer" + def constructor(context: ActorContext): ActorRef = { + context.actorOf(Props[HackClearActor](), name = "HackClearer") + } +} + +final case class HackEntityEnvelope( + channel: 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. @@ -45,32 +72,27 @@ class HackClearActor() extends Actor { //TODO we can just walk across the list of doors and extract only the first few entries val (unhackObjects, stillHackedObjects) = PartitionEntries(hackedObjects, now) hackedObjects = stillHackedObjects - unhackObjects.foreach(entry => { - entry.target.Actor ! CommonMessages.ClearHack() - context.parent ! HackClearActor.SendHackMessageHackCleared( - entry.target.GUID, - entry.zone.id, - entry.unk1, - entry.unk2 - ) //call up to the main event system - if (entry.target.Definition == GlobalDefinitions.main_terminal) { - ClearVirusFromBuilding(entry.target) + unhackObjects + .map { case HackClearActor.HackEntry(target, zone, unk1, unk2, _, _) => + target.Actor ! CommonMessages.ClearHack() + if (target.Definition == GlobalDefinitions.main_terminal) { + ClearVirusFromBuilding(target) + } + (zone, MessageEnvelope(zone.id, LocalAction.HackClear(target.GUID, unk1, unk2))) + } + .groupBy(_._1) + .foreach { case (zone, list) => + zone.LocalEvents ! BundledEnvelope(list.map(_._2)) } - }) RestartTimer() case HackClearActor.ObjectIsResecured(target) => hackedObjects.find { _.target == target } match { - case Some(entry: HackClearActor.HackEntry) => + case Some(HackClearActor.HackEntry(target, zone, unk1, unk2, _, _)) => hackedObjects = hackedObjects.filterNot(x => x.target == target) - entry.target.Actor ! CommonMessages.ClearHack() - context.parent ! HackClearActor.SendHackMessageHackCleared( - entry.target.GUID, - entry.zone.id, - entry.unk1, - entry.unk2 - ) //call up to the main event system + target.Actor ! CommonMessages.ClearHack() + zone.LocalEvents ! MessageEnvelope(zone.id, LocalAction.HackClear(target.GUID, 3212836864L, HackState7.Unk8)) // Restart the timer in case the object we just removed was the next one scheduled RestartTimer() @@ -109,17 +131,13 @@ class HackClearActor() extends Actor { import net.psforever.objects.serverobject.structures.Building import net.psforever.objects.serverobject.terminals.Terminal import net.psforever.actors.zone.BuildingActor - import net.psforever.services.Service - import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} val building = target.asInstanceOf[Terminal].Owner.asInstanceOf[Building] building.virusId = 8 building.virusInstalledBy = None - val msg = AvatarAction.GenericObjectAction(Service.defaultPlayerGUID, target.GUID, 60) + val msg = GenericObjectAction(target.GUID, 60) val events = building.Zone.AvatarEvents - building.PlayersInSOI.foreach { player => - events ! AvatarServiceMessage(player.Name, msg) - } + events ! BundledEnvelope(building.PlayersInSOI.map { player => MessageEnvelope(player.Name, msg) }) building.Actor ! BuildingActor.MapUpdate() } diff --git a/src/main/scala/net/psforever/services/properties/PropertyOverrideManager.scala b/src/main/scala/net/psforever/services/properties/PropertyOverrideManager.scala index 9af42b969..0a7888ae4 100644 --- a/src/main/scala/net/psforever/services/properties/PropertyOverrideManager.scala +++ b/src/main/scala/net/psforever/services/properties/PropertyOverrideManager.scala @@ -5,6 +5,7 @@ import net.psforever.packet.game.{GamePropertyTarget, PropertyOverrideMessage} import net.psforever.packet.game.PropertyOverrideMessage.GamePropertyScope import net.psforever.packet.game.objectcreate.ObjectClass import net.psforever.zones.Zones + import scala.collection.mutable.ListBuffer class PropertyOverrideManager extends Actor { @@ -12,11 +13,10 @@ class PropertyOverrideManager extends Actor { private var overrides: Map[Int, Map[String, List[(String, String)]]] = Map() private var gamePropertyScopes: List[PropertyOverrideMessage.GamePropertyScope] = List() - lazy private val zoneIds: Iterable[Int] = Zones.zones.map(_.Number) override def preStart(): Unit = { LoadOverridesFromFile(zoneId = 0) // Global overrides - for (zoneId <- zoneIds) { + for (zoneId <- Zones.zones.map(_.Number)) { LoadOverridesFromFile(zoneId) } ProcessGamePropertyScopes() @@ -26,7 +26,7 @@ class PropertyOverrideManager extends Actor { case PropertyOverrideManager.GetOverridesMessage => sender() ! gamePropertyScopes - case _ => ; + case _ => () } private def LoadOverridesFromFile(zoneId: Int): Unit = { @@ -37,11 +37,11 @@ class PropertyOverrideManager extends Actor { } if (zoneOverrides == null) { log.debug(s"PropertyOverride: no overrides found for zone $zoneId using filename game_objects$zoneId.adb.lst") - return + } else { + val grouped = zoneOverrides.groupBy(_._1).view.mapValues(_.map(x => (x._2, x._3)).toList).toMap + log.debug(s"PropertyOverride: loaded property overrides for zone $zoneId: ${grouped.toString}") + overrides += (zoneId -> grouped) } - val grouped = zoneOverrides.groupBy(_._1).view.mapValues(_.map(x => (x._2, x._3)).toList).toMap - log.debug(s"PropertyOverride: loaded property overrides for zone $zoneId: ${grouped.toString}") - overrides += (zoneId -> grouped) } private def ProcessGamePropertyScopes(): Unit = { @@ -61,28 +61,26 @@ class PropertyOverrideManager extends Actor { gamePropertyScopes = scopesBuffer.toList } - def LoadFile(path: String): ListBuffer[(String, String, String)] = { + def LoadFile(path: String): List[(String, String, String)] = { val stream = getClass.getClassLoader.getResourceAsStream(path) if (stream == null) { - return null - } - val content = scala.io.Source.fromInputStream(stream).getLines().filter(x => x.startsWith("add_property")) - val data: ListBuffer[(String, String, String)] = ListBuffer() - for (line <- content) { - val splitLine = line.split(" ") - if (splitLine.length >= 3) { - val objectName = splitLine(1) - val property = splitLine(2) - var propertyValue = "" - for (i <- 3 until splitLine.length) { - propertyValue += splitLine(i) + " " + List.empty[(String, String, String)] + } else { + val content = scala.io.Source.fromInputStream(stream).getLines() + val data = content + .filter(_.startsWith("add_property")) + .map { line => (line, line.split("\\s+")) } + .filter(_._2.length > 2) //n >= 3 + .map { case (line, tokens) => + val objectName = tokens(1) + val property = tokens(2) + val propertyValue = line.drop(objectName.length + property.length + 15) //"add_property" (12) + spaces (3) + (objectName, property, propertyValue) } - propertyValue = propertyValue.trim - data += ((objectName, property, propertyValue)) - } + .toList + stream.close() + data } - stream.close() - data } } diff --git a/src/main/scala/net/psforever/services/teamwork/SquadService.scala b/src/main/scala/net/psforever/services/teamwork/SquadService.scala index 926dba54e..fcac21e2c 100644 --- a/src/main/scala/net/psforever/services/teamwork/SquadService.scala +++ b/src/main/scala/net/psforever/services/teamwork/SquadService.scala @@ -176,20 +176,20 @@ class SquadService extends Actor { def receive: Receive = { //subscribe to a faction's channel - necessary to receive updates about listed squads - case Service.Join(faction) if SquadService.FactionWordSalad.indexOf(faction) > -1 => + case Service.Join(faction, _) if SquadService.FactionWordSalad.indexOf(faction) > -1 => JoinByFaction(faction, sender()) //subscribe to the player's personal channel - necessary for future and previous squad information - case Service.Join(char_id) => + case Service.Join(char_id, _) => JoinByCharacterId(char_id, sender()) - case Service.Leave(Some(faction)) if SquadService.FactionWordSalad.indexOf(faction) > -1 => + case Service.Leave(faction) if SquadService.FactionWordSalad.indexOf(faction) > -1 => LeaveByFaction(faction, sender()) - case Service.Leave(Some(char_id)) => + case Service.Leave(char_id) => LeaveByCharacterId(char_id, sender()) - case Service.Leave(None) | Service.LeaveAll() => + case Service.LeaveAll => LeaveInGeneral(sender()) case Terminated(actorRef) => diff --git a/src/main/scala/net/psforever/services/teamwork/SquadServiceMessage.scala b/src/main/scala/net/psforever/services/teamwork/SquadServiceMessage.scala index f99942df6..8cda11648 100644 --- a/src/main/scala/net/psforever/services/teamwork/SquadServiceMessage.scala +++ b/src/main/scala/net/psforever/services/teamwork/SquadServiceMessage.scala @@ -5,6 +5,7 @@ import net.psforever.objects.Player import net.psforever.objects.avatar.Certification import net.psforever.objects.zones.Zone import net.psforever.packet.game.{WaypointEventAction, WaypointInfo, SquadAction => PacketSquadAction} +import net.psforever.services.base.message.{EventMessage, EventResponse} import net.psforever.types.{PlanetSideGUID, SquadRequestType, SquadWaypoint, Vector3} final case class SquadServiceMessage(tplayer: Player, zone: Zone, actionMessage: Any) @@ -15,7 +16,9 @@ object SquadServiceMessage { } object SquadAction { - sealed trait Action + sealed trait Action extends EventMessage { + def response(): EventResponse = null + } final case class InitSquadList() extends Action final case class InitCharId() extends Action diff --git a/src/main/scala/net/psforever/services/teamwork/SquadServiceResponse.scala b/src/main/scala/net/psforever/services/teamwork/SquadServiceResponse.scala index bd2753292..a5281453c 100644 --- a/src/main/scala/net/psforever/services/teamwork/SquadServiceResponse.scala +++ b/src/main/scala/net/psforever/services/teamwork/SquadServiceResponse.scala @@ -2,14 +2,23 @@ package net.psforever.services.teamwork import akka.actor.ActorRef +import net.psforever.objects.Default import net.psforever.objects.avatar.Certification import net.psforever.objects.teamwork.Squad import net.psforever.packet.game.{SquadDetail, SquadInfo, WaypointEventAction, WaypointInfo} +import net.psforever.services.base.message.EventResponse import net.psforever.types.{ChatMessageType, PlanetSideGUID, SquadResponseType, SquadWaypoint} -import net.psforever.services.GenericEventBusMsg +import net.psforever.services.base.EventSystemStamp +import net.psforever.services.base.envelope.GenericResponseEnvelope -final case class SquadServiceResponse(channel: String, exclude: Iterable[Long], response: SquadResponse.Response) - extends GenericEventBusMsg +case object SquadStamp extends EventSystemStamp + +final case class SquadServiceResponse(channel: String, exclude: Iterable[Long], reply: SquadResponse.Response) + extends EventResponse with GenericResponseEnvelope { + def filter: PlanetSideGUID = Default.GUID0 + + def stamp: EventSystemStamp = SquadStamp +} object SquadServiceResponse { def apply(toChannel: String, response: SquadResponse.Response): SquadServiceResponse = @@ -20,7 +29,7 @@ object SquadServiceResponse { } object SquadResponse { - sealed trait Response + sealed trait Response extends EventResponse final case class ListSquadFavorite(line: Int, task: String) extends Response diff --git a/src/main/scala/net/psforever/services/teamwork/SquadSubscriptionEntity.scala b/src/main/scala/net/psforever/services/teamwork/SquadSubscriptionEntity.scala index 104bd5320..fe0537992 100644 --- a/src/main/scala/net/psforever/services/teamwork/SquadSubscriptionEntity.scala +++ b/src/main/scala/net/psforever/services/teamwork/SquadSubscriptionEntity.scala @@ -2,11 +2,11 @@ package net.psforever.services.teamwork import akka.actor.ActorRef -import scala.collection.mutable +import scala.collection.mutable import net.psforever.objects.teamwork.{Squad, SquadFeatures} import net.psforever.packet.game.SquadDetail -import net.psforever.services.GenericEventBus +import net.psforever.services.base.bus.GenericEventBus import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID} class SquadSubscriptionEntity { @@ -28,7 +28,7 @@ class SquadSubscriptionEntity { * @see `Service.Join` * @see `Service.Leave` */ - val SquadEvents = new GenericEventBus[SquadServiceResponse] + val SquadEvents = new GenericEventBus /** * This collection contains the message-sending contact reference for individuals. diff --git a/src/main/scala/net/psforever/services/vehicle/VehicleAction.scala b/src/main/scala/net/psforever/services/vehicle/VehicleAction.scala new file mode 100644 index 000000000..dad025f86 --- /dev/null +++ b/src/main/scala/net/psforever/services/vehicle/VehicleAction.scala @@ -0,0 +1,143 @@ +// Copyright (c) 2017-2026 PSForever +package net.psforever.services.vehicle + +import net.psforever.objects.{PlanetSideGameObject, Vehicle} +import net.psforever.objects.equipment.Equipment +import net.psforever.objects.inventory.InventoryItem +import net.psforever.objects.serverobject.tube.SpawnTube +import net.psforever.objects.zones.Zone +import net.psforever.packet.game.ObjectCreateMessage +import net.psforever.packet.game.objectcreate.{ConstructorData, ObjectCreateMessageParent} +import net.psforever.services.base.message.{EventMessage, EventResponse, SelfRespondingEvent} +import net.psforever.types.{BailType, DriveState, PlanetSideGUID, Vector3} + +object VehicleAction { + final case class ChildObjectState(object_guid: PlanetSideGUID, pitch: Float, yaw: Float) extends SelfRespondingEvent + + final case class DeployRequest( + object_guid: PlanetSideGUID, + state: DriveState.Value, + unk1: Int, + unk2: Boolean, + pos: Vector3 + ) extends SelfRespondingEvent + + final case class DismountVehicle(bailType: BailType.Value, unk2: Boolean) extends SelfRespondingEvent + + final case class EquipmentCreatedInSlot(pkt: ObjectCreateMessage) extends EventResponse + + final case class EquipmentInSlot(target_guid: PlanetSideGUID, slot: Int, equipment: Equipment) extends EventMessage { + override def response(): EventResponse = { + val definition = equipment.Definition + val pkt = ObjectCreateMessage( + definition.ObjectId, + equipment.GUID, + ObjectCreateMessageParent(target_guid, slot), + definition.Packet.ConstructorData(equipment).get + ) + VehicleAction.EquipmentCreatedInSlot(pkt) + } + } + + final case class FrameVehicleState( + vehicle_guid: PlanetSideGUID, + unk1: Int, + pos: Vector3, + orient: Vector3, + vel: Option[Vector3], + unk2: Boolean, + unk3: Int, + unk4: Int, + is_crouched: Boolean, + unk6: Boolean, + unk7: Boolean, + unk8: Int, + unk9: Long, + unkA: Long + ) extends SelfRespondingEvent + + final case class InventoryState( + obj: PlanetSideGameObject, + parent_guid: PlanetSideGUID, + start: Int, + con_data: ConstructorData + ) extends SelfRespondingEvent + + final case class InventoryState2(obj_guid: PlanetSideGUID, parent_guid: PlanetSideGUID, value: Int) extends SelfRespondingEvent + + final case class KickPassenger(unk1: Int, unk2: Boolean, vehicle_guid: PlanetSideGUID) extends SelfRespondingEvent + + final case class LoadVehicle( + vehicle: Vehicle, + vtype: Int, + vguid: PlanetSideGUID, + vdata: ConstructorData + ) extends SelfRespondingEvent + + final case class MountVehicle(object_guid: PlanetSideGUID, seat: Int) extends SelfRespondingEvent + + final case class Ownership(vehicle_guid: PlanetSideGUID) extends SelfRespondingEvent + + final case class LoseOwnership(owner_guid: PlanetSideGUID, vehicle_guid: PlanetSideGUID) extends SelfRespondingEvent + + final case class SeatPermissions(vehicle_guid: PlanetSideGUID, seat_group: Int, permission: Long) extends SelfRespondingEvent + + final case class StowCreatedEquipment( + vehicle_guid: PlanetSideGUID, + slot: Int, + itype: Int, + iguid: PlanetSideGUID, + idata: ConstructorData + ) extends EventResponse + + final case class StowEquipment(vehicle_guid: PlanetSideGUID, slot: Int, item: Equipment) extends SelfRespondingEvent + + final case class UnloadVehicle(vehicle: Vehicle, vehicle_guid: PlanetSideGUID) extends SelfRespondingEvent + + final case class UnstowEquipment(item_guid: PlanetSideGUID) extends SelfRespondingEvent + + final case class VehicleState( + vehicle_guid: PlanetSideGUID, + unk1: Int, + pos: Vector3, + ang: Vector3, + vel: Option[Vector3], + unk2: Option[Int], + unk3: Int, + unk4: Int, + wheel_direction: Int, + unk5: Boolean, + unk6: Boolean + ) extends SelfRespondingEvent + + final case class UpdateAmsSpawnList(list: List[SpawnTube]) extends EventResponse + + final case class UpdateAmsSpawnPoint(zone: Zone) extends EventMessage { + override def response(): EventResponse = { + VehicleAction.UpdateAmsSpawnList(Zone.AmsSpawnPoints(zone)) + } + } + + final case class AMSDeploymentChange(zone: Zone) extends EventMessage { + override def response(): EventResponse = { + VehicleAction.UpdateAmsSpawnList(Zone.AmsSpawnPoints(zone)) + } + } + + final case class TransferPassengerChannel( + temp_channel: String, + new_channel: String, + vehicle: Vehicle, + vehicle_to_delete: PlanetSideGUID + ) extends SelfRespondingEvent + + final case class KickCargo(cargo: Vehicle, speed: Int, delay: Long) extends SelfRespondingEvent + + final case class ChangeLoadout( + target_guid: PlanetSideGUID, + removed_weapons: List[(Equipment, PlanetSideGUID)], + new_weapons: List[InventoryItem], + old_inventory: List[(Equipment, PlanetSideGUID)], + new_inventory: List[InventoryItem] + ) extends SelfRespondingEvent +} diff --git a/src/main/scala/net/psforever/services/vehicle/VehicleService.scala b/src/main/scala/net/psforever/services/vehicle/VehicleService.scala index 72168faa0..26f130f43 100644 --- a/src/main/scala/net/psforever/services/vehicle/VehicleService.scala +++ b/src/main/scala/net/psforever/services/vehicle/VehicleService.scala @@ -1,442 +1,18 @@ -// Copyright (c) 2017 PSForever +// Copyright (c) 2017-2026 PSForever package net.psforever.services.vehicle -import akka.actor.{Actor, ActorRef, Props} -import net.psforever.objects.serverobject.pad.VehicleSpawnPad -import net.psforever.objects.zones.Zone -import net.psforever.packet.game.ObjectCreateMessage -import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent -import net.psforever.services.vehicle.support.TurretUpgrader -import net.psforever.types.DriveState -import net.psforever.services.{GenericEventBus, Service} +import akka.actor.Props +import net.psforever.services.base.{EventSystemStamp, GenericEventServiceWithCacheAndSupport} +import net.psforever.services.vehicle.support.TurretUpgradeSupport -class VehicleService(zone: Zone) extends Actor { - private val turretUpgrade: ActorRef = context.actorOf(Props[TurretUpgrader](), s"${zone.id}-turret-upgrade-agent") - private[this] val log = org.log4s.getLogger +case object VehicleStamp extends EventSystemStamp - val VehicleEvents = new GenericEventBus[VehicleServiceResponse] - - def receive: Receive = { - case Service.Join(channel) => - val path = s"/$channel/Vehicle" - VehicleEvents.subscribe(sender(), path) - - case Service.Leave(None) => - VehicleEvents.unsubscribe(sender()) - - case Service.Leave(Some(channel)) => - val path = s"/$channel/Vehicle" - VehicleEvents.unsubscribe(sender(), path) - - case Service.LeaveAll() => - VehicleEvents.unsubscribe(sender()) - - case VehicleServiceMessage(forChannel, action) => - action match { - case VehicleAction.ChangeAmmo(player_guid, weapon_guid, weapon_slot, old_ammo_guid, ammo_id, ammo_guid, ammo_data) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.ChangeAmmo(weapon_guid, weapon_slot, old_ammo_guid, ammo_id, ammo_guid, ammo_data) - ) - ) - case VehicleAction.ChangeFireState_Start(player_guid, weapon_guid) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.ChangeFireState_Start(weapon_guid) - ) - ) - case VehicleAction.ChangeFireState_Stop(player_guid, weapon_guid) => - VehicleEvents.publish( - VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.ChangeFireState_Stop(weapon_guid)) - ) - case VehicleAction.ChildObjectState(player_guid, object_guid, pitch, yaw) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.ChildObjectState(object_guid, pitch, yaw) - ) - ) - case VehicleAction.DeployRequest(player_guid, object_guid, state, unk1, unk2, pos) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.DeployRequest(object_guid, state, unk1, unk2, pos) - ) - ) - case VehicleAction.DismountVehicle(player_guid, bailType, unk2) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.DismountVehicle(bailType, unk2) - ) - ) - case VehicleAction.EquipmentInSlot(player_guid, target_guid, slot, equipment) => - val definition = equipment.Definition - val pkt = ObjectCreateMessage( - definition.ObjectId, - equipment.GUID, - ObjectCreateMessageParent(target_guid, slot), - definition.Packet.ConstructorData(equipment).get - ) - ObjectCreateMessageParent(target_guid, slot) - VehicleEvents.publish( - VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.EquipmentInSlot(pkt)) - ) - case VehicleAction.FrameVehicleState( - player_guid, - vehicle_guid, - unk1, - pos, - orient, - vel, - unk2, - unk3, - unk4, - is_crouched, - unk6, - unk7, - unk8, - unk9, - unkA - ) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.FrameVehicleState( - vehicle_guid, - unk1, - pos, - orient, - vel, - unk2, - unk3, - unk4, - is_crouched, - unk6, - unk7, - unk8, - unk9, - unkA - ) - ) - ) - case VehicleAction.GenericObjectAction(player_guid, guid, code) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.GenericObjectAction(guid, code) - ) - ) - case VehicleAction.InventoryState(player_guid, obj, parent_guid, start, con_data) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.InventoryState(obj, parent_guid, start, con_data) - ) - ) - case VehicleAction.InventoryState2(player_guid, obj_guid, parent_guid, value) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.InventoryState2(obj_guid, parent_guid, value) - ) - ) - case VehicleAction.KickPassenger(player_guid, seat_num, kickedByDriver, vehicle_guid) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.KickPassenger(seat_num, kickedByDriver, vehicle_guid) - ) - ) - case VehicleAction.ObjectDelete(guid) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - Service.defaultPlayerGUID, - VehicleResponse.ObjectDelete(guid) - ) - ) - case VehicleAction.LoadVehicle(player_guid, vehicle, vtype, vguid, vdata) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.LoadVehicle(vehicle, vtype, vguid, vdata) - ) - ) - case VehicleAction.MountVehicle(player_guid, vehicle_guid, seat) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.MountVehicle(vehicle_guid, seat) - ) - ) - case VehicleAction.LoseOwnership(owner_guid, vehicle_guid) => - VehicleEvents.publish( - VehicleServiceResponse(s"/$forChannel/Vehicle", Service.defaultPlayerGUID, VehicleResponse.LoseOwnership(owner_guid, vehicle_guid)) - ) - case VehicleAction.Ownership(player_guid, vehicle_guid) => - VehicleEvents.publish( - VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.Ownership(vehicle_guid)) - ) - case VehicleAction.PlanetsideAttribute(exclude_guid, target_guid, attribute_type, attribute_value) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - exclude_guid, - VehicleResponse.PlanetsideAttribute(target_guid, attribute_type, attribute_value) - ) - ) - - case VehicleAction.Reload(player_guid, weapon_guid) => - VehicleEvents.publish( - VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.Reload(weapon_guid)) - ) - case VehicleAction.SeatPermissions(player_guid, vehicle_guid, seat_group, permission) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission) - ) - ) - case VehicleAction.StowEquipment(player_guid, vehicle_guid, slot, item) => - val definition = item.Definition - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.StowEquipment( - vehicle_guid, - slot, - definition.ObjectId, - item.GUID, - definition.Packet.DetailedConstructorData(item).get - ) - ) - ) - case VehicleAction.WeaponDryFire(player_guid, weapon_guid) => - VehicleEvents.publish( - VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.WeaponDryFire(weapon_guid)) - ) - case VehicleAction.UnloadVehicle(player_guid, vehicle, vehicle_guid) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.UnloadVehicle(vehicle, vehicle_guid) - ) - ) - case VehicleAction.UnstowEquipment(player_guid, item_guid) => - VehicleEvents.publish( - VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.UnstowEquipment(item_guid)) - ) - case VehicleAction.VehicleState( - player_guid, - vehicle_guid, - unk1, - pos, - ang, - vel, - unk2, - unk3, - unk4, - wheel_direction, - unk5, - unk6 - ) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.VehicleState( - vehicle_guid, - unk1, - pos, - ang, - vel, - unk2, - unk3, - unk4, - wheel_direction, - unk5, - unk6 - ) - ) - ) - case VehicleAction.SendResponse(player_guid, msg) => - VehicleEvents.publish( - VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.SendResponse(msg)) - ) - - //unlike other messages, just return to sender, don't publish - case VehicleAction.UpdateAmsSpawnPoint(zone: Zone) => - sender() ! VehicleServiceResponse( - s"/$forChannel/Vehicle", - Service.defaultPlayerGUID, - VehicleResponse.UpdateAmsSpawnPoint(AmsSpawnPoints(zone)) - ) - - case VehicleAction.TransferPassengerChannel( - player_guid, - old_channel, - temp_channel, - vehicle, - vehicle_to_delete - ) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - player_guid, - VehicleResponse.TransferPassengerChannel(old_channel, temp_channel, vehicle, vehicle_to_delete) - ) - ) - case VehicleAction.KickCargo(player_guid, cargo, speed, delay) => - VehicleEvents.publish( - VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.KickCargo(cargo, speed, delay)) - ) - - case VehicleAction.ChangeLoadout(target_guid, removed_weapons, new_weapons, old_inventory, new_inventory) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$forChannel/Vehicle", - Service.defaultPlayerGUID, - VehicleResponse.ChangeLoadout(target_guid, removed_weapons, new_weapons, old_inventory, new_inventory) - ) - ) - case _ => ; - } - - //message to TurretUpgrader - case VehicleServiceMessage.TurretUpgrade(msg) => - turretUpgrade forward msg - - //from VehicleSpawnControl, etc. - case VehicleSpawnPad.ConcealPlayer(player_guid) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/${zone.id}/Vehicle", - Service.defaultPlayerGUID, - VehicleResponse.ConcealPlayer(player_guid) - ) - ) - - case VehicleSpawnPad.AttachToRails(vehicle, pad) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/${zone.id}/Vehicle", - Service.defaultPlayerGUID, - VehicleResponse.AttachToRails(vehicle.GUID, pad.GUID) - ) - ) - - case VehicleSpawnPad.StartPlayerSeatedInVehicle(driver_name, vehicle, pad) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$driver_name/Vehicle", - Service.defaultPlayerGUID, - VehicleResponse.StartPlayerSeatedInVehicle(vehicle, pad) - ) - ) - - case VehicleSpawnPad.PlayerSeatedInVehicle(driver_name, vehicle, pad) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$driver_name/Vehicle", - Service.defaultPlayerGUID, - VehicleResponse.PlayerSeatedInVehicle(vehicle, pad) - ) - ) - - case VehicleSpawnPad.ServerVehicleOverrideStart(driver_name, vehicle, pad) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$driver_name/Vehicle", - Service.defaultPlayerGUID, - VehicleResponse.ServerVehicleOverrideStart(vehicle, pad) - ) - ) - - case VehicleSpawnPad.ServerVehicleOverrideEnd(driver_name, vehicle, pad) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$driver_name/Vehicle", - Service.defaultPlayerGUID, - VehicleResponse.ServerVehicleOverrideEnd(vehicle, pad) - ) - ) - - case VehicleSpawnPad.DetachFromRails(vehicle, pad) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/${zone.id}/Vehicle", - Service.defaultPlayerGUID, - VehicleResponse.DetachFromRails(vehicle.GUID, pad.GUID, pad.Position, pad.Orientation.z) - ) - ) - case VehicleSpawnPad.ResetSpawnPad(pad) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/${zone.id}/Vehicle", - Service.defaultPlayerGUID, - VehicleResponse.ResetSpawnPad(pad.GUID) - ) - ) - - case VehicleSpawnPad.RevealPlayer(player_guid) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/${zone.id}/Vehicle", - Service.defaultPlayerGUID, - VehicleResponse.RevealPlayer(player_guid) - ) - ) - - case VehicleSpawnPad.PeriodicReminder(to, reason, data) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/$to/Vehicle", - Service.defaultPlayerGUID, - VehicleResponse.PeriodicReminder(reason, data) - ) - ) - - //correspondence from WorldSessionActor - case VehicleServiceMessage.AMSDeploymentChange(_) => - VehicleEvents.publish( - VehicleServiceResponse( - s"/${zone.id}/Vehicle", - Service.defaultPlayerGUID, - VehicleResponse.UpdateAmsSpawnPoint(AmsSpawnPoints(zone)) - ) - ) - - case msg => - log.warn(s"Unhandled message $msg from ${sender()}") - } - - import net.psforever.objects.serverobject.tube.SpawnTube - 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 VehicleService { + def apply(): Props = { + Props( + classOf[GenericEventServiceWithCacheAndSupport], + VehicleStamp, + List(TurretUpgradeSupport) + ) } } diff --git a/src/main/scala/net/psforever/services/vehicle/VehicleServiceMessage.scala b/src/main/scala/net/psforever/services/vehicle/VehicleServiceMessage.scala deleted file mode 100644 index 07bb29769..000000000 --- a/src/main/scala/net/psforever/services/vehicle/VehicleServiceMessage.scala +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) 2017 PSForever -package net.psforever.services.vehicle - -import net.psforever.objects.{PlanetSideGameObject, Vehicle} -import net.psforever.objects.equipment.Equipment -import net.psforever.objects.inventory.InventoryItem -import net.psforever.objects.zones.Zone -import net.psforever.packet.PlanetSideGamePacket -import net.psforever.packet.game.objectcreate.ConstructorData -import net.psforever.types.{BailType, DriveState, PlanetSideGUID, Vector3} - -final case class VehicleServiceMessage(forChannel: String, actionMessage: VehicleAction.Action) - -object VehicleServiceMessage { - final case class GiveActorControl(vehicle: Vehicle, actorName: String) - final case class RevokeActorControl(vehicle: Vehicle) - - final case class TurretUpgrade(msg: Any) - - final case class AMSDeploymentChange(zone: Zone) -} - -object VehicleAction { - trait Action - - final case class ChangeAmmo( - player_guid: PlanetSideGUID, - weapon_guid: PlanetSideGUID, - weapon_slot: Int, - old_ammo_guid: PlanetSideGUID, - ammo_id: Int, - ammo_guid: PlanetSideGUID, - ammo_data: ConstructorData - ) extends Action - final case class ChangeFireState_Start(player_guid: PlanetSideGUID, weapon_guid: PlanetSideGUID) extends Action - final case class ChangeFireState_Stop(player_guid: PlanetSideGUID, weapon_guid: PlanetSideGUID) extends Action - final case class ChildObjectState(player_guid: PlanetSideGUID, object_guid: PlanetSideGUID, pitch: Float, yaw: Float) - extends Action - final case class DeployRequest( - player_guid: PlanetSideGUID, - object_guid: PlanetSideGUID, - state: DriveState.Value, - unk1: Int, - unk2: Boolean, - pos: Vector3 - ) extends Action - final case class DismountVehicle(player_guid: PlanetSideGUID, bailType: BailType.Value, unk2: Boolean) extends Action - final case class EquipmentInSlot( - player_guid: PlanetSideGUID, - target_guid: PlanetSideGUID, - slot: Int, - equipment: Equipment - ) extends Action - final case class FrameVehicleState( - player_guid: PlanetSideGUID, - vehicle_guid: PlanetSideGUID, - unk1: Int, - pos: Vector3, - orient: Vector3, - vel: Option[Vector3], - unk2: Boolean, - unk3: Int, - unk4: Int, - is_crouched: Boolean, - unk6: Boolean, - unk7: Boolean, - unk8: Int, - unk9: Long, - unkA: Long - ) extends Action - final case class GenericObjectAction(player_guid: PlanetSideGUID, guid: PlanetSideGUID, action: Int) extends Action - final case class InventoryState( - player_guid: PlanetSideGUID, - obj: PlanetSideGameObject, - parent_guid: PlanetSideGUID, - start: Int, - con_data: ConstructorData - ) extends Action - final case class InventoryState2( - player_guid: PlanetSideGUID, - obj_guid: PlanetSideGUID, - parent_guid: PlanetSideGUID, - value: Int - ) extends Action - final case class KickPassenger(player_guid: PlanetSideGUID, unk1: Int, unk2: Boolean, vehicle_guid: PlanetSideGUID) - extends Action - final case class LoadVehicle( - player_guid: PlanetSideGUID, - vehicle: Vehicle, - vtype: Int, - vguid: PlanetSideGUID, - vdata: ConstructorData - ) extends Action - final case class MountVehicle(player_guid: PlanetSideGUID, object_guid: PlanetSideGUID, seat: Int) extends Action - final case class ObjectDelete(guid: PlanetSideGUID) extends Action - final case class LoseOwnership(owner_guid: PlanetSideGUID, vehicle_guid: PlanetSideGUID) extends Action - final case class Ownership(player_guid: PlanetSideGUID, vehicle_guid: PlanetSideGUID) extends Action - final case class PlanetsideAttribute( - player_guid: PlanetSideGUID, - target_guid: PlanetSideGUID, - attribute_type: Int, - attribute_value: Long - ) extends Action - final case class Reload(player_guid: PlanetSideGUID, weapon_guid: PlanetSideGUID) extends Action - final case class SeatPermissions( - player_guid: PlanetSideGUID, - vehicle_guid: PlanetSideGUID, - seat_group: Int, - permission: Long - ) extends Action - final case class StowEquipment(player_guid: PlanetSideGUID, vehicle_guid: PlanetSideGUID, slot: Int, item: Equipment) - extends Action - final case class UnloadVehicle( - player_guid: PlanetSideGUID, - vehicle: Vehicle, - vehicle_guid: PlanetSideGUID - ) extends Action - final case class UnstowEquipment(player_guid: PlanetSideGUID, item_guid: PlanetSideGUID) extends Action - final case class VehicleState( - player_guid: PlanetSideGUID, - vehicle_guid: PlanetSideGUID, - unk1: Int, - pos: Vector3, - ang: Vector3, - vel: Option[Vector3], - unk2: Option[Int], - unk3: Int, - unk4: Int, - wheel_direction: Int, - unk5: Boolean, - unk6: Boolean - ) extends Action - final case class SendResponse(player_guid: PlanetSideGUID, msg: PlanetSideGamePacket) extends Action - final case class WeaponDryFire(player_guid: PlanetSideGUID, weapon_guid: PlanetSideGUID) extends Action - final case class UpdateAmsSpawnPoint(zone: Zone) extends Action - - final case class TransferPassengerChannel( - player_guid: PlanetSideGUID, - temp_channel: String, - new_channel: String, - vehicle: Vehicle, - vehicle_to_delete: PlanetSideGUID - ) extends Action - - final case class KickCargo(player_guid: PlanetSideGUID, cargo: Vehicle, speed: Int, delay: Long) extends Action - - final case class ChangeLoadout( - target_guid: PlanetSideGUID, - removed_weapons: List[(Equipment, PlanetSideGUID)], - new_weapons: List[InventoryItem], - old_inventory: List[(Equipment, PlanetSideGUID)], - new_inventory: List[InventoryItem] - ) extends Action -} diff --git a/src/main/scala/net/psforever/services/vehicle/VehicleServiceResponse.scala b/src/main/scala/net/psforever/services/vehicle/VehicleServiceResponse.scala deleted file mode 100644 index 22e2ed94f..000000000 --- a/src/main/scala/net/psforever/services/vehicle/VehicleServiceResponse.scala +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) 2017 PSForever -package net.psforever.services.vehicle - -import net.psforever.objects.equipment.Equipment -import net.psforever.objects.inventory.InventoryItem -import net.psforever.objects.serverobject.pad.VehicleSpawnPad -import net.psforever.objects.serverobject.pad.VehicleSpawnPad.Reminders -import net.psforever.objects.{PlanetSideGameObject, Vehicle} -import net.psforever.objects.serverobject.tube.SpawnTube -import net.psforever.packet.PlanetSideGamePacket -import net.psforever.packet.game.objectcreate.ConstructorData -import net.psforever.packet.game.ObjectCreateMessage -import net.psforever.types.{BailType, DriveState, PlanetSideGUID, Vector3} -import net.psforever.services.GenericEventBusMsg - -final case class VehicleServiceResponse( - channel: String, - avatar_guid: PlanetSideGUID, - replyMessage: VehicleResponse.Response -) extends GenericEventBusMsg - -object VehicleResponse { - trait Response - - final case class ChangeAmmo( - weapon_guid: PlanetSideGUID, - weapon_slot: Int, - old_ammo_guid: PlanetSideGUID, - ammo_id: Int, - ammo_guid: PlanetSideGUID, - ammo_data: ConstructorData - ) extends Response - final case class ChangeFireState_Start(weapon_guid: PlanetSideGUID) extends Response - final case class ChangeFireState_Stop(weapon_guid: PlanetSideGUID) extends Response - final case class ChildObjectState(object_guid: PlanetSideGUID, pitch: Float, yaw: Float) extends Response - final case class ConcealPlayer(player_guid: PlanetSideGUID) extends Response - final case class DeployRequest( - object_guid: PlanetSideGUID, - state: DriveState.Value, - unk1: Int, - unk2: Boolean, - pos: Vector3 - ) extends Response - final case class DismountVehicle(bailType: BailType.Value, unk2: Boolean) extends Response - final case class EquipmentInSlot(pkt: ObjectCreateMessage) extends Response - final case class FrameVehicleState( - vehicle_guid: PlanetSideGUID, - unk1: Int, - pos: Vector3, - orient: Vector3, - vel: Option[Vector3], - unk2: Boolean, - unk3: Int, - unk4: Int, - is_crouched: Boolean, - unk6: Boolean, - unk7: Boolean, - unk8: Int, - unk9: Long, - unkA: Long - ) extends Response - final case class GenericObjectAction(guid: PlanetSideGUID, action: Int) extends Response - final case class HitHint(source_guid: PlanetSideGUID) extends Response - final case class InventoryState( - obj: PlanetSideGameObject, - parent_guid: PlanetSideGUID, - start: Int, - con_data: ConstructorData - ) extends Response - final case class InventoryState2(obj_guid: PlanetSideGUID, parent_guid: PlanetSideGUID, value: Int) extends Response - final case class KickPassenger(seat_num: Int, kickedByDriver: Boolean, vehicle_guid: PlanetSideGUID) extends Response - final case class LoadVehicle(vehicle: Vehicle, vtype: Int, vguid: PlanetSideGUID, vdata: ConstructorData) - extends Response - final case class MountVehicle(object_guid: PlanetSideGUID, seat: Int) extends Response - final case class ObjectDelete(guid: PlanetSideGUID) extends Response - final case class Ownership(vehicle_guid: PlanetSideGUID) extends Response - final case class LoseOwnership(owner_guid: PlanetSideGUID, vehicle_guid: PlanetSideGUID) - extends Response - final case class PlanetsideAttribute(vehicle_guid: PlanetSideGUID, attribute_type: Int, attribute_value: Long) - extends Response - final case class Reload(weapon_guid: PlanetSideGUID) extends Response - final case class RevealPlayer(player_guid: PlanetSideGUID) extends Response - final case class SeatPermissions(vehicle_guid: PlanetSideGUID, seat_group: Int, permission: Long) extends Response - final case class StowEquipment( - vehicle_guid: PlanetSideGUID, - slot: Int, - itype: Int, - iguid: PlanetSideGUID, - idata: ConstructorData - ) extends Response - final case class WeaponDryFire(weapon_guid: PlanetSideGUID) extends Response - final case class UnloadVehicle(vehicle: Vehicle, vehicle_guid: PlanetSideGUID) extends Response - final case class UnstowEquipment(item_guid: PlanetSideGUID) extends Response - final case class VehicleState( - vehicle_guid: PlanetSideGUID, - unk1: Int, - pos: Vector3, - ang: Vector3, - vel: Option[Vector3], - unk2: Option[Int], - unk3: Int, - unk4: Int, - wheel_direction: Int, - unk5: Boolean, - unk6: Boolean - ) extends Response - final case class SendResponse(msg: PlanetSideGamePacket) extends Response - final case class UpdateAmsSpawnPoint(list: List[SpawnTube]) extends Response - - final case class AttachToRails(vehicle_guid: PlanetSideGUID, rails_guid: PlanetSideGUID) extends Response - final case class StartPlayerSeatedInVehicle(vehicle: Vehicle, pad: VehicleSpawnPad) extends Response - final case class PlayerSeatedInVehicle(vehicle: Vehicle, pad: VehicleSpawnPad) extends Response - final case class DetachFromRails( - vehicle_guid: PlanetSideGUID, - rails_guid: PlanetSideGUID, - rails_pos: Vector3, - rails_rot: Float - ) extends Response - final case class ServerVehicleOverrideStart(vehicle: Vehicle, pad: VehicleSpawnPad) extends Response - final case class ServerVehicleOverrideEnd(vehicle: Vehicle, pad: VehicleSpawnPad) extends Response - final case class ResetSpawnPad(pad_guid: PlanetSideGUID) extends Response - final case class PeriodicReminder(reason: Reminders.Value, data: Option[Any] = None) extends Response - - final case class TransferPassengerChannel( - old_channel: String, - temp_channel: String, - vehicle: Vehicle, - vehicle_to_delete: PlanetSideGUID - ) extends Response - - final case class KickCargo(cargo: Vehicle, speed: Int, delay: Long) extends Response - - final case class ChangeLoadout( - target_guid: PlanetSideGUID, - removed_weapons: List[(Equipment, PlanetSideGUID)], - new_weapons: List[InventoryItem], - old_inventory: List[(Equipment, PlanetSideGUID)], - new_inventory: List[InventoryItem] - ) extends Response -} 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 bd6e02866..ae2ce9d51 100644 --- a/src/main/scala/net/psforever/services/vehicle/support/TurretUpgrader.scala +++ b/src/main/scala/net/psforever/services/vehicle/support/TurretUpgrader.scala @@ -1,7 +1,7 @@ // Copyright (c) 2017 PSForever package net.psforever.services.vehicle.support -import akka.actor.Cancellable +import akka.actor.{ActorContext, ActorRef, Cancellable, Props} import net.psforever.objects.equipment.EquipmentSlot import net.psforever.objects.{AmmoBox, Default, PlanetSideGameObject, Tool} import net.psforever.objects.guid._ @@ -9,14 +9,29 @@ 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.{EventServiceSupport, GenericSupportEnvelopeOnly} +import net.psforever.services.base.envelope.{BundledEnvelope, MessageEnvelope} import net.psforever.types.PlanetSideGUID -import net.psforever.services.support.{SimilarityComparator, SupportActor, SupportActorCaseConversions} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.support.{SimilarityComparator, SupportActor, SupportActorCaseConversions} +import net.psforever.services.vehicle.VehicleAction import scala.concurrent.Future import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits.global +case object TurretUpgradeSupport + extends EventServiceSupport { + def label: String = "turretUpgrader" + def constructor(context: ActorContext): ActorRef = { + context.actorOf(Props[TurretUpgrader](), name = "TurretUpgrader") + } +} + +final case class TurretEnvelope(supportMessage: Any) + extends GenericSupportEnvelopeOnly { + def supportLabel: String = "turretUpgrader" +} + class TurretUpgrader extends SupportActor[TurretUpgrader.Entry] { var task: Cancellable = Default.Cancellable @@ -159,9 +174,10 @@ class TurretUpgrader extends SupportActor[TurretUpgrader.Entry] { seat.unmount(tplayer) tplayer.VehicleSeated = None if (tplayer.HasGUID) { - context.parent ! VehicleServiceMessage( + context.parent ! MessageEnvelope( zoneId, - VehicleAction.KickPassenger(tplayer.GUID, 4, unk2=false, turretGUID) + tplayer.GUID, + VehicleAction.KickPassenger(4, unk2=false, turretGUID) ) } }) @@ -229,15 +245,16 @@ class TurretUpgrader extends SupportActor[TurretUpgrader.Entry] { trace(s"Wall turret finished ${target.Upgrade} upgrade") val targetGUID = target.GUID if (target.Health > 0) { - target.Weapons + context.parent ! BundledEnvelope(target.Weapons .map { case (index: Int, slot: EquipmentSlot) => (index, slot.Equipment) } .collect { case (index, Some(tool: Tool)) => - context.parent ! VehicleServiceMessage( + MessageEnvelope( zone.id, - VehicleAction.EquipmentInSlot(PlanetSideGUID(0), targetGUID, index, tool) + VehicleAction.EquipmentInSlot(targetGUID, index, tool) ) } + ) } Finalize(target, entry.upgrade) } diff --git a/src/main/scala/net/psforever/util/Config.scala b/src/main/scala/net/psforever/util/Config.scala index c814904b6..f32c2adaf 100644 --- a/src/main/scala/net/psforever/util/Config.scala +++ b/src/main/scala/net/psforever/util/Config.scala @@ -131,7 +131,8 @@ case class AntiCheatConfig( case class NetworkConfig( session: SessionConfig, - middleware: MiddlewareConfig + middleware: MiddlewareConfig, + eventCaching: CachedMessagesConfig ) case class MiddlewareConfig( @@ -147,6 +148,12 @@ case class SessionConfig( outboundGraceTime: FiniteDuration ) +case class CachedMessagesConfig( + flushCacheDelay: Long, //milliseconds + flushCacheMaxDelay: Long, //milliseconds + messageTrafficThreshold: Long +) + case class GameConfig( instantAction: InstantActionConfig, amenityAutorepairRate: Float, diff --git a/src/test/scala/objects/DamageModelTests.scala b/src/test/scala/objects/DamageModelTests.scala index b19467e75..1dcd9757b 100644 --- a/src/test/scala/objects/DamageModelTests.scala +++ b/src/test/scala/objects/DamageModelTests.scala @@ -31,6 +31,9 @@ class DamageCalculationsTests extends Specification { val projectile = Projectile(proj, wep, wep_fmode, player, Vector3(2, 2, 0), Vector3.Zero) val target = Vehicle(GlobalDefinitions.fury) target.Position = Vector3(10, 0, 0) + player.GUID = PlanetSideGUID(1) + projectile.GUID = PlanetSideGUID(2) + target.GUID = PlanetSideGUID(3) val resprojectile = DamageInteraction( SourceEntry(target), ProjectileReason( @@ -271,6 +274,7 @@ class DamageCalculationsTests extends Specification { ) val minDamageBase = charge_weapon.Projectile.Charging.get.min.Damage0 val chargeBaseDamage = charge_weapon.Projectile.Damage0 + charge_projectile.GUID = PlanetSideGUID(1) "charge (none)" in { val cprojectile = charge_projectile.quality(ProjectileQuality.Modified(0)) @@ -329,6 +333,7 @@ class DamageCalculationsTests extends Specification { Vector3(2, 2, 0), Vector3.Zero ) + flak_projectile.GUID = PlanetSideGUID(1) "flak hit (resolution is splash, no degrade)" in { val resfprojectile = DamageInteraction( diff --git a/src/test/scala/objects/DamageableTest.scala b/src/test/scala/objects/DamageableTest.scala index bd2381fb9..8c3357fe4 100644 --- a/src/test/scala/objects/DamageableTest.scala +++ b/src/test/scala/objects/DamageableTest.scala @@ -22,11 +22,9 @@ import net.psforever.objects.vital.{SpawningActivity, Vitality} import net.psforever.objects.zones.{Zone, ZoneMap} import net.psforever.packet.game.DamageWithPositionMessage import net.psforever.types._ -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.support.SupportActor -import net.psforever.services.vehicle.support.TurretUpgrader -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.support.SupportActor +import net.psforever.services.vehicle.support.TurretEnvelope import org.specs2.mutable.Specification import scala.concurrent.duration._ @@ -38,6 +36,9 @@ import net.psforever.objects.vital.interaction.DamageInteraction import net.psforever.objects.vital.base.DamageResolution import net.psforever.objects.vital.projectile.ProjectileReason import net.psforever.objects.vital.resolution.ResolutionCalculations.Output +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{ObjectDelete, PlanetsideAttribute, SendResponse} +import net.psforever.services.vehicle.support.TurretUpgrader class DamageableTest extends Specification { val player1: Player = Player(Avatar(0, "TestCharacter1", PlanetSideEmpire.TR, CharacterSex.Male, 0, CharacterVoice.Mute)) @@ -328,7 +329,7 @@ class DamageableEntityDamageTest extends ActorTest { val msg1 = avatarProbe.receiveOne(5000 milliseconds) val msg2 = activityProbe.receiveOne(5000 milliseconds) msg1 match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(ValidPlanetSideGUID(2), 0, 3600)) => () + case MessageEnvelope("test", _, PlanetsideAttribute(ValidPlanetSideGUID(2), 0, 3600)) => () case _ => assert(false, "DamageableEntity:handle taking damage - player not messaged") } msg2 match { @@ -402,13 +403,13 @@ class DamageableEntityDestroyedTest extends ActorTest { val msg1_2 = avatarProbe.receiveN(2, 500 milliseconds) assert( msg1_2.head match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true case _ => false } ) assert( msg1_2(1) match { - case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => true + case MessageEnvelope("test", _, AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => true case _ => false } ) @@ -553,19 +554,19 @@ class DamageableAmenityTest extends ActorTest { term.Actor ! Vitality.Damage(applyDamageTo) val msg1234 = avatarProbe.receiveN(4, 3000 milliseconds) msg1234.head match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => () + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => () case _ => assert(false) } msg1234(1) match { - case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => () + case MessageEnvelope("test", _, AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => () case _ => assert(false) } msg1234(2) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 50, 1)) => () + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 50, 1)) => () case _ => assert(false) } msg1234(3) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 51, 1)) => () + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 51, 1)) => () case _ => assert(false) } assert(term.Health <= term.Definition.DamageDestroysAt) @@ -641,7 +642,7 @@ class DamageableMountableDamageTest extends ActorTest { val msg2 = activityProbe.receiveOne(5000 milliseconds) assert( msg1_3.head match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true case _ => false } ) @@ -656,9 +657,10 @@ class DamageableMountableDamageTest extends ActorTest { ) assert( msg1_3(1) match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter2", - AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(_, Vector3(2, 2, 2))) + _, + SendResponse(Seq(DamageWithPositionMessage(_, Vector3(2, 2, 2)))) ) => true case _ => false @@ -744,11 +746,11 @@ class DamageableMountableDestroyTest extends ActorTest { val msg3 = player2Probe.receiveOne(200 milliseconds) msg12.head match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => () + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => () case _ => assert(false) } msg12(1) match { - case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => () + case MessageEnvelope("test", _, AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => () case _ => assert(false) } msg3 match { @@ -831,7 +833,7 @@ class DamageableWeaponTurretDamageTest extends ActorTest { val msg4 = avatarProbe.receiveOne(500 milliseconds) assert( msg12 match { - case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(2), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true case _ => false } ) @@ -846,9 +848,10 @@ class DamageableWeaponTurretDamageTest extends ActorTest { ) assert( msg4 match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter2", - AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(_, Vector3(2, 2, 2))) + _, + SendResponse(Seq(DamageWithPositionMessage(_, Vector3(2, 2, 2)))) ) => true case _ => false @@ -931,9 +934,10 @@ class DamageableWeaponTurretJammerTest extends ActorTest { val msg12 = vehicleProbe.receiveN(2, 500 milliseconds) assert( msg12.head match { - case VehicleServiceMessage( + case MessageEnvelope( "test", - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, PlanetSideGUID(2), 27, 1) + _, + PlanetsideAttribute(PlanetSideGUID(2), 27, 1) ) => true case _ => false @@ -941,9 +945,10 @@ class DamageableWeaponTurretJammerTest extends ActorTest { ) assert( msg12(1) match { - case VehicleServiceMessage( + case MessageEnvelope( "test", - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, PlanetSideGUID(5), 27, 1) + _, + PlanetsideAttribute(PlanetSideGUID(5), 27, 1) ) => true case _ => false @@ -1070,12 +1075,12 @@ class DamageableWeaponTurretDestructionTest extends ActorTest { val msg3 = player2Probe.receiveOne(200 milliseconds) val msg56 = vehicleProbe.receiveN(2, 200 milliseconds) msg12_4.head match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => ; + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => ; case _ => assert(false, s"DamageableWeaponTurretDestructionTest-1: ${msg12_4.head}") } msg12_4(1) match { - case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => ; + case MessageEnvelope("test", _, AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => ; case _ => assert(false, s"DamageableWeaponTurretDestructionTest-2: ${msg12_4(1)}") } @@ -1085,17 +1090,17 @@ class DamageableWeaponTurretDestructionTest extends ActorTest { assert(false, s"DamageableWeaponTurretDestructionTest-3: player not dead - $msg3") } msg12_4(2) match { - case AvatarServiceMessage("test", AvatarAction.ObjectDelete(PlanetSideGUID(0), PlanetSideGUID(5), _)) => ; + case MessageEnvelope("test", _, ObjectDelete(PlanetSideGUID(5), _)) => ; case _ => assert(false, s"DamageableWeaponTurretDestructionTest-4: ${msg12_4(2)}") } msg56.head match { - case VehicleServiceMessage.TurretUpgrade(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 VehicleServiceMessage.TurretUpgrade(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) @@ -1180,13 +1185,13 @@ class DamageableVehicleDamageTest extends ActorTest { val msg4 = avatarProbe.receiveOne(200 milliseconds) assert( msg12.head match { - case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)) => true + case MessageEnvelope(_, _, PlanetsideAttribute(PlanetSideGUID(1), 68, _)) => true case _ => false } ) assert( msg12(1) match { - case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(1), 0, _)) => true case _ => false } ) @@ -1201,9 +1206,10 @@ class DamageableVehicleDamageTest extends ActorTest { ) assert( msg4 match { - case AvatarServiceMessage( - "TestCharacter2", - AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(9, Vector3(2, 0, 0))) + case MessageEnvelope( + "TestCharacter2", + _, + SendResponse(Seq(DamageWithPositionMessage(9, Vector3(2, 0, 0)))) ) => true case _ => false @@ -1316,11 +1322,11 @@ class DamageableVehicleDamageMountedTest extends FreedContextActorTest { val msg3 = activityProbe.receiveOne(500 milliseconds) val msg45 = avatarProbe.receiveN(2,500 milliseconds) msg12.head match { - case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 68, _)) => ; + case MessageEnvelope(_, _, PlanetsideAttribute(PlanetSideGUID(1), 68, _)) => ; case _ => assert(false) } msg12(1) match { - case VehicleServiceMessage("test", VehicleAction.PlanetsideAttribute(PlanetSideGUID(0), PlanetSideGUID(1), 0, _)) => ; + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(1), 0, _)) => ; case _ => assert(false) } msg3 match { @@ -1331,16 +1337,18 @@ class DamageableVehicleDamageMountedTest extends FreedContextActorTest { case _ => assert(false) } msg45.head match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter2", - AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(400, Vector3(2, 0, 0))) + _, + SendResponse(Seq(DamageWithPositionMessage(400, Vector3(2, 0, 0)))) ) => ; case _ => assert(false) } msg45(1) match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter3", - AvatarAction.SendResponse(Service.defaultPlayerGUID, DamageWithPositionMessage(0, Vector3(2, 0, 0))) + _, + SendResponse(Seq(DamageWithPositionMessage(0, Vector3(2, 0, 0)))) ) => ; case _ => assert(false) } @@ -1451,9 +1459,10 @@ class DamageableVehicleJammeringMountedTest extends FreedContextActorTest { player3Probe.expectNoMessage(200 milliseconds) assert( msg12 match { - case VehicleServiceMessage( + case MessageEnvelope( "test", - VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, PlanetSideGUID(4), 27, 1) + _, + PlanetsideAttribute(PlanetSideGUID(4), 27, 1) ) => true case _ => false @@ -1545,11 +1554,11 @@ class DamageableVehicleDestroyTest extends ActorTest { val msg124 = avatarProbe.receiveN(2, 3000 milliseconds) val msg3 = player2Probe.receiveOne(3000 milliseconds) msg124.head match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => () + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(1), 0, _)) => () case _ => assert(false) } msg124(1) match { - case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(1), _, _, Vector3(1, 0, 0))) => () + case MessageEnvelope("test", _, AvatarAction.Destroy(PlanetSideGUID(1), _, _, Vector3(1, 0, 0))) => () case _ => assert(false) } msg3 match { @@ -1710,31 +1719,31 @@ class DamageableVehicleDestroyTest extends ActorTest { // vehicleProbe.expectNoMessage(10 milliseconds) // assert( // msg_avatar.exists({ -// case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(4), 0, _)) => true +// case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(4), 0, _)) => true // case _ => false // }) // ) // assert( // msg_avatar.exists({ -// case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(4), _, _, Vector3(1, 0, 0))) => true +// case MessageEnvelope("test", _, AvatarAction.Destroy(PlanetSideGUID(4), _, _, Vector3(1, 0, 0))) => true // case _ => false // }) // ) // assert( // msg_avatar.exists({ -// case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true +// case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(1), 0, _)) => true // case _ => false // }) // ) // assert( // msg_avatar.exists({ -// case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(1), _, _, Vector3(1, 0, 0))) => true +// case MessageEnvelope("test", _, AvatarAction.Destroy(PlanetSideGUID(1), _, _, Vector3(1, 0, 0))) => true // case _ => false // }) // ) // assert( // msg_avatar.exists({ -// case AvatarServiceMessage("test", AvatarAction.ObjectDelete(PlanetSideGUID(0), PlanetSideGUID(2), _)) => true +// case MessageEnvelope("test", _, ObjectDelete(PlanetSideGUID(0), PlanetSideGUID(2), _)) => true // case _ => false // }) // ) @@ -1752,9 +1761,10 @@ class DamageableVehicleDestroyTest extends ActorTest { // ) // assert( // msg_vehicle.exists({ -// case VehicleServiceMessage( +// case MessageEnvelope( // "test", -// VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, PlanetSideGUID(4), 27, 0) +// _, +// PlanetsideAttribute(PlanetSideGUID(4), 27, 0) // ) => // true // case _ => false @@ -1762,9 +1772,10 @@ class DamageableVehicleDestroyTest extends ActorTest { // ) // assert( // msg_vehicle.exists({ -// case VehicleServiceMessage( +// case MessageEnvelope( // "test", -// VehicleAction.PlanetsideAttribute(Service.defaultPlayerGUID, PlanetSideGUID(1), 68, 0) +// _, +// PlanetsideAttribute(PlanetSideGUID(1), 68, 0) // ) => // true // case _ => false diff --git a/src/test/scala/objects/DeployableBehaviorTest.scala b/src/test/scala/objects/DeployableBehaviorTest.scala index e9ac7df88..77b74fc18 100644 --- a/src/test/scala/objects/DeployableBehaviorTest.scala +++ b/src/test/scala/objects/DeployableBehaviorTest.scala @@ -13,9 +13,9 @@ import net.psforever.objects.guid.NumberPoolHub import net.psforever.objects.guid.source.MaxNumberSource import net.psforever.objects.zones.{Zone, ZoneDeployableActor, ZoneMap} import net.psforever.packet.game._ -import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{ObjectDelete, SendResponse} +import net.psforever.services.local.LocalAction import net.psforever.types._ import scala.collection.mutable @@ -50,16 +50,14 @@ class DeployableBehaviorSetupTest extends ActorTest { val eventsMsgs = eventsProbe.receiveN(2, 10.seconds) eventsMsgs.head match { - case LocalServiceMessage("test", LocalAction.DeployItem(obj)) => + case MessageEnvelope("test", _, LocalAction.DeployItem(obj)) => assert(obj eq jmine, "self-setup test - not same mine") case _ => assert( false, "self-setup test - wrong deploy message") } eventsMsgs(1) match { - case LocalServiceMessage( - "TR", + case MessageEnvelope("TR", _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Build, DeployableInfo(PlanetSideGUID(1), DeployableIcon.DisruptorMine, Vector3(1,2,3), PlanetSideGUID(0)) ) @@ -160,39 +158,36 @@ class DeployableBehaviorSetupOwnedP2Test extends FreedContextActorTest { //assert(false, "test needs to be fixed") val eventsMsgs = eventsProbe.receiveN(7, 10.seconds) eventsMsgs.head match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction.SendResponse( - Service.defaultPlayerGUID, - ObjectDeployedMessage(0, "jammer_mine", DeployOutcome.Success, 1, 20) - ) + _, + SendResponse(Seq(ObjectDeployedMessage(0, "jammer_mine", DeployOutcome.Success, 1, 20))) ) => ; case _ => assert(false, "owned setup test, 2 - did not receive build confirmation") } eventsMsgs(1) match { - case LocalServiceMessage("TestCharacter1", LocalAction.DeployableUIFor(DeployedItem.jammer_mine)) => ; + case MessageEnvelope("TestCharacter1", _, LocalAction.DeployableUIFor(DeployedItem.jammer_mine)) => ; case _ => assert(false, "owned setup test, 2 - did not receive ui update") } eventsMsgs(2) match { - case LocalServiceMessage( + case MessageEnvelope( "test", - LocalAction.TriggerEffectLocation(PlanetSideGUID(3), "spawn_object_effect", Vector3(1,2,3), Vector3(4,5,6)) + PlanetSideGUID(3), + LocalAction.TriggerEffectLocation("spawn_object_effect", Vector3(1,2,3), Vector3(4,5,6)) ) => ; case _ => assert(false, "owned setup test, 2 - no spawn fx") } eventsMsgs(3) match { - case LocalServiceMessage("test", LocalAction.DeployItem(obj)) => + case MessageEnvelope("test", _, LocalAction.DeployItem(obj)) => assert(obj eq jmine, "owned setup test, 2 - not same mine") case _ => assert( false, "owned setup test, 2 - wrong deploy message") } //the message order can be jumbled from here-on eventsMsgs(4) match { - case LocalServiceMessage( - "TR", + case MessageEnvelope("TR", _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Build, DeployableInfo(PlanetSideGUID(1), DeployableIcon.DisruptorMine, Vector3(1,2,3), PlanetSideGUID(3)) ) @@ -201,15 +196,16 @@ class DeployableBehaviorSetupOwnedP2Test extends FreedContextActorTest { assert(false, "owned setup test, 2 - no icon or wrong icon") } eventsMsgs(5) match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction.SendResponse(Service.defaultPlayerGUID, GenericObjectActionMessage(PlanetSideGUID(1), 21)) + _, + SendResponse(Seq(GenericObjectActionMessage(PlanetSideGUID(1), 21))) ) => ; case _ => assert(false, "owned setup test, 2 - build action not reset (GOAM21)") } eventsMsgs(6) match { - case AvatarServiceMessage("test", AvatarAction.ObjectDelete(Service.defaultPlayerGUID, PlanetSideGUID(2), 0)) => ; + case MessageEnvelope("test", _, ObjectDelete(PlanetSideGUID(2), 0)) => ; case _ => assert(false, "owned setup test, 2 - construction tool not deleted") } @@ -250,14 +246,13 @@ class DeployableBehaviorDeconstructTest extends ActorTest { jmine.Actor ! Deployable.Deconstruct() val eventsMsgs = eventsProbe.receiveN(2, 10.seconds) eventsMsgs.head match { - case LocalServiceMessage("test", LocalAction.EliminateDeployable(_, PlanetSideGUID(1), Vector3(1,2,3), 2)) => ; + case MessageEnvelope("test", _, LocalAction.EliminateDeployable(_, PlanetSideGUID(1), Vector3(1,2,3), 2)) => ; case _ => assert(false, "deconstruct test - not eliminating deployable") } eventsMsgs(1) match { - case LocalServiceMessage( - "TR", + case MessageEnvelope( + "TR", _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Dismiss, DeployableInfo(PlanetSideGUID(1), DeployableIcon.DisruptorMine, Vector3(1, 2, 3), PlanetSideGUID(0)) ) @@ -313,15 +308,14 @@ class DeployableBehaviorDeconstructOwnedTest extends FreedContextActorTest { jmine.Actor ! Deployable.Deconstruct() val eventsMsgs = eventsProbe.receiveN(3, 10.seconds) eventsMsgs.head match { - case LocalServiceMessage("test",LocalAction.EliminateDeployable(mine, ValidPlanetSideGUID(1), Vector3(1.0,2.0,3.0),2)) + case MessageEnvelope("test", _, LocalAction.EliminateDeployable(mine, ValidPlanetSideGUID(1), Vector3(1.0,2.0,3.0),2)) if mine eq jmine => ; case _ => assert(false, "owned deconstruct test - not eliminating deployable") } eventsMsgs(1) match { - case LocalServiceMessage( - "TR", + case MessageEnvelope( + "TR", _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Dismiss, DeployableInfo(PlanetSideGUID(1), DeployableIcon.DisruptorMine, Vector3(1, 2, 3), PlanetSideGUID(0)) ) @@ -329,7 +323,7 @@ class DeployableBehaviorDeconstructOwnedTest extends FreedContextActorTest { case _ => assert(false, "owned deconstruct test - not removing icon") } eventsMsgs(2) match { - case LocalServiceMessage("TestCharacter1", LocalAction.DeployableUIFor(DeployedItem.jammer_mine)) => ; + case MessageEnvelope("TestCharacter1", _, LocalAction.DeployableUIFor(DeployedItem.jammer_mine)) => ; case _ => assert(false, "") } diff --git a/src/test/scala/objects/DeployableTest.scala b/src/test/scala/objects/DeployableTest.scala index b02c1ed21..88536ee18 100644 --- a/src/test/scala/objects/DeployableTest.scala +++ b/src/test/scala/objects/DeployableTest.scala @@ -12,19 +12,20 @@ import net.psforever.objects.guid.source.MaxNumberSource import net.psforever.objects.serverobject.mount.{MountInfo, Mountable, SeatDefinition} import net.psforever.objects.vital.Vitality import net.psforever.objects.zones.{Zone, ZoneDeployableActor, ZoneMap} -import net.psforever.objects.{TurretDeployable, _} +import net.psforever.objects._ import net.psforever.packet.game.{DeployableIcon, DeployableInfo, DeploymentAction} import net.psforever.types._ import org.specs2.mutable.Specification import net.psforever.services.Service -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.local.LocalAction import net.psforever.objects.avatar.Avatar import net.psforever.objects.vital.base.DamageResolution import net.psforever.objects.vital.interaction.DamageInteraction import net.psforever.objects.vital.projectile.ProjectileReason import akka.actor.typed.scaladsl.adapter._ import net.psforever.objects.sourcing.{PlayerSource, SourceEntry} +import net.psforever.services.base.envelope.MessageEnvelope import scala.collection.mutable import scala.concurrent.duration._ @@ -370,8 +371,9 @@ class ExplosiveDeployableJammerTest extends ActorTest { case _ => assert(false, "") } // eventMsgs.head match { -// case LocalServiceMessage( +// case MessageEnvelope( // "NC", +// _, // LocalAction.DeployableMapIcon( // ValidPlanetSideGUID(0), // DeploymentAction.Dismiss, @@ -381,14 +383,14 @@ class ExplosiveDeployableJammerTest extends ActorTest { // case _ => assert(false, "") // } eventMsgs(1) match { - case LocalServiceMessage("test", LocalAction.Detonate(PlanetSideGUID(1), _)) => ; + case MessageEnvelope("test", _, LocalAction.Detonate(PlanetSideGUID(1), _)) => ; case _ => assert(false, "") } eventMsgs(2) match { - case LocalServiceMessage( + case MessageEnvelope( "NC", + _, LocalAction.DeployableMapIcon( - ValidPlanetSideGUID(0), DeploymentAction.Dismiss, DeployableInfo(ValidPlanetSideGUID(1), DeployableIcon.HEMine, Vector3.Zero, ValidPlanetSideGUID(0)) ) @@ -396,10 +398,11 @@ class ExplosiveDeployableJammerTest extends ActorTest { case _ => assert(false, "") } eventMsgs(3) match { - case AvatarServiceMessage( + case MessageEnvelope( "test", + _, AvatarAction.Destroy( - ValidPlanetSideGUID(1), + ValidPlanetSideGUID(1), ValidPlanetSideGUID(3), ValidPlanetSideGUID(0), Vector3.Zero @@ -484,15 +487,15 @@ class ExplosiveDeployableJammerExplodeTest extends ActorTest { case _ => assert(false, "") } eventMsgs(1) match { - case LocalServiceMessage("test", LocalAction.Detonate(PlanetSideGUID(2), target)) + case MessageEnvelope("test", _, LocalAction.Detonate(PlanetSideGUID(2), target)) if target eq h_mine => () case _ => assert(false, "") } eventMsgs(2) match { - case LocalServiceMessage( + case MessageEnvelope( "NC", + _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Dismiss, DeployableInfo(PlanetSideGUID(2), DeployableIcon.HEMine, _, PlanetSideGUID(0)) ) @@ -577,10 +580,10 @@ class ExplosiveDeployableDestructionTest extends ActorTest { player1Probe.expectNoMessage(200 milliseconds) val p2Msgs = player2Probe.receiveN(1, 200 milliseconds) eventMsgs.head match { - case LocalServiceMessage( + case MessageEnvelope( "NC", + _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Dismiss, DeployableInfo(PlanetSideGUID(2), DeployableIcon.HEMine, _, PlanetSideGUID(0)) ) @@ -588,14 +591,14 @@ class ExplosiveDeployableDestructionTest extends ActorTest { case _ => assert(false, "") } eventMsgs(1) match { - case AvatarServiceMessage( - "test", - AvatarAction.Destroy(PlanetSideGUID(2), PlanetSideGUID(3), Service.defaultPlayerGUID, Vector3.Zero) + case MessageEnvelope( + "test", _, + AvatarAction.Destroy(PlanetSideGUID(2), PlanetSideGUID(3), Default.GUID0, Vector3.Zero) ) => ; case _ => assert(false, "") } eventMsgs(2) match { - case LocalServiceMessage("test", LocalAction.TriggerEffect(_, "detonate_damaged_mine", PlanetSideGUID(2))) => ; + case MessageEnvelope("test", _, LocalAction.TriggerEffect("detonate_damaged_mine", PlanetSideGUID(2))) => ; case _ => assert(false, "") } p2Msgs.head match { diff --git a/src/test/scala/objects/DeploymentTest.scala b/src/test/scala/objects/DeploymentTest.scala index 0b86da3f9..685479768 100644 --- a/src/test/scala/objects/DeploymentTest.scala +++ b/src/test/scala/objects/DeploymentTest.scala @@ -8,9 +8,10 @@ import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.{GlobalDefinitions, Vehicle} import net.psforever.objects.serverobject.deploy.{Deployment, DeploymentBehavior} import net.psforever.objects.zones.{Zone, ZoneMap} +import net.psforever.services.base.envelope.MessageEnvelope import net.psforever.types.{DriveState, PlanetSideEmpire, PlanetSideGUID, Vector3} import org.specs2.mutable.Specification -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.vehicle.VehicleAction import scala.concurrent.duration.Duration @@ -72,9 +73,10 @@ class DeploymentBehavior2Test extends ActorTest { case _ => assert(false, "") } reply2.head match { - case VehicleServiceMessage( + 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, "") } @@ -84,9 +86,10 @@ class DeploymentBehavior2Test extends ActorTest { case _ => assert(false, "") } reply2(1) match { - case VehicleServiceMessage( + 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, "") } @@ -100,9 +103,10 @@ class DeploymentBehavior2Test extends ActorTest { case _ => assert(false, "") } reply4.head match { - case VehicleServiceMessage( + 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, "") } @@ -112,9 +116,10 @@ class DeploymentBehavior2Test extends ActorTest { case _ => assert(false, "") } reply4(1) match { - case VehicleServiceMessage( + 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, "") } @@ -140,9 +145,10 @@ class DeploymentBehavior3Test extends ActorTest { case _ => assert(false, "") } reply2.head match { - case VehicleServiceMessage( - "test", - VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deploying, 0, false, Vector3.Zero) + case MessageEnvelope( + "test", + _, + VehicleAction.DeployRequest(PlanetSideGUID(1), DriveState.Deploying, 0, false, Vector3.Zero) ) => () case _ => assert(false, "") } @@ -152,9 +158,10 @@ class DeploymentBehavior3Test extends ActorTest { case _ => assert(false, "") } reply2(1) match { - case VehicleServiceMessage( - "test", - VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Deployed, 0, false, Vector3.Zero) + case MessageEnvelope( + "test", + _, + VehicleAction.DeployRequest(PlanetSideGUID(1), DriveState.Deployed, 0, false, Vector3.Zero) ) => () case _ => assert(false, "") } @@ -168,9 +175,10 @@ class DeploymentBehavior3Test extends ActorTest { case _ => assert(false, "") } reply4.head match { - case VehicleServiceMessage( - "test", - VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Undeploying, 0, false, Vector3.Zero) + case MessageEnvelope( + "test", + _, + VehicleAction.DeployRequest(PlanetSideGUID(1), DriveState.Undeploying, 0, false, Vector3.Zero) ) => () case _ => assert(false, "") } @@ -180,9 +188,10 @@ class DeploymentBehavior3Test extends ActorTest { case _ => assert(false, "") } reply4(1) match { - case VehicleServiceMessage( - "test", - VehicleAction.DeployRequest(_, PlanetSideGUID(1), DriveState.Mobile, 0, false, Vector3.Zero) + case MessageEnvelope( + "test", + _, + 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 15d9db03b..48191ec59 100644 --- a/src/test/scala/objects/DoorTest.scala +++ b/src/test/scala/objects/DoorTest.scala @@ -12,7 +12,9 @@ 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.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse} +import net.psforever.services.base.envelope.GenericResponseEnvelope +import net.psforever.services.local.support.DoorMessage +import net.psforever.services.local.LocalAction import net.psforever.types._ import org.specs2.mutable.Specification @@ -77,7 +79,7 @@ class DoorControlOpenTest extends ActorTest { door.Actor ! CommonMessages.Use(player) val reply = probe.receiveOne(1000 milliseconds) assert(reply match { - case LocalServiceMessage("test", LocalAction.DoorOpens(PlanetSideGUID(0), _, d)) => d eq door + case DoorMessage("test", LocalAction.DoorOpens(_, thisDoor), _) => thisDoor eq door case _ => false }) assert(door.Open.isDefined) @@ -105,7 +107,7 @@ class DoorControlAlreadyOpenTest extends ActorTest { door.Actor.tell(CommonMessages.Use(player), probe.ref) val reply = probe.receiveOne(1000 milliseconds) assert(reply match { - case LocalServiceResponse("test", _, LocalResponse.DoorOpens(guid)) => guid == door.GUID + case GenericResponseEnvelope("test", _, LocalAction.DoorOpens(_, thisDoor)) => thisDoor eq door case _ => false }) } diff --git a/src/test/scala/objects/FacilityTurretTest.scala b/src/test/scala/objects/FacilityTurretTest.scala index 4fcc4f218..105652f08 100644 --- a/src/test/scala/objects/FacilityTurretTest.scala +++ b/src/test/scala/objects/FacilityTurretTest.scala @@ -19,8 +19,9 @@ import net.psforever.objects.zones.{Zone, ZoneMap} import net.psforever.packet.game.{InventoryStateMessage, RepairMessage} import net.psforever.types._ import org.specs2.mutable.Specification -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} +import net.psforever.services.vehicle.VehicleAction import scala.collection.mutable import scala.concurrent.duration._ @@ -259,10 +260,10 @@ class FacilityTurretControlRestorationTest extends ActorTest { val msg4 = vehicleProbe.receiveOne(500 milliseconds) assert( msg12345.head match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction - .SendResponse(PlanetSideGUID(0), InventoryStateMessage(PlanetSideGUID(8), _, PlanetSideGUID(7), _)) + _, + SendResponse(Seq(InventoryStateMessage(PlanetSideGUID(8), _, PlanetSideGUID(7), _))) ) => true case _ => false @@ -270,27 +271,28 @@ class FacilityTurretControlRestorationTest extends ActorTest { ) assert( msg12345(1) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true case _ => false } ) assert( msg12345(2) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 50, 0)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 50, 0)) => true case _ => false } ) assert( msg12345(3) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 51, 0)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 51, 0)) => true case _ => false } ) assert( msg12345(4) match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction.SendResponse(PlanetSideGUID(0), RepairMessage(PlanetSideGUID(2), _)) + _, + SendResponse(Seq(RepairMessage(PlanetSideGUID(2), _))) ) => true case _ => false @@ -298,7 +300,7 @@ class FacilityTurretControlRestorationTest extends ActorTest { ) assert( msg4 match { - case VehicleServiceMessage("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/GeneratorTest.scala b/src/test/scala/objects/GeneratorTest.scala index ef2c175e4..feeea8c2e 100644 --- a/src/test/scala/objects/GeneratorTest.scala +++ b/src/test/scala/objects/GeneratorTest.scala @@ -24,7 +24,9 @@ import net.psforever.objects.zones.{Zone, ZoneMap} import net.psforever.packet.game.{InventoryStateMessage, RepairMessage, TriggerEffectMessage} import net.psforever.types._ import org.specs2.mutable.Specification -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} import scala.concurrent.duration._ @@ -118,7 +120,7 @@ class GeneratorControlDamageTest extends ActorTest { val msg_building = buildingProbe.receiveOne(500 milliseconds) assert( msg_avatar match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true case _ => false } ) @@ -203,7 +205,7 @@ class GeneratorControlCriticalTest extends ActorTest { val msg_building = buildingProbe.receiveOne(500 milliseconds) assert( msg_avatar match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true case _ => false } ) @@ -312,21 +314,22 @@ class GeneratorControlDestroyedTest extends ActorTest { ) assert( msg_avatar2.head match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true case _ => false } ) assert( msg_avatar2(1) match { - case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => true + case MessageEnvelope("test", _, AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => true case _ => false } ) assert( msg_avatar2(2) match { - case AvatarServiceMessage( + case MessageEnvelope( "test", - AvatarAction.SendResponse(_, TriggerEffectMessage(PlanetSideGUID(2), "explosion_generator", None, None)) + _, + SendResponse(Seq(TriggerEffectMessage(PlanetSideGUID(2), "explosion_generator", None, None))) ) => true case _ => false @@ -440,21 +443,22 @@ class GeneratorControlKillsTest extends ActorTest { ) assert( msg_avatar2.head match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true case _ => false } ) assert( msg_avatar2(1) match { - case AvatarServiceMessage("test", AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => true + case MessageEnvelope("test", _, AvatarAction.Destroy(PlanetSideGUID(2), _, _, Vector3(1, 0, 0))) => true case _ => false } ) assert( msg_avatar2(2) match { - case AvatarServiceMessage( + case MessageEnvelope( "test", - AvatarAction.SendResponse(_, TriggerEffectMessage(PlanetSideGUID(2), "explosion_generator", None, None)) + _, + SendResponse(Seq(TriggerEffectMessage(PlanetSideGUID(2), "explosion_generator", None, None))) ) => true case _ => false @@ -818,10 +822,10 @@ class GeneratorControlRepairPastRestorePoint extends ActorTest { val msg_building = buildingProbe.receiveOne(200 milliseconds) assert( msg_avatar.head match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction - .SendResponse(_, InventoryStateMessage(ValidPlanetSideGUID(5), _, ValidPlanetSideGUID(4), _)) + _, + SendResponse(Seq(InventoryStateMessage(ValidPlanetSideGUID(5), _, ValidPlanetSideGUID(4), _))) ) => true case _ => false @@ -829,15 +833,16 @@ class GeneratorControlRepairPastRestorePoint extends ActorTest { ) assert( msg_avatar(1) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true case _ => false } ) assert( msg_avatar(2) match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction.SendResponse(_, RepairMessage(ValidPlanetSideGUID(2), _)) + _, + SendResponse(Seq(RepairMessage(ValidPlanetSideGUID(2), _))) ) => true case _ => false diff --git a/src/test/scala/objects/OrbitalShuttlePadTest.scala b/src/test/scala/objects/OrbitalShuttlePadTest.scala index 04eb3f252..170316a62 100644 --- a/src/test/scala/objects/OrbitalShuttlePadTest.scala +++ b/src/test/scala/objects/OrbitalShuttlePadTest.scala @@ -24,7 +24,7 @@ import scala.concurrent.duration._ class OrbitalShuttlePadControlTest extends FreedContextActorTest { import akka.actor.typed.scaladsl.adapter._ val services: ActorRef = ServiceManager.boot(system) - services ! ServiceManager.Register(Props[GalaxyService](), "galaxy") + services ! ServiceManager.Register(GalaxyService(), "galaxy") services ! ServiceManager.Register(Props[HartService](), "hart") expectNoMessage(1000 milliseconds) var buildingMap = new TrieMap[Int, Building]() @@ -82,7 +82,7 @@ class OrbitalShuttlePadControlTest extends FreedContextActorTest { "startup and create the shuttle" in { assert(building.Amenities.size == 9) assert(vehicles.isEmpty) - pad.Actor ! Service.Startup() + pad.Actor ! Service.Startup expectNoMessage(max = 5 seconds) assert(building.Amenities.size == 10) assert(vehicles.size == 1) diff --git a/src/test/scala/objects/PlayerControlTest.scala b/src/test/scala/objects/PlayerControlTest.scala index 3c343b439..eeee7f3b5 100644 --- a/src/test/scala/objects/PlayerControlTest.scala +++ b/src/test/scala/objects/PlayerControlTest.scala @@ -24,7 +24,9 @@ import net.psforever.objects.vital.projectile.ProjectileReason import net.psforever.objects.vital.resolution.ResolutionCalculations.Output import net.psforever.packet.game._ import net.psforever.types._ -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} +import net.psforever.services.avatar.AvatarAction +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{HintsAtAttacker, PlanetsideAttribute, SendResponse} import scala.concurrent.duration._ @@ -68,9 +70,10 @@ class PlayerControlHealTest extends ActorTest { val msg_avatar = avatarProbe.receiveN(4, 500 milliseconds) assert( msg_avatar.head match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction.SendResponse(_, InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _)) + _, + SendResponse(Seq(InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _))) ) => true case _ => false @@ -78,25 +81,27 @@ class PlayerControlHealTest extends ActorTest { ) assert( msg_avatar(1) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true case _ => false } ) assert( msg_avatar(2) match { - case AvatarServiceMessage( - "TestCharacter2", - AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 55, 1) - ) => + case MessageEnvelope( + "TestCharacter2", + _, + PlanetsideAttribute(PlanetSideGUID(2), 55, 1) + ) => true case _ => false } ) assert( msg_avatar(3) match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction.SendResponse(_, RepairMessage(PlanetSideGUID(2), _)) + _, + SendResponse(Seq(RepairMessage(PlanetSideGUID(2), _))) ) => true case _ => false @@ -146,9 +151,10 @@ class PlayerControlHealSelfTest extends ActorTest { val msg_avatar1 = avatarProbe.receiveN(2, 500 milliseconds) assert( msg_avatar1.head match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction.SendResponse(_, InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _)) + _, + SendResponse(Seq(InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _))) ) => true case _ => false @@ -156,7 +162,7 @@ class PlayerControlHealSelfTest extends ActorTest { ) assert( msg_avatar1(1) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(1), 0, _)) => true case _ => false } ) @@ -169,9 +175,10 @@ class PlayerControlHealSelfTest extends ActorTest { val msg_avatar2 = avatarProbe.receiveN(2, 500 milliseconds) assert( msg_avatar2.head match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction.SendResponse(_, InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _)) + _, + SendResponse(Seq(InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _))) ) => true case _ => false @@ -179,7 +186,7 @@ class PlayerControlHealSelfTest extends ActorTest { ) assert( msg_avatar2(1) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(1), 0, _)) => true case _ => false } ) @@ -229,9 +236,10 @@ class PlayerControlRepairTest extends ActorTest { val msg_avatar = avatarProbe.receiveN(5, 1000 milliseconds) assert( msg_avatar.head match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction.SendResponse(_, InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _)) + _, + SendResponse(Seq(InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _))) ) => true case _ => false @@ -239,25 +247,27 @@ class PlayerControlRepairTest extends ActorTest { ) assert( msg_avatar(1) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 4, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 4, _)) => true case _ => false } ) assert( msg_avatar(2) match { - case AvatarServiceMessage( - "TestCharacter2", - AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 56, 1) - ) => + case MessageEnvelope( + "TestCharacter2", + _, + PlanetsideAttribute(PlanetSideGUID(2), 56, 1) + ) => true case _ => false } ) assert( msg_avatar(3) match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction.SendResponse(_, RepairMessage(PlanetSideGUID(2), _)) + _, + SendResponse(Seq(RepairMessage(PlanetSideGUID(2), _))) ) => true case _ => false @@ -265,9 +275,10 @@ class PlayerControlRepairTest extends ActorTest { ) assert( msg_avatar(4) match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter2", - AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 56, 1) + _, + PlanetsideAttribute(PlanetSideGUID(2), 56, 1) ) => true case _ => false @@ -319,9 +330,10 @@ class PlayerControlRepairSelfTest extends ActorTest { val msg_avatar1 = avatarProbe.receiveN(2, 500 milliseconds) assert( msg_avatar1.head match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction.SendResponse(_, InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _)) + _, + SendResponse(Seq(InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _))) ) => true case _ => false @@ -329,7 +341,7 @@ class PlayerControlRepairSelfTest extends ActorTest { ) assert( msg_avatar1(1) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 4, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(1), 4, _)) => true case _ => false } ) @@ -342,9 +354,10 @@ class PlayerControlRepairSelfTest extends ActorTest { val msg_avatar2 = avatarProbe.receiveN(2, 500 milliseconds) assert( msg_avatar2.head match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction.SendResponse(_, InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _)) + _, + SendResponse(Seq(InventoryStateMessage(PlanetSideGUID(4), _, PlanetSideGUID(3), _))) ) => true case _ => false @@ -352,7 +365,7 @@ class PlayerControlRepairSelfTest extends ActorTest { ) assert( msg_avatar2(1) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 4, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(1), 4, _)) => true case _ => false } ) @@ -425,7 +438,7 @@ class PlayerControlDamageTest extends ActorTest { val msg_activity = activityProbe.receiveOne(200 milliseconds) assert( msg_avatar.head match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 4, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 4, _)) => true case _ => false } ) @@ -437,7 +450,7 @@ class PlayerControlDamageTest extends ActorTest { ) assert( msg_avatar(1) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true case _ => false } ) @@ -452,9 +465,10 @@ class PlayerControlDamageTest extends ActorTest { ) assert( msg_avatar(2) match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter2", - AvatarAction.HitHint(PlanetSideGUID(1), PlanetSideGUID(2)) + PlanetSideGUID(1), + HintsAtAttacker(PlanetSideGUID(2)) ) => true case _ => false @@ -537,7 +551,7 @@ class PlayerControlDeathStandingTest extends ActorTest { activityProbe.expectNoMessage(200 milliseconds) assert( msg_avatar.head match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 4, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 4, _)) => true case _ => false } ) @@ -549,28 +563,29 @@ class PlayerControlDeathStandingTest extends ActorTest { ) assert( msg_avatar(1) match { - case AvatarServiceMessage("TestCharacter2", AvatarAction.Killed(PlanetSideGUID(2), _, None)) => true + case MessageEnvelope("TestCharacter2", _, AvatarAction.Killed(_, None)) => true case _ => false } ) assert( msg_avatar(2) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true case _ => false } ) assert( msg_avatar(3) match { - case AvatarServiceMessage("TestCharacter2", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 7, _)) => + case MessageEnvelope("TestCharacter2", _, PlanetsideAttribute(PlanetSideGUID(2), 7, _)) => true case _ => false } ) assert( msg_avatar(4) match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter2", - AvatarAction.SendResponse(_, DestroyMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _)) + _, + SendResponse(Seq(DestroyMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _))) ) => true case _ => false @@ -663,8 +678,9 @@ class PlayerControlDeathStandingTest extends ActorTest { // ) // assert( // msg_avatar.head match { -// case AvatarServiceMessage( +// case MessageEnvelope( // "TestCharacter2", +// _, // AvatarAction.Killed(PlanetSideGUID(2), _, Some(PlanetSideGUID(7))) // ) => // true @@ -673,15 +689,16 @@ class PlayerControlDeathStandingTest extends ActorTest { // ) // assert( // msg_avatar(1) match { -// case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true +// case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true // case _ => false // } // ) // assert( // msg_avatar(2) match { -// case AvatarServiceMessage( +// case MessageEnvelope( // "TestCharacter2", -// AvatarAction.SendResponse(_, DestroyMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _)) +// _, +// SendResponse(DestroyMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _)) // ) => // true // case _ => false @@ -731,8 +748,9 @@ class PlayerControlDeathStandingTest extends ActorTest { // val msg_drown = avatarProbe.receiveOne(250 milliseconds) // assert( // msg_drown match { -// case AvatarServiceMessage( +// case MessageEnvelope( // "TestCharacter1", +// _, // AvatarAction.OxygenState(OxygenStateTarget(PlanetSideGUID(1), _, OxygenState.Suffocation, 100f), _) // ) => true // case _ => false @@ -785,8 +803,9 @@ class PlayerControlDeathStandingTest extends ActorTest { // val msg_drown = avatarProbe.receiveOne(250 milliseconds) // assert( // msg_drown match { -// case AvatarServiceMessage( +// case MessageEnvelope( // "TestCharacter1", +// _, // AvatarAction.OxygenState(OxygenStateTarget(PlanetSideGUID(1), _, OxygenState.Suffocation, 100f), _) // ) => true // case _ => false @@ -798,8 +817,9 @@ class PlayerControlDeathStandingTest extends ActorTest { // val msg_recover = avatarProbe.receiveOne(250 milliseconds) // assert( // msg_recover match { -// case AvatarServiceMessage( +// case MessageEnvelope( // "TestCharacter1", +// _, // AvatarAction.OxygenState(OxygenStateTarget(PlanetSideGUID(1), _, OxygenState.Recovery, _), _) // ) => true // case _ => false @@ -851,19 +871,19 @@ class PlayerControlInteractWithLavaTest extends ActorTest { val msg_burn = avatarProbe.receiveN(3, 1 seconds) assert( msg_burn.head match { - case AvatarServiceMessage("test-map", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true + case MessageEnvelope("test-map", _, PlanetsideAttribute(PlanetSideGUID(1), 0, _)) => true case _ => false } ) assert( msg_burn(1) match { - case AvatarServiceMessage("TestCharacter1", AvatarAction.EnvironmentalDamage(PlanetSideGUID(1), _, _)) => true + case MessageEnvelope("TestCharacter1", _, AvatarAction.EnvironmentalDamage(PlanetSideGUID(1), _, _)) => true case _ => false } ) assert( msg_burn(2) match { - case AvatarServiceMessage("test-map", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 54, _)) => true + case MessageEnvelope("test-map", _, PlanetsideAttribute(PlanetSideGUID(1), 54, _)) => true case _ => false } ) diff --git a/src/test/scala/objects/RepairableTest.scala b/src/test/scala/objects/RepairableTest.scala index 3d2c6fc38..20d49812f 100644 --- a/src/test/scala/objects/RepairableTest.scala +++ b/src/test/scala/objects/RepairableTest.scala @@ -17,8 +17,9 @@ import net.psforever.objects.vehicles.control.VehicleControl import net.psforever.objects.zones.{Zone, ZoneMap} import net.psforever.packet.game.{InventoryStateMessage, RepairMessage} import net.psforever.types._ -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} +import net.psforever.services.vehicle.VehicleAction import scala.concurrent.duration._ @@ -70,10 +71,10 @@ class RepairableEntityRepairTest extends ActorTest { val msg123 = avatarProbe.receiveN(3, 500 milliseconds) assert( msg123.head match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction - .SendResponse(PlanetSideGUID(0), InventoryStateMessage(PlanetSideGUID(5), _, PlanetSideGUID(4), _)) + _, + SendResponse(Seq(InventoryStateMessage(PlanetSideGUID(5), _, PlanetSideGUID(4), _))) ) => true case _ => false @@ -81,15 +82,16 @@ class RepairableEntityRepairTest extends ActorTest { ) assert( msg123(1) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true case _ => false } ) assert( msg123(2) match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction.SendResponse(PlanetSideGUID(0), RepairMessage(PlanetSideGUID(2), _)) + _, + SendResponse(Seq(RepairMessage(PlanetSideGUID(2), _))) ) => true case _ => false @@ -187,10 +189,10 @@ class RepairableAmenityTest extends ActorTest { val msg12345 = avatarProbe.receiveN(5, 500 milliseconds) assert( msg12345.head match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction - .SendResponse(PlanetSideGUID(0), InventoryStateMessage(PlanetSideGUID(5), _, PlanetSideGUID(4), _)) + _, + SendResponse(Seq(InventoryStateMessage(PlanetSideGUID(5), _, PlanetSideGUID(4), _))) ) => true case _ => false @@ -198,27 +200,28 @@ class RepairableAmenityTest extends ActorTest { ) assert( msg12345(1) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true case _ => false } ) assert( msg12345(2) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 50, 0)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 50, 0)) => true case _ => false } ) assert( msg12345(3) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 51, 0)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 51, 0)) => true case _ => false } ) assert( msg12345(4) match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction.SendResponse(PlanetSideGUID(0), RepairMessage(PlanetSideGUID(2), _)) + _, + SendResponse(Seq(RepairMessage(PlanetSideGUID(2), _))) ) => true case _ => false @@ -285,10 +288,10 @@ class RepairableTurretWeapon extends ActorTest { val msg4 = vehicleProbe.receiveOne(500 milliseconds) assert( msg12345.head match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction - .SendResponse(PlanetSideGUID(0), InventoryStateMessage(PlanetSideGUID(8), _, PlanetSideGUID(7), _)) + _, + SendResponse(Seq(InventoryStateMessage(PlanetSideGUID(8), _, PlanetSideGUID(7), _))) ) => true case _ => false @@ -296,16 +299,17 @@ class RepairableTurretWeapon extends ActorTest { ) assert( msg12345(1) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true case _ => false } ) //msg12345(2) and msg12345(3) are related to RepairableAmenity assert( msg12345(4) match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction.SendResponse(PlanetSideGUID(0), RepairMessage(PlanetSideGUID(2), _)) + _, + SendResponse(Seq(RepairMessage(PlanetSideGUID(2), _))) ) => true case _ => false @@ -313,7 +317,7 @@ class RepairableTurretWeapon extends ActorTest { ) assert( msg4 match { - case VehicleServiceMessage("test", VehicleAction.EquipmentInSlot(_, PlanetSideGUID(2), 1, t)) + case MessageEnvelope("test", _, VehicleAction.EquipmentInSlot(PlanetSideGUID(2), 1, t)) if t eq turretWeapon => true case _ => false @@ -366,10 +370,10 @@ class RepairableVehicleRepair extends ActorTest { val msg123 = avatarProbe.receiveN(3, 500 milliseconds) assert( msg123.head match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction - .SendResponse(PlanetSideGUID(0), InventoryStateMessage(PlanetSideGUID(6), _, PlanetSideGUID(5), _)) + _, + SendResponse(Seq(InventoryStateMessage(PlanetSideGUID(6), _, PlanetSideGUID(5), _))) ) => true case _ => false @@ -377,15 +381,16 @@ class RepairableVehicleRepair extends ActorTest { ) assert( msg123(1) match { - case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true + case MessageEnvelope("test", _, PlanetsideAttribute(PlanetSideGUID(1), 0, _)) => true case _ => false } ) assert( msg123(2) match { - case AvatarServiceMessage( + case MessageEnvelope( "TestCharacter1", - AvatarAction.SendResponse(PlanetSideGUID(0), RepairMessage(PlanetSideGUID(1), _)) + _, + SendResponse(Seq(RepairMessage(PlanetSideGUID(1), _))) ) => true case _ => false diff --git a/src/test/scala/objects/ResourceSiloTest.scala b/src/test/scala/objects/ResourceSiloTest.scala index aa56739af..ef6683df6 100644 --- a/src/test/scala/objects/ResourceSiloTest.scala +++ b/src/test/scala/objects/ResourceSiloTest.scala @@ -16,8 +16,9 @@ import net.psforever.objects.zones.{Zone, ZoneMap} import net.psforever.packet.game.UseItemMessage import net.psforever.types._ import org.specs2.mutable.Specification -import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} import net.psforever.objects.avatar.Avatar +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.PlanetsideAttribute import net.psforever.services.{InterstellarClusterService, Service, ServiceManager} import net.psforever.services.galaxy.GalaxyService @@ -90,7 +91,7 @@ class ResourceSiloControlStartupTest extends ActorTest { "Resource silo" should { "startup properly" in { - obj.Actor ! Service.Startup() + obj.Actor ! Service.Startup expectNoMessage(max = 1000 milliseconds) } } @@ -112,7 +113,7 @@ class ResourceSiloControlStartupMessageNoneTest extends ActorTest { "report if it has no NTU on startup" in { obj.NtuCapacitor = 0 assert(obj.NtuCapacitor == 0) - obj.Actor ! Service.Startup() + obj.Actor ! Service.Startup val ownerMsg = buildingEvents.receiveOne(200 milliseconds) assert(ownerMsg match { case BuildingActor.NtuDepleted() => true @@ -138,7 +139,7 @@ class ResourceSiloControlStartupMessageSomeTest extends ActorTest { "report if it has any NTU on startup" in { obj.NtuCapacitor = 1 assert(obj.NtuCapacitor == 1) - obj.Actor ! Service.Startup() + obj.Actor ! Service.Startup val ownerMsg = buildingEvents.receiveOne(200 milliseconds) assert(ownerMsg match { case BuildingActor.SuppliedWithNtu() => true @@ -150,7 +151,7 @@ class ResourceSiloControlStartupMessageSomeTest extends ActorTest { class ResourceSiloControlUseTest extends FreedContextActorTest { import akka.actor.typed.scaladsl.adapter._ - ServiceManager.boot(system) ! ServiceManager.Register(Props[GalaxyService](), "galaxy") + ServiceManager.boot(system) ! ServiceManager.Register(GalaxyService(), "galaxy") expectNoMessage(1000 milliseconds) var buildingMap = new TrieMap[Int, Building]() val guid = new NumberPoolHub(new MaxNumberSource(max = 10)) @@ -196,7 +197,7 @@ class ResourceSiloControlUseTest extends FreedContextActorTest { ant.DeploymentState = DriveState.Deployed building.Amenities = silo silo.Actor = system.actorOf(Props(classOf[ResourceSiloControl], silo), "test-silo") - silo.Actor ! Service.Startup() + silo.Actor ! Service.Startup "Resource silo" should { "respond when being used" in { @@ -224,7 +225,7 @@ class ResourceSiloControlNtuWarningTest extends ActorTest { val zoneEvents: TestProbe = TestProbe("zone-events") zone.AvatarEvents = zoneEvents.ref - obj.Actor ! Service.Startup() + obj.Actor ! Service.Startup obj.Actor ! ResourceSilo.UpdateChargeLevel(-obj.NtuCapacitor) zoneEvents.receiveN(3, 500.milliseconds) //events from setup @@ -235,7 +236,7 @@ class ResourceSiloControlNtuWarningTest extends ActorTest { val reply = zoneEvents.receiveOne(5000 milliseconds) reply match { - case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(6), 47, 0)) => ; + case MessageEnvelope("nowhere", _, PlanetsideAttribute(PlanetSideGUID(6), 47, 0)) => ; case _ => assert(ResourceSiloTest.fail, s"$reply is wrong") } assert(!obj.LowNtuWarningOn) @@ -256,7 +257,7 @@ class ResourceSiloControlUpdate1Test extends ActorTest { val buildingEvents: TestProbe = TestProbe("building-events") zone.AvatarEvents = zoneEvents.ref bldg.Actor = buildingEvents.ref - obj.Actor ! Service.Startup() + obj.Actor ! Service.Startup buildingEvents.receiveOne(500 milliseconds) //message caused by "startup" obj.Actor ! ResourceSilo.UpdateChargeLevel(-obj.NtuCapacitor) zoneEvents.receiveN(3, 500.milliseconds) //events from setup @@ -274,12 +275,12 @@ class ResourceSiloControlUpdate1Test extends ActorTest { assert(obj.NtuCapacitor == 305) assert(obj.CapacitorDisplay == 3) reply1.head match { - case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(1), 45, 3)) => ; + case MessageEnvelope("nowhere", _, PlanetsideAttribute(PlanetSideGUID(1), 45, 3)) => ; case _ => assert(ResourceSiloTest.fail, s"$reply1 is wrong") } assert(reply2.isInstanceOf[BuildingActor.MapUpdate], s"$reply2 is wrong") reply1(1) match { - case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(6), 47, 0)) => ; + case MessageEnvelope("nowhere", _, PlanetsideAttribute(PlanetSideGUID(6), 47, 0)) => ; case _ => assert(ResourceSiloTest.fail, s"${reply1(1)} is wrong") } assert(!obj.LowNtuWarningOn) @@ -300,7 +301,7 @@ class ResourceSiloControlUpdate2Test extends ActorTest { val buildingEvents: TestProbe = TestProbe("building-events") zone.AvatarEvents = zoneEvents.ref bldg.Actor = buildingEvents.ref - obj.Actor ! Service.Startup() + obj.Actor ! Service.Startup buildingEvents.receiveOne(500 milliseconds) //message caused by "startup" obj.Actor ! ResourceSilo.UpdateChargeLevel(-obj.NtuCapacitor + 100) zoneEvents.receiveN(3, 500.milliseconds) //events from setup @@ -318,12 +319,12 @@ class ResourceSiloControlUpdate2Test extends ActorTest { assert(obj.NtuCapacitor == 205) assert(obj.CapacitorDisplay == 2) reply1.head match { - case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(1), 45, 2)) => ; + case MessageEnvelope("nowhere", _, PlanetsideAttribute(PlanetSideGUID(1), 45, 2)) => ; case _ => assert(ResourceSiloTest.fail, s"$reply1 is wrong") } assert(reply2.isInstanceOf[BuildingActor.MapUpdate]) reply1(1) match { - case AvatarServiceMessage("nowhere", AvatarAction.PlanetsideAttribute(PlanetSideGUID(6), 47, 0)) => ; + case MessageEnvelope("nowhere", _, PlanetsideAttribute(PlanetSideGUID(6), 47, 0)) => ; case _ => assert(ResourceSiloTest.fail, s"${reply1(1)} is wrong") } assert(!obj.LowNtuWarningOn) @@ -344,7 +345,7 @@ class ResourceSiloControlNoUpdateTest extends ActorTest { val buildingEvents: TestProbe = TestProbe("building-events") zone.AvatarEvents = zoneEvents.ref bldg.Actor = buildingEvents.ref - obj.Actor ! Service.Startup() + obj.Actor ! Service.Startup obj.NtuCapacitor = 0 "Resource silo" should { diff --git a/src/test/scala/objects/TelepadRouterTest.scala b/src/test/scala/objects/TelepadRouterTest.scala index 7eff27c37..bc43ef10d 100644 --- a/src/test/scala/objects/TelepadRouterTest.scala +++ b/src/test/scala/objects/TelepadRouterTest.scala @@ -15,7 +15,9 @@ import net.psforever.objects.vehicles.{Utility, UtilityType} import net.psforever.objects.zones.{Zone, ZoneDeployableActor, ZoneMap} import net.psforever.packet.game.objectcreate.ObjectCreateMessageParent import net.psforever.packet.game._ -import net.psforever.services.local.{LocalAction, LocalServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.SendResponse +import net.psforever.services.local.LocalAction import net.psforever.types.{DriveState, PlanetSideGUID, Vector3} import scala.collection.mutable @@ -47,16 +49,16 @@ class TelepadDeployableNoRouterTest extends ActorTest { val eventsMsgs = eventsProbe.receiveN(4, 10.seconds) eventsMsgs.head match { - case LocalServiceMessage("test", LocalAction.DeployItem(obj)) => + case MessageEnvelope("test", _, LocalAction.DeployItem(obj)) => assert(obj eq telepad, "no-router telepad deployable testt - not same telepad") case _ => assert( false, "no-router telepad deployable test - wrong deploy message") } eventsMsgs(1) match { - case LocalServiceMessage( + case MessageEnvelope( "NEUTRAL", + _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Build, DeployableInfo(PlanetSideGUID(1), DeployableIcon.RouterTelepad, Vector3.Zero, PlanetSideGUID(0)) ) @@ -64,14 +66,14 @@ class TelepadDeployableNoRouterTest extends ActorTest { case _ => assert(false, "no-router telepad deployable test - no icon or wrong icon") } eventsMsgs(2) match { - case LocalServiceMessage("test", LocalAction.EliminateDeployable(`telepad`, PlanetSideGUID(1), Vector3.Zero, 2)) => ; + case MessageEnvelope("test", _, LocalAction.EliminateDeployable(`telepad`, PlanetSideGUID(1), Vector3.Zero, 2)) => ; case _ => assert(false, "no-router telepad deployable test - not eliminating deployable") } eventsMsgs(3) match { - case LocalServiceMessage( + case MessageEnvelope( "NEUTRAL", + _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Dismiss, DeployableInfo(PlanetSideGUID(1), DeployableIcon.RouterTelepad, Vector3.Zero, PlanetSideGUID(0)) ) @@ -116,16 +118,16 @@ class TelepadDeployableNoActivationTest extends ActorTest { val eventsMsgs = eventsProbe.receiveN(4, 10.seconds) eventsMsgs.head match { - case LocalServiceMessage("test", LocalAction.DeployItem(obj)) => + case MessageEnvelope("test", _, LocalAction.DeployItem(obj)) => assert(obj eq telepad, "no-activate telepad deployable testt - not same telepad") case _ => assert( false, "no-activate telepad deployable test - wrong deploy message") } eventsMsgs(1) match { - case LocalServiceMessage( + case MessageEnvelope( "NEUTRAL", + _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Build, DeployableInfo(PlanetSideGUID(1), DeployableIcon.RouterTelepad, Vector3.Zero, PlanetSideGUID(0)) ) @@ -134,14 +136,14 @@ class TelepadDeployableNoActivationTest extends ActorTest { assert( false, "no-activate telepad deployable test - no icon or wrong icon") } eventsMsgs(2) match { - case LocalServiceMessage("test", LocalAction.EliminateDeployable(`telepad`, PlanetSideGUID(1), Vector3.Zero, 2)) => ; + case MessageEnvelope("test", _, LocalAction.EliminateDeployable(`telepad`, PlanetSideGUID(1), Vector3.Zero, 2)) => ; case _ => assert(false, "no-activate telepad deployable test - not eliminating deployable") } eventsMsgs(3) match { - case LocalServiceMessage( + case MessageEnvelope( "NEUTRAL", + _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Dismiss, DeployableInfo(PlanetSideGUID(1), DeployableIcon.RouterTelepad, Vector3.Zero, PlanetSideGUID(0)) ) @@ -189,16 +191,16 @@ class TelepadDeployableAttemptTest extends ActorTest { val eventsMsgs = eventsProbe.receiveN(2, 10.seconds) val routerMsgs = routerProbe.receiveN(1, 10.seconds) eventsMsgs.head match { - case LocalServiceMessage("test", LocalAction.DeployItem(obj)) => + case MessageEnvelope("test", _, LocalAction.DeployItem(obj)) => assert(obj eq telepad, "link attempt telepad deployable testt - not same telepad") case _ => assert( false, "link attempt telepad deployable test - wrong deploy message") } eventsMsgs(1) match { - case LocalServiceMessage( + case MessageEnvelope( "NEUTRAL", + _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Build, DeployableInfo(PlanetSideGUID(1), DeployableIcon.RouterTelepad, Vector3.Zero, PlanetSideGUID(0)) ) @@ -262,16 +264,16 @@ class TelepadDeployableResponseFromRouterTest extends FreedContextActorTest { val eventsMsgs = eventsProbe.receiveN(9, 10.seconds) eventsMsgs.head match { - case LocalServiceMessage("test", LocalAction.DeployItem(obj)) => + case MessageEnvelope("test", _, LocalAction.DeployItem(obj)) => assert(obj eq telepad, "link to router test - not same telepad") case _ => assert( false, "link to router test - wrong deploy message") } eventsMsgs(1) match { - case LocalServiceMessage( + case MessageEnvelope( "NEUTRAL", + _, LocalAction.DeployableMapIcon( - PlanetSideGUID(0), DeploymentAction.Build, DeployableInfo(PlanetSideGUID(1), DeployableIcon.RouterTelepad, Vector3.Zero, PlanetSideGUID(0)) ) @@ -279,53 +281,60 @@ class TelepadDeployableResponseFromRouterTest extends FreedContextActorTest { case _ => assert(false, "link to router test - no icon or wrong icon") } eventsMsgs(2) match { - case LocalServiceMessage( + case MessageEnvelope( "test", - LocalAction.SendResponse( + _, + SendResponse(Seq( ObjectCreateMessage(_, 744, PlanetSideGUID(3), Some(ObjectCreateMessageParent(PlanetSideGUID(2), 2)), _) - ) + )) ) => ; case _ => assert(false, "link to router test - did not create the internal router telepad (1)") } eventsMsgs(3) match { - case LocalServiceMessage( + case MessageEnvelope( "test", - LocalAction.SendResponse(GenericObjectActionMessage(PlanetSideGUID(3), 27)) + _, + SendResponse(Seq(GenericObjectActionMessage(PlanetSideGUID(3), 27))) ) => ; case _ => assert(false, "link to router test - did not create the internal router telepad (2)") } eventsMsgs(4) match { - case LocalServiceMessage( + case MessageEnvelope( "test", - LocalAction.SendResponse(GenericObjectActionMessage(PlanetSideGUID(3), 30)) + _, + SendResponse(Seq(GenericObjectActionMessage(PlanetSideGUID(3), 30))) ) => ; case _ => assert(false, "link to router test - did not create the internal router telepad (3)") } eventsMsgs(5) match { - case LocalServiceMessage( + case MessageEnvelope( "test", - LocalAction.SendResponse(GenericObjectActionMessage(PlanetSideGUID(3), 27)) + _, + SendResponse(Seq(GenericObjectActionMessage(PlanetSideGUID(3), 27))) ) => ; case _ => assert(false, "link to router test - did not link the internal telepad (1)") } eventsMsgs(6) match { - case LocalServiceMessage( + case MessageEnvelope( "test", - LocalAction.SendResponse(GenericObjectActionMessage(PlanetSideGUID(3), 28)) + _, + SendResponse(Seq(GenericObjectActionMessage(PlanetSideGUID(3), 28))) ) => ; case _ => assert(false, "link to router test - did not link the internal telepad (2)") } eventsMsgs(7) match { - case LocalServiceMessage( + case MessageEnvelope( "test", - LocalAction.SendResponse(GenericObjectActionMessage(PlanetSideGUID(1), 27)) + _, + SendResponse(Seq(GenericObjectActionMessage(PlanetSideGUID(1), 27))) ) => ; case _ => assert(false, "link to router test - did not link the telepad (1)") } eventsMsgs(8) match { - case LocalServiceMessage( + case MessageEnvelope( "test", - LocalAction.SendResponse(GenericObjectActionMessage(PlanetSideGUID(1), 28)) + _, + SendResponse(Seq(GenericObjectActionMessage(PlanetSideGUID(1), 28))) ) => ; case _ => assert(false, "link to router test - did not link the telepad (2)") } diff --git a/src/test/scala/objects/VehicleControlTest.scala b/src/test/scala/objects/VehicleControlTest.scala index 9b9b7ca79..e57ed428f 100644 --- a/src/test/scala/objects/VehicleControlTest.scala +++ b/src/test/scala/objects/VehicleControlTest.scala @@ -25,7 +25,9 @@ import net.psforever.objects.vital.{ShieldCharge, SpawningActivity, Vitality} import net.psforever.objects.zones.{Zone, ZoneMap} import net.psforever.packet.game._ import net.psforever.services.ServiceManager -import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage} +import net.psforever.services.base.envelope.MessageEnvelope +import net.psforever.services.base.message.{PlanetsideAttribute, SendResponse} +import net.psforever.services.vehicle.VehicleAction import net.psforever.types._ import scala.concurrent.duration._ @@ -72,7 +74,7 @@ class VehicleControlPrepareForDeletionPassengerTest extends ActorTest { val vehicle_msg = vehicleProbe.receiveN(1, 500 milliseconds) vehicle_msg.head match { - case VehicleServiceMessage("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}") } @@ -136,32 +138,32 @@ class VehicleControlPrepareForDeletionPassengerTest extends ActorTest { // val vehicle_msg = vehicleProbe.receiveN(6, 1 minute) // //dismounting as cargo messages // vehicle_msg.head match { -// case VehicleServiceMessage("test", VehicleAction.KickPassenger(PlanetSideGUID(3), 4, true, PlanetSideGUID(1))) => ; +// case MessageEnvelope("test", _, VehicleAction.KickPassenger(PlanetSideGUID(3), 4, true, PlanetSideGUID(1))) => ; // case _ => // assert(false, s"VehicleControlPrepareForDeletionMountedInTest-1: ${vehicle_msg.head}") // } // vehicle_msg(1) match { -// case VehicleServiceMessage(_, VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 0, _))) => ; +// case MessageEnvelope(_, _, SendResponse(PlanetsideAttributeMessage(PlanetSideGUID(1), 0, _))) => ; // case _ => // assert(false, s"VehicleControlPrepareForDeletionMountedInTest-2: ${vehicle_msg(1)}") // } // vehicle_msg(2) match { -// case VehicleServiceMessage(_, VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 68, _))) => ; +// case MessageEnvelope(_, _, SendResponse(PlanetsideAttributeMessage(PlanetSideGUID(1), 68, _))) => ; // case _ => // assert(false, s"VehicleControlPrepareForDeletionMountedInTest-3: ${vehicle_msg(2)}") // } // vehicle_msg(3) match { -// case VehicleServiceMessage("test", VehicleAction.SendResponse(_, CargoMountPointStatusMessage(PlanetSideGUID(2), _, PlanetSideGUID(1), _, 1, CargoStatus.InProgress, 0))) => ; +// case MessageEnvelope("test", _, SendResponse(CargoMountPointStatusMessage(PlanetSideGUID(2), _, PlanetSideGUID(1), _, 1, CargoStatus.InProgress, 0))) => ; // case _ => // assert(false, s"VehicleControlPrepareForDeletionMountedInTest-4: ${vehicle_msg(3)}") // } // vehicle_msg(4) match { -// case VehicleServiceMessage("test", VehicleAction.SendResponse(_, ObjectDetachMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _, _, _))) => ; +// case MessageEnvelope("test", _, SendResponse(ObjectDetachMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _, _, _))) => ; // case _ => // assert(false, s"VehicleControlPrepareForDeletionMountedInTest-5: ${vehicle_msg(4)}") // } // vehicle_msg(5) match { -// case VehicleServiceMessage("test", VehicleAction.SendResponse(_, CargoMountPointStatusMessage(PlanetSideGUID(2), _, _, PlanetSideGUID(1), 1, CargoStatus.Empty, 0))) => ; +// case MessageEnvelope("test", _, SendResponse(CargoMountPointStatusMessage(PlanetSideGUID(2), _, _, PlanetSideGUID(1), 1, CargoStatus.Empty, 0))) => ; // case _ => // assert(false, s"VehicleControlPrepareForDeletionMountedInTest-6: ${vehicle_msg(5)}") // } @@ -224,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 VehicleServiceMessage("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}") } @@ -232,27 +234,27 @@ class VehicleControlPrepareForDeletionMountedCargoTest extends FreedContextActor assert(lodestar.Seats(0).occupant.isEmpty) //cargo dismounting messages vehicleMsgs(1) match { - case VehicleServiceMessage(_, VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 0, _))) => () + case MessageEnvelope(_, _, SendResponse(Seq(PlanetsideAttributeMessage(PlanetSideGUID(1), 0, _)))) => () case _ => assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-2: ${vehicleMsgs(1)}") } vehicleMsgs(2) match { - case VehicleServiceMessage(_, VehicleAction.SendResponse(_, PlanetsideAttributeMessage(PlanetSideGUID(1), 68, _))) => () + case MessageEnvelope(_, _, SendResponse(Seq(PlanetsideAttributeMessage(PlanetSideGUID(1), 68, _)))) => () case _ => assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-3: ${vehicleMsgs(2)}") } vehicleMsgs(3) match { - case VehicleServiceMessage("test", VehicleAction.SendResponse(_, CargoMountPointStatusMessage(PlanetSideGUID(2), _, PlanetSideGUID(1), _, 1, CargoStatus.InProgress, 0))) => ; + case MessageEnvelope("test", _, SendResponse(Seq(CargoMountPointStatusMessage(PlanetSideGUID(2), _, PlanetSideGUID(1), _, 1, CargoStatus.InProgress, 0)))) => ; case _ => assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-4: ${vehicleMsgs(3)}") } vehicleMsgs(4) match { - case VehicleServiceMessage("test", VehicleAction.SendResponse(_, ObjectDetachMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _, _, _))) => () + case MessageEnvelope("test", _, SendResponse(Seq(ObjectDetachMessage(PlanetSideGUID(2), PlanetSideGUID(1), _, _, _, _)))) => () case _ => assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-5: ${vehicleMsgs(4)}") } vehicleMsgs(5) match { - case VehicleServiceMessage("test", VehicleAction.SendResponse(_, CargoMountPointStatusMessage(PlanetSideGUID(2), _, _, PlanetSideGUID(1), 1, CargoStatus.Empty, 0))) => () + case MessageEnvelope("test", _, SendResponse(Seq(CargoMountPointStatusMessage(PlanetSideGUID(2), _, _, PlanetSideGUID(1), 1, CargoStatus.Empty, 0)))) => () case _ => assert(false, s"VehicleControlPrepareForDeletionMountedCargoTest-6: ${vehicleMsgs(5)}") } @@ -476,8 +478,8 @@ class VehicleControlShieldsChargingTest extends ActorTest { vehicle.Actor ! CommonMessages.ChargeShields(15, None) val msg = probe.receiveOne(500 milliseconds) assert(msg match { - case VehicleServiceMessage(_, VehicleAction.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] })) @@ -544,7 +546,7 @@ class VehicleControlShieldsNotChargingTooEarlyTest extends ActorTest { val msg = probe.receiveOne(200 milliseconds) //assert(msg.isInstanceOf[Vehicle.UpdateShieldsCharge]) assert(msg match { - case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(_, PlanetSideGUID(10), 68, 15)) => true + case MessageEnvelope(_, _, PlanetsideAttribute(PlanetSideGUID(10), 68, 15)) => true case _ => false }) assert(vehicle.Shields == 15) @@ -1004,7 +1006,7 @@ class VehicleControlInteractWithLavaTest extends ActorTest { msg_burn.foreach { msg => assert( msg match { - case VehicleServiceMessage("test-zone", VehicleAction.PlanetsideAttribute(_, PlanetSideGUID(2), 0, _)) => true + case MessageEnvelope("test-zone", _, PlanetsideAttribute(PlanetSideGUID(2), 0, _)) => true case _ => false } ) @@ -1102,7 +1104,7 @@ class ApcControlCanChargeCapacitor extends FreedContextActorTest { do { val msg = vehicleProbe.receiveOne(3.seconds) msg match { - case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(_, PlanetSideGUID(1), 113, capacitance)) => + case MessageEnvelope(_, _, PlanetsideAttribute(PlanetSideGUID(1), 113, capacitance)) => assert(capacitance > 0) case _ => assert(false) @@ -1176,17 +1178,17 @@ class ApcControlCanEmp extends FreedContextActorTest { apc.Actor ! SpecialEmp.Burst() val vehicleMsgs = vehicleProbe.receiveN(2, 500.milliseconds) vehicleMsgs.head match { - case VehicleServiceMessage(_, VehicleAction.PlanetsideAttribute(_, PlanetSideGUID(1), 113, 0)) => ; + case MessageEnvelope(_, _, PlanetsideAttribute(PlanetSideGUID(1), 113, 0)) => ; case _ => assert(false) } vehicleMsgs(1) match { - case VehicleServiceMessage( - "test-zone", - VehicleAction.SendResponse( - _, - TriggerEffectMessage(_, "apc_explosion_emp_vs", None, Some(TriggeredEffectLocation(Vector3.Zero, Vector3.Zero))) - ) - ) => ; + case MessageEnvelope( + "test-zone", + _, + SendResponse(Seq( + TriggerEffectMessage(_, "apc_explosion_emp_vs", None, Some(TriggeredEffectLocation(Vector3.Zero, Vector3.Zero))) + )) + ) => ; case _ => assert(false) } assert(apc.Capacitor == 0) diff --git a/src/test/scala/objects/terminal/ProximityTest.scala b/src/test/scala/objects/terminal/ProximityTest.scala index f3c89992a..2d6334774 100644 --- a/src/test/scala/objects/terminal/ProximityTest.scala +++ b/src/test/scala/objects/terminal/ProximityTest.scala @@ -2,29 +2,23 @@ package objects.terminal import java.util.concurrent.atomic.AtomicInteger - import akka.actor.Props import akka.testkit.TestProbe import base.ActorTest import net.psforever.actors.zone.ZoneActor import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.structures.{Building, StructureType} -import net.psforever.objects.serverobject.terminals.{ - ProximityTerminal, - ProximityTerminalControl, - ProximityUnit, - Terminal -} +import net.psforever.objects.serverobject.terminals.{ProximityTerminal, ProximityTerminalControl, ProximityUnit, Terminal} import net.psforever.objects.zones.{Zone, ZoneMap} import net.psforever.objects.{GlobalDefinitions, Player} import net.psforever.types.{CharacterSex, CharacterVoice, PlanetSideEmpire, PlanetSideGUID} import org.specs2.mutable.Specification import net.psforever.services.Service -import net.psforever.services.local.LocalService import scala.concurrent.duration._ import akka.actor.typed.scaladsl.adapter._ import net.psforever.objects.avatar.Avatar +import net.psforever.services.base.envelope.MessageEnvelope class ProximityTest extends Specification { @@ -195,7 +189,7 @@ class ProximityTerminalControlStartTest extends ActorTest { avatar.GUID = PlanetSideGUID(1) terminal.GUID = PlanetSideGUID(2) - terminal.Actor ! Service.Startup() + terminal.Actor ! Service.Startup expectNoMessage(500 milliseconds) //spacer val probe1 = new TestProbe(system, "local-events") val probe2 = new TestProbe(system, "target-callback") @@ -206,7 +200,7 @@ class ProximityTerminalControlStartTest extends ActorTest { assert(terminal.Owner.Continent.equals("test")) terminal.Actor.tell(CommonMessages.Use(avatar, Some(avatar)), probe2.ref) - probe1.expectMsgClass(1 second, classOf[Terminal.StartProximityEffect]) + probe1.expectMsgClass(1 second, classOf[MessageEnvelope]) probe2.expectMsgClass(1 second, classOf[ProximityUnit.Action]) assert(terminal.NumberUsers == 1) } @@ -263,7 +257,7 @@ class ProximityTerminalControlTwoUsersTest extends ActorTest { avatar.GUID = PlanetSideGUID(1) avatar2.GUID = PlanetSideGUID(2) terminal.GUID = PlanetSideGUID(3) - terminal.Actor ! Service.Startup() + terminal.Actor ! Service.Startup expectNoMessage(500 milliseconds) //spacer val probe1 = new TestProbe(system, "local-events") val probe2 = new TestProbe(system, "target-callback-1") @@ -276,7 +270,7 @@ class ProximityTerminalControlTwoUsersTest extends ActorTest { assert(terminal.Owner.Continent.equals("test")) terminal.Actor.tell(CommonMessages.Use(avatar, Some(avatar)), probe2.ref) - probe1.expectMsgClass(1 second, classOf[Terminal.StartProximityEffect]) + probe1.expectMsgClass(1 second, classOf[MessageEnvelope]) probe2.expectMsgClass(5 second, classOf[ProximityUnit.Action]) terminal.Actor.tell(CommonMessages.Use(avatar2, Some(avatar2)), probe3.ref) @@ -322,7 +316,7 @@ class ProximityTerminalControlStopTest extends ActorTest { avatar.GUID = PlanetSideGUID(1) terminal.GUID = PlanetSideGUID(2) - terminal.Actor ! Service.Startup() + terminal.Actor ! Service.Startup expectNoMessage(500 milliseconds) //spacer val probe1 = new TestProbe(system, "local-events") val probe2 = new TestProbe(system, "target-callback") @@ -334,7 +328,7 @@ class ProximityTerminalControlStopTest extends ActorTest { assert(terminal.Owner.Continent.equals("test")) terminal.Actor.tell(CommonMessages.Use(avatar, Some(avatar)), probe2.ref) - probe1.expectMsgClass(1 second, classOf[Terminal.StartProximityEffect]) + probe1.expectMsgClass(1 second, classOf[MessageEnvelope]) probe2.expectMsgClass(1 second, classOf[ProximityUnit.Action]) terminal.Actor ! CommonMessages.Unuse(avatar, Some(avatar)) @@ -346,7 +340,7 @@ class ProximityTerminalControlStopTest extends ActorTest { case out => assert(false, s"last message $out is not StopAction") } //probe2.expectMsgClass(1 second, classOf[ProximityUnit.StopAction]) - probe1.expectMsgClass(1 second, classOf[Terminal.StopProximityEffect]) + probe1.expectMsgClass(1 second, classOf[MessageEnvelope]) assert(terminal.NumberUsers == 0) } } @@ -400,7 +394,7 @@ class ProximityTerminalControlNotStopTest extends ActorTest { avatar.GUID = PlanetSideGUID(1) avatar2.GUID = PlanetSideGUID(2) terminal.GUID = PlanetSideGUID(3) - terminal.Actor ! Service.Startup() + terminal.Actor ! Service.Startup expectNoMessage(500 milliseconds) //spacer val probe1 = new TestProbe(system, "local-events") val probe2 = new TestProbe(system, "target-callback-1") @@ -412,7 +406,7 @@ class ProximityTerminalControlNotStopTest extends ActorTest { assert(terminal.Owner.Continent.equals("test")) terminal.Actor.tell(CommonMessages.Use(avatar, Some(avatar)), probe2.ref) - probe1.expectMsgClass(100 millisecond, classOf[Terminal.StartProximityEffect]) + probe1.expectMsgClass(100 millisecond, classOf[MessageEnvelope]) assert(terminal.NumberUsers == 1) terminal.Actor.tell(CommonMessages.Use(avatar2, Some(avatar2)), probe3.ref) @@ -424,7 +418,7 @@ class ProximityTerminalControlNotStopTest extends ActorTest { assert(terminal.NumberUsers == 1) terminal.Actor ! CommonMessages.Unuse(avatar2, Some(avatar2)) - probe1.expectMsgClass(100 millisecond, classOf[Terminal.StopProximityEffect]) + probe1.expectMsgClass(100 millisecond, classOf[MessageEnvelope]) assert(terminal.NumberUsers == 0) } } @@ -434,9 +428,4 @@ object ProximityTest { val avatarId = new AtomicInteger(0) class SampleTerminal extends Terminal(GlobalDefinitions.dropship_vehicle_terminal) with ProximityUnit - - class ProbedLocalService(probe: TestProbe, zone: Zone) extends LocalService(zone) { - self.tell(Service.Join("test"), probe.ref) - } - } diff --git a/src/test/scala/service/HartServiceTest.scala b/src/test/scala/service/HartServiceTest.scala index 3952a8f7a..c0a4a33c3 100644 --- a/src/test/scala/service/HartServiceTest.scala +++ b/src/test/scala/service/HartServiceTest.scala @@ -3,7 +3,7 @@ package service import akka.actor.{ActorRef, Props} import akka.testkit.TestProbe -import base.ActorTest +import _root_.base.ActorTest import net.psforever.objects.zones.{Zone, ZoneMap} import net.psforever.services.hart.{HartService, HartTimer} import net.psforever.types.PlanetSideGUID diff --git a/src/test/scala/service/HartTimerTest.scala b/src/test/scala/service/HartTimerTest.scala index 038909b17..486d88dd0 100644 --- a/src/test/scala/service/HartTimerTest.scala +++ b/src/test/scala/service/HartTimerTest.scala @@ -1,9 +1,9 @@ // Copyright (c) 2021 PSForever package service -import akka.actor.Props +import akka.actor.{ActorRef, Props} import akka.testkit.TestProbe -import base.ActorTest +import _root_.base.ActorTest import net.psforever.objects.zones.{Zone, ZoneMap} import net.psforever.services.hart.HartTimer import net.psforever.types.PlanetSideGUID @@ -14,11 +14,11 @@ class HartTimerNotScheduled extends ActorTest { "HartTimer" should { val catchall = new TestProbe(system).ref val zone = new Zone("test", new ZoneMap("test"), 0) { - override def SetupNumberPools() = {} - override def AvatarEvents = catchall - override def LocalEvents = catchall - override def VehicleEvents = catchall - override def Activity = catchall + override def SetupNumberPools(): Unit = {} + override def AvatarEvents: ActorRef = catchall + override def LocalEvents: ActorRef = catchall + override def VehicleEvents: ActorRef = catchall + override def Activity: ActorRef = catchall } val timer = system.actorOf(Props(classOf[HartTimer], zone), "hart-timer") @@ -34,11 +34,11 @@ class HartTimerInitializedPairingScheduled extends ActorTest { "HartTimer" should { val catchall = new TestProbe(system).ref val zone = new Zone("test", new ZoneMap("test"), 0) { - override def SetupNumberPools() = {} - override def AvatarEvents = catchall - override def LocalEvents = catchall - override def VehicleEvents = catchall - override def Activity = catchall + override def SetupNumberPools(): Unit = {} + override def AvatarEvents: ActorRef = catchall + override def LocalEvents: ActorRef = catchall + override def VehicleEvents: ActorRef = catchall + override def Activity: ActorRef = catchall } val timer = system.actorOf(Props(classOf[HartTimer], zone), "hart-timer") @@ -54,4 +54,4 @@ class HartTimerInitializedPairingScheduled extends ActorTest { } } -object HartTimerTest { /* initially left empty */ } +object HartTimerTest { /* initially left empty */ } \ No newline at end of file diff --git a/src/test/scala/service/LocalServiceTest.scala b/src/test/scala/service/LocalServiceTest.scala deleted file mode 100644 index 4bdaac10e..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.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), LocalResponse.SendResponse(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", - LocalAction.DeployableMapIcon( - PlanetSideGUID(10), - DeploymentAction.Build, - DeployableInfo(PlanetSideGUID(40), DeployableIcon.Boomer, Vector3(1, 2, 3), PlanetSideGUID(11)) - ) - ) - expectMsg( - LocalServiceResponse( - "/test/Local", - PlanetSideGUID(10), - LocalResponse.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", LocalAction.DoorCloses(PlanetSideGUID(10), PlanetSideGUID(40))) - probe.expectMsg(LocalServiceResponse("/test/Local", PlanetSideGUID(10), LocalResponse.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", LocalAction.HackClear(PlanetSideGUID(10), obj, 0L, HackState7.Unk8)) - expectMsg( - LocalServiceResponse("/test/Local", PlanetSideGUID(10), LocalResponse.SendHackMessageHackCleared(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 ! Terminal.StartProximityEffect(terminal) - expectMsg( - LocalServiceResponse( - "/nowhere/Local", - PlanetSideGUID(0), - LocalResponse.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 ! Terminal.StopProximityEffect(terminal) - expectMsg( - LocalServiceResponse( - "/nowhere/Local", - PlanetSideGUID(0), - LocalResponse.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", - LocalAction.RouterTelepadTransport( - PlanetSideGUID(10), - PlanetSideGUID(11), - PlanetSideGUID(12), - PlanetSideGUID(13) - ) - ) - expectMsg( - LocalServiceResponse( - "/test/Local", - PlanetSideGUID(10), - LocalResponse.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", LocalAction.SetEmpire(PlanetSideGUID(10), PlanetSideEmpire.TR)) - expectMsg( - LocalServiceResponse( - "/test/Local", - PlanetSideGUID(0), - LocalResponse.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", LocalAction.ToggleTeleportSystem(PlanetSideGUID(10), router, None)) - expectMsg( - LocalServiceResponse("/test/Local", PlanetSideGUID(10), LocalResponse.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(PlanetSideGUID(10), "on", PlanetSideGUID(40))) - expectMsg( - LocalServiceResponse( - "/test/Local", - PlanetSideGUID(10), - LocalResponse.TriggerEffect(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", - LocalAction.TriggerEffectInfo(PlanetSideGUID(10), "on", PlanetSideGUID(40), true, 1000) - ) - expectMsg( - LocalServiceResponse( - "/test/Local", - PlanetSideGUID(10), - LocalResponse.TriggerEffect(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", - LocalAction.TriggerEffectLocation( - PlanetSideGUID(10), - "spawn_object_failed_effect", - Vector3(1.1f, 2.2f, 3.3f), - Vector3(4.4f, 5.5f, 6.6f) - ) - ) - expectMsg( - LocalServiceResponse( - "/test/Local", - PlanetSideGUID(10), - LocalResponse.TriggerEffect( - 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", - LocalAction.TriggerSound(PlanetSideGUID(10), TriggeredSound.LockedOut, Vector3(1.1f, 2.2f, 3.3f), 0, 0.75f) - ) - expectMsg( - LocalServiceResponse( - "/test/Local", - PlanetSideGUID(10), - LocalResponse.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 691780b58..000000000 --- a/src/test/scala/service/VehicleServiceTest.scala +++ /dev/null @@ -1,428 +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], Zone.Nowhere), "v-service") - assert(true) - } - } -} - -class VehicleService2Test extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "subscribe" in { - val service = system.actorOf(Props(classOf[VehicleService], Zone.Nowhere), "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], Zone.Nowhere), "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], Zone.Nowhere), "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], Zone.Nowhere), "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], Zone.Nowhere), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage("test", VehicleAction.Ownership(PlanetSideGUID(10), PlanetSideGUID(11))) - expectMsg( - VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.Ownership(PlanetSideGUID(11))) - ) - } - } -} - -class ChildObjectStateTest extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "pass ChildObjectState" in { - val service = system.actorOf(Props(classOf[VehicleService], Zone.Nowhere), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - VehicleAction.ChildObjectState(PlanetSideGUID(10), PlanetSideGUID(11), 1.2f, 3.4f) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleResponse.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], Zone.Nowhere), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - VehicleAction.DeployRequest( - PlanetSideGUID(10), - PlanetSideGUID(11), - DriveState.Mobile, - 0, - false, - Vector3(1.2f, 3.4f, 5.6f) - ) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleResponse.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], Zone.Nowhere), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage("test", VehicleAction.DismountVehicle(PlanetSideGUID(10), BailType.Normal, false)) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleResponse.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], Zone.Nowhere), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - VehicleAction.InventoryState(PlanetSideGUID(10), tool, PlanetSideGUID(11), 0, cdata) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleResponse.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], Zone.Nowhere), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - VehicleAction.InventoryState2(PlanetSideGUID(10), PlanetSideGUID(11), PlanetSideGUID(12), 13) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleResponse.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], Zone.Nowhere), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - VehicleAction.KickPassenger(PlanetSideGUID(10), 0, false, PlanetSideGUID(11)) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleResponse.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], Zone.Nowhere), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - VehicleAction.LoadVehicle(PlanetSideGUID(10), vehicle, 12, PlanetSideGUID(11), cdata) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleResponse.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], Zone.Nowhere), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage("test", VehicleAction.MountVehicle(PlanetSideGUID(10), PlanetSideGUID(11), 0)) - expectMsg( - VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.MountVehicle(PlanetSideGUID(11), 0)) - ) - } - } -} - -class SeatPermissionsTest extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "pass SeatPermissions" in { - val service = system.actorOf(Props(classOf[VehicleService], Zone.Nowhere), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - VehicleAction.SeatPermissions(PlanetSideGUID(10), PlanetSideGUID(11), 0, 12L) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleResponse.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], Zone.Nowhere), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - VehicleAction.StowEquipment(PlanetSideGUID(10), PlanetSideGUID(11), 0, tool) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleResponse.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], Zone.Nowhere), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage("test", VehicleAction.UnstowEquipment(PlanetSideGUID(10), PlanetSideGUID(11))) - expectMsg( - VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.UnstowEquipment(PlanetSideGUID(11))) - ) - } - } -} - -class VehicleStateTest extends ActorTest { - ServiceManager.boot(system) - - "VehicleService" should { - "pass VehicleState" in { - val service = system.actorOf(Props(classOf[VehicleService], Zone.Nowhere), "v-service") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - VehicleAction.VehicleState( - PlanetSideGUID(10), - 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), - VehicleResponse.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], Zone.Nowhere), "v-service") - val fury = Vehicle(GlobalDefinitions.fury) - fury.Actor = system.actorOf(Props(classOf[VehicleControl], fury), "test-fury") - service ! Service.Join("test") - service ! VehicleServiceMessage( - "test", - VehicleAction.TransferPassengerChannel( - PlanetSideGUID(10), - "old_channel", - "new_channel", - fury, - PlanetSideGUID(11) - ) - ) - expectMsg( - VehicleServiceResponse( - "/test/Vehicle", - PlanetSideGUID(10), - VehicleResponse.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), VehicleResponse.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..b2f11c1f5 --- /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) + 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) + 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..7a10dfcdf --- /dev/null +++ b/src/test/scala/service/base/EventServiceCacheSupportTest.scala @@ -0,0 +1,222 @@ +// Copyright (c) 2026 PSForever +package service.base + +import akka.actor.{ActorRef, ActorSystem, Props} +import akka.testkit.TestProbe +import base.ActorTest +import net.psforever.objects.Default +import net.psforever.services.Service +import net.psforever.services.base.message.EventMessage +import net.psforever.services.base.{CachedEnvelope, CachedGenericEventEnvelope, EventServiceSupport, GenericEventServiceWithCacheAndSupport, GenericSupportEnvelope} +import net.psforever.types.PlanetSideGUID + +import scala.concurrent.duration._ + +object EventServiceCacheSupportTest { + final case class CachedSupportTestEnvelope( + guid: PlanetSideGUID, + channel: String, + override val msg: EventMessage, + supportMessage: Any + ) extends CachedGenericEventEnvelope with GenericSupportEnvelope { + assert(guid != Default.GUID0, "can not cache message under default GUID") + def filter: PlanetSideGUID = Default.GUID0 + def supportLabel: String = "supportActor" + } + + class TestCacheService(eventSupportServices: List[EventServiceSupport]) + extends GenericEventServiceWithCacheAndSupport(EventServiceTestBase.TestStamp, eventSupportServices) + + def SpawnTestSystem(eventSupportServices: List[EventServiceSupport])(implicit system: ActorSystem, self: ActorRef): ActorRef = { + val name = self.getClass.getSimpleName.replace("EventServiceCacheSupportTest", "") + system.actorOf(Props(classOf[TestCacheService], eventSupportServices), name = s"EventServiceCacheSupportTest.$name") + } +} + +class EventServiceCacheSupportTestDefault extends ActorTest { + "GenericEventServiceWithCacheAndSupport" should { + "construct" in { + EventServiceCacheSupportTest.SpawnTestSystem(List()) + } + } +} + +class EventServiceCacheSupportTestSupportNormally extends ActorTest { + 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 = EventServiceCacheSupportTest.SpawnTestSystem(List(TestSupportActorLoader)) + 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 EventServiceTestBase._ + "GenericEventServiceWithCacheAndSupport" should { + "wait on sending designated cache-able messages after a few milliseconds" in { + val mainProbe = TestProbe("MainProbe") + val events = EventServiceCacheSupportTest.SpawnTestSystem(List()) + 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 EventServiceTestBase._ + "GenericEventServiceWithCacheAndSupport" should { + "send cache-able messages within a time span in bulk" in { + val mainProbe = TestProbe("MainProbe") + val events = EventServiceCacheSupportTest.SpawnTestSystem(List()) + 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 EventServiceTestBase._ + "GenericEventServiceWithCacheAndSupport" should { + "only caches and dispatches the last message with a given filtering token" in { + val mainProbe = TestProbe("MainProbe") + val events = EventServiceCacheSupportTest.SpawnTestSystem(List()) + 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 + mainProbe.expectNoMessage(50 milliseconds) + events ! secondMessage + events ! thirdMessage + 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") + } + mainProbe.expectNoMessage(150 milliseconds) + } + } +} + +class EventServiceCacheSupportTestMultipleCachedMessages extends ActorTest { + 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 = EventServiceCacheSupportTest.SpawnTestSystem(List()) + 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 = EventServiceCacheSupportTest.SpawnTestSystem(List(TestSupportActorLoader)) + 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..53d96d3c7 --- /dev/null +++ b/src/test/scala/service/base/EventServiceSupportTest.scala @@ -0,0 +1,134 @@ +// Copyright (c) 2026 PSForever +package service.base + +import akka.actor.{ActorRef, ActorSystem, 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 { + final case class TestSupportOnlyEnvelope(supportLabel: String, supportMessage: EventServiceTestBase.SupportActorRepliesWith) + extends GenericSupportEnvelopeOnly + + class TestSupportService(eventSupportServices: List[EventServiceSupport]) + extends GenericEventServiceWithSupport(EventServiceTestBase.TestStamp, eventSupportServices) + + def SpawnTestSystem(eventSupportServices: List[EventServiceSupport])(implicit system: ActorSystem, self: ActorRef): ActorRef = { + val name = self.getClass.getSimpleName.replace("EventServiceSupportTest", "") + system.actorOf(Props(classOf[TestSupportService], eventSupportServices), name = s"EventServiceSupportTest.$name") + } +} + +class EventServiceSupportTestDefault extends ActorTest { + "GenericEventServiceWithSupport" should { + "construct" in { + EventServiceSupportTest.SpawnTestSystem(List()) + } + } +} + +class EventServiceSupportTestLoadSupportClass extends ActorTest { + import EventServiceTestBase._ + "GenericEventServiceWithSupport" should { + "construct with setting up a test support class" in { + EventServiceSupportTest.SpawnTestSystem(List(TestSupportActorLoader)) + } + } +} + +class EventServiceSupportTestSendToSupportClass extends ActorTest { + 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 = EventServiceSupportTest.SpawnTestSystem(List(TestSupportActorLoader)) + 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 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 = EventServiceSupportTest.SpawnTestSystem(List(TestSupportActorLoader)) + 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 = EventServiceSupportTest.SpawnTestSystem(List(TestSupportActorLoader)) + 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 = EventServiceSupportTest.SpawnTestSystem(List(TestSupportActorLoader)) + 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..1be95949a --- /dev/null +++ b/src/test/scala/service/base/EventServiceTest.scala @@ -0,0 +1,179 @@ +// Copyright (c) 2026 PSForever +package service.base + +import akka.actor.{ActorRef, ActorSystem, Props} +import akka.testkit.TestProbe +import base.ActorTest +import net.psforever.objects.Default +import net.psforever.services.Service +import net.psforever.services.base.GenericEventService +import net.psforever.services.base.envelope.{GenericResponseEnvelope, MessageEnvelope} + +import scala.concurrent.duration._ + +object EventServiceTest { + class TestService() extends GenericEventService(EventServiceTestBase.TestStamp) + + def SpawnTestSystem()(implicit system: ActorSystem, self: ActorRef): ActorRef = { + val name = self.getClass.getSimpleName.replace("EventServiceTest", "") + system.actorOf(Props[TestService](), name = s"EventServiceTest.$name") + } +} + +class EventServiceTestDefault extends ActorTest { + "GenericEventSystem" should { + "construct" in { + EventServiceTest.SpawnTestSystem() + } + } +} + +class EventServiceTestSubscribe extends ActorTest { + "GenericEventSystem" should { + "be subscribed to by channel name" in { + val probe = TestProbe("testProbe") + val events = EventServiceTest.SpawnTestSystem() + events.tell(Service.Join("test"), probe.ref) + probe.expectNoMessage(100 milliseconds) + } + } +} + +class EventServiceTestSubscribeConfirm extends ActorTest { + "GenericEventSystem" should { + "be subscribed to by channel name and send a confirmation when prompted" in { + val probe = TestProbe("testProbe") + val events = EventServiceTest.SpawnTestSystem() + events.tell(Service.Join("test", sendJoinConfirmation = true), probe.ref) + val reply = probe.receiveOne(100 milliseconds) + assert(reply == Service.JoinConfirmation(events, "test"), "join confirmation expected but not received") + } + } +} + +class EventServiceTestSubscriptionMessage extends ActorTest { + import EventServiceTestBase._ + "GenericEventSystem" should { + "receive messages from a subscribed channel" in { + val probe = TestProbe("testProbe") + val events = EventServiceTest.SpawnTestSystem() + events.tell(Service.Join("test"), probe.ref) + events ! MessageEnvelope("test", Default.GUID0, TestMessage(5)) + probe.receiveN(1, 100 milliseconds) + } + } +} + +class EventServiceTestSubscriptionReply extends ActorTest { + import EventServiceTestBase._ + "GenericEventSystem" should { + "receive messages that are responses to the original message from a subscribed channel" in { + val probe = TestProbe("testProbe") + val events = EventServiceTest.SpawnTestSystem() + val msg = MessageEnvelope("test", Default.GUID0, TestMessage(5)) + // s => "/$s" is the default channel manipulation of the event system + val formalReply = msg.response(EventServiceTestBase.TestStamp) + events.tell(Service.Join("test"), probe.ref) + events ! MessageEnvelope("test", Default.GUID0, TestMessage(5)) + val reply = probe.receiveOne(100 milliseconds) + assert(reply == formalReply, "(1) message expected but not received format") + } + } +} + +class EventServiceTestNotSubscribed extends ActorTest { + import EventServiceTestBase._ + "GenericEventSystem" should { + "not receive messages from an unsubscribed channel" in { + val probe = TestProbe("testProbe") + val missedProbe = TestProbe("testProbe") + val events = EventServiceTest.SpawnTestSystem() + events.tell(Service.Join("test"), probe.ref) + events.tell(Service.Join("notATest"), missedProbe.ref) + events ! MessageEnvelope("test", Default.GUID0, 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 EventServiceTestUnexpectedMessages extends ActorTest { + import EventServiceTestBase._ + "GenericEventSystem" should { + "ignore unexpected messages" in { + val probe = TestProbe("testProbe") + val events = EventServiceTest.SpawnTestSystem() + events.tell(Service.Join("test"), probe.ref) + events ! TestMessage(5) + probe.expectNoMessage(250 milliseconds) + //the warn log should show something + } + } +} + +class EventServiceTestLeave extends ActorTest { + import EventServiceTestBase._ + "GenericEventSystem" should { + "leave single channels" in { + val probe = TestProbe("testProbe") + val events = EventServiceTest.SpawnTestSystem() + events.tell(Service.Join("test"), probe.ref) + events.tell(Service.Join("anotherTest"), probe.ref) + + events ! MessageEnvelope("test", Default.GUID0, TestMessage(5)) + events ! MessageEnvelope("anotherTest", Default.GUID0, 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("anotherTest"), probe.ref) + events ! MessageEnvelope("test", Default.GUID0, TestMessage(5)) + events ! MessageEnvelope("anotherTest", Default.GUID0, 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 EventServiceTestLeaveAll extends ActorTest { + import EventServiceTestBase._ + "GenericEventSystem" should { + "leave all channels (1)" in { + val probe = TestProbe("testProbe") + val events = EventServiceTest.SpawnTestSystem() + events.tell(Service.Join("test"), probe.ref) + events.tell(Service.Join("anotherTest"), probe.ref) + + events ! MessageEnvelope("test", Default.GUID0, TestMessage(5)) + events ! MessageEnvelope("anotherTest", Default.GUID0, 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", Default.GUID0, TestMessage(5)) + events ! MessageEnvelope("anotherTest", Default.GUID0, 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..81ec8b1be --- /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.objects.Default +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( + channel: String, + msg: SupportActorRepliesWith + ) extends GenericSupportEnvelope { + def filter: PlanetSideGUID = Default.GUID0 + 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..87d055260 --- /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(Default.GUID0, "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 + } + } + } +}