mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-02-06 10:31:00 +00:00
Changes to AvatarService packets and support actors in regards to corpse management and tests regarding corpse management. Seriously, those tests.
This commit is contained in:
parent
7f40b31a34
commit
f444a35785
|
|
@ -689,6 +689,17 @@ object GlobalDefinitions {
|
|||
}
|
||||
}
|
||||
|
||||
def isMaxArms(tdef : ToolDefinition) : Boolean = {
|
||||
tdef match {
|
||||
case `trhev_dualcycler` | `nchev_scattercannon` | `vshev_quasar`
|
||||
| `trhev_pounder` | `nchev_falcon` | `vshev_comet`
|
||||
| `trhev_burster` | `nchev_sparrow` | `vshev_starfire` =>
|
||||
true
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
def AIMAX(faction : PlanetSideEmpire.Value) : ToolDefinition = {
|
||||
faction match {
|
||||
case PlanetSideEmpire.TR => trhev_dualcycler
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ package net.psforever.objects.definition.converter
|
|||
import net.psforever.objects.{EquipmentSlot, Player}
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.packet.game.objectcreate.{BasicCharacterData, CharacterAppearanceData, CharacterData, DetailedCharacterData, DrawnSlot, InternalSlot, InventoryData, PlacementData, RibbonBars}
|
||||
import net.psforever.types.{CharacterGender, GrenadeState}
|
||||
import net.psforever.types.{CharacterGender, GrenadeState, Vector3}
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
|
@ -33,7 +33,7 @@ class CorpseConverter extends AvatarConverter {
|
|||
*/
|
||||
private def MakeAppearanceData(obj : Player) : CharacterAppearanceData = {
|
||||
CharacterAppearanceData(
|
||||
PlacementData(obj.Position, obj.Orientation),
|
||||
PlacementData(obj.Position, Vector3(0,0, obj.Orientation.z)),
|
||||
BasicCharacterData(obj.Name, obj.Faction, CharacterGender.Male, 0, 0),
|
||||
0,
|
||||
false,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import net.psforever.objects.serverobject.structures.{Building, FoundationBuilde
|
|||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
|
||||
import net.psforever.objects.{Avatar, GlobalDefinitions, Player, Vehicle}
|
||||
import net.psforever.objects._
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.{CharacterGender, PlanetSideEmpire, Vector3}
|
||||
import org.specs2.mutable.Specification
|
||||
|
|
@ -489,6 +489,60 @@ class ZonePopulationTest extends ActorTest {
|
|||
}
|
||||
}
|
||||
|
||||
class ZoneGroundTest extends ActorTest {
|
||||
val item = AmmoBox(GlobalDefinitions.bullet_9mm)
|
||||
item.GUID = PlanetSideGUID(10)
|
||||
|
||||
"ZoneGroundActor" should {
|
||||
"drop item on ground" in {
|
||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "drop-item-test") ! "!"
|
||||
receiveOne(Duration.create(200, "ms")) //consume
|
||||
|
||||
assert(zone.EquipmentOnGround.isEmpty)
|
||||
assert(item.Position == Vector3.Zero)
|
||||
assert(item.Orientation == Vector3.Zero)
|
||||
zone.Ground ! Zone.DropItemOnGround(item, Vector3(1.1f, 2.2f, 3.3f), Vector3(4.4f, 5.5f, 6.6f))
|
||||
expectNoMsg(Duration.create(100, "ms"))
|
||||
|
||||
assert(zone.EquipmentOnGround == List(item))
|
||||
assert(item.Position == Vector3(1.1f, 2.2f, 3.3f))
|
||||
assert(item.Orientation == Vector3(4.4f, 5.5f, 6.6f))
|
||||
}
|
||||
|
||||
"get item from ground (success)" in {
|
||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
||||
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
|
||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "get-item-test-good") ! "!"
|
||||
receiveOne(Duration.create(200, "ms")) //consume
|
||||
zone.Ground ! Zone.DropItemOnGround(item, Vector3.Zero, Vector3.Zero)
|
||||
expectNoMsg(Duration.create(100, "ms"))
|
||||
|
||||
assert(zone.EquipmentOnGround == List(item))
|
||||
zone.Ground ! Zone.GetItemOnGround(player, PlanetSideGUID(10))
|
||||
val reply = receiveOne(Duration.create(100, "ms"))
|
||||
|
||||
assert(zone.EquipmentOnGround.isEmpty)
|
||||
assert(reply.isInstanceOf[Zone.ItemFromGround])
|
||||
assert(reply.asInstanceOf[Zone.ItemFromGround].player == player)
|
||||
assert(reply.asInstanceOf[Zone.ItemFromGround].item == item)
|
||||
}
|
||||
|
||||
"get item from ground (failure)" in {
|
||||
val zone = new Zone("test", new ZoneMap(""), 0)
|
||||
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
|
||||
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "get-item-test-fail") ! "!"
|
||||
receiveOne(Duration.create(200, "ms")) //consume
|
||||
zone.Ground ! Zone.DropItemOnGround(item, Vector3.Zero, Vector3.Zero)
|
||||
expectNoMsg(Duration.create(100, "ms"))
|
||||
|
||||
assert(zone.EquipmentOnGround == List(item))
|
||||
zone.Ground ! Zone.GetItemOnGround(player, PlanetSideGUID(11)) //wrong guid
|
||||
expectNoMsg(Duration.create(500, "ms"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ZoneTest {
|
||||
class ZoneInitActor(zone : Zone) extends Actor {
|
||||
def receive : Receive = {
|
||||
|
|
|
|||
|
|
@ -37,21 +37,21 @@ object Maps {
|
|||
LocalObject(371, Door.Constructor) //courtyard
|
||||
LocalObject(372, Door.Constructor) //courtyard
|
||||
LocalObject(373, Door.Constructor) //courtyard
|
||||
LocalObject(375, Door.Constructor) //2nd level door
|
||||
LocalObject(376, Door.Constructor) //2nd level door
|
||||
LocalObject(375, Door.Constructor(Vector3(3924.0f, 4231.2656f, 271.82812f), Vector3(0, 0, 180))) //2nd level door, south
|
||||
LocalObject(376, Door.Constructor(Vector3(3924.0f, 4240.2656f, 271.82812f), Vector3(0, 0, 0))) //2nd level door, north
|
||||
LocalObject(383, Door.Constructor) //courtyard
|
||||
LocalObject(384, Door.Constructor) //3rd floor door
|
||||
LocalObject(384, Door.Constructor(Vector3(3939.6328f, 4232.547f, 279.26562f), Vector3(0, 0, 270))) //3rd floor door
|
||||
LocalObject(385, Door.Constructor) //courtyard
|
||||
LocalObject(387, Door.Constructor) //2nd level door
|
||||
LocalObject(387, Door.Constructor(Vector3(3951.9531f, 4260.008f, 271.82812f), Vector3(0, 0, 270))) //2nd level door, stairwell
|
||||
LocalObject(391, Door.Constructor) //courtyard
|
||||
LocalObject(393, Door.Constructor) //air term building, upstairs door
|
||||
LocalObject(394, Door.Constructor) //air term building, f.door
|
||||
LocalObject(393, Door.Constructor(Vector3(3997.8984f, 4344.3203f, 271.8125f), Vector3(0, 0, 0))) //air term building, upstairs door
|
||||
LocalObject(394, Door.Constructor(Vector3(3999.9766f, 4314.3203f, 266.82812f), Vector3(0, 0, 270))) //air term building, f.door
|
||||
LocalObject(396, Door.Constructor) //courtyard
|
||||
LocalObject(398, Door.Constructor) //courtyard
|
||||
LocalObject(399, Door.Constructor) //courtyard
|
||||
LocalObject(402, Door.Constructor) //courtyard
|
||||
LocalObject(403, Door.Constructor) //courtyard
|
||||
LocalObject(404, Door.Constructor) //b.door
|
||||
LocalObject(404, Door.Constructor(Vector3(4060.0078f, 4319.9766f, 266.8125f), Vector3(0, 0, 0))) //b.door
|
||||
LocalObject(603, Door.Constructor)
|
||||
LocalObject(604, Door.Constructor)
|
||||
LocalObject(605, Door.Constructor)
|
||||
|
|
@ -61,19 +61,33 @@ object Maps {
|
|||
LocalObject(611, Door.Constructor)
|
||||
LocalObject(614, Door.Constructor)
|
||||
LocalObject(619, Door.Constructor)
|
||||
LocalObject(620, Door.Constructor) //generator room door
|
||||
LocalObject(620, Door.Constructor(Vector3(3983.9531f, 4299.992f, 249.29688f), Vector3(0, 0, 90))) //generator room door
|
||||
LocalObject(621, Door.Constructor)
|
||||
LocalObject(622, Door.Constructor) //spawn room door
|
||||
LocalObject(623, Door.Constructor) //spawn room door
|
||||
LocalObject(630, Door.Constructor) //spawn room door
|
||||
LocalObject(622, Door.Constructor(Vector3(3988.0078f, 4248.0156f, 256.82812f), Vector3(0, 0, 180))) //spawn room door
|
||||
LocalObject(623, Door.Constructor(Vector3(3988.0078f, 4271.9766f, 256.79688f), Vector3(0, 0, 0))) //spawn room door
|
||||
LocalObject(630, Door.Constructor(Vector3(4000.0078f, 4252.0f, 249.29688f), Vector3(0, 0, 270))) //spawn room door
|
||||
LocalObject(631, Door.Constructor) //spawn room door, kitchen
|
||||
LocalObject(634, Door.Constructor) //air term building, interior
|
||||
LocalObject(638, Door.Constructor) //cc door
|
||||
LocalObject(642, Door.Constructor) //cc door, interior
|
||||
LocalObject(643, Door.Constructor) //cc door
|
||||
LocalObject(645, Door.Constructor) //b.door interior
|
||||
LocalObject(646, Door.Constructor) //b.door interior
|
||||
LocalObject(715, Door.Constructor) //f.door
|
||||
LocalObject(638, Door.Constructor(Vector3(4016.0078f, 4212.008f, 249.29688f), Vector3(0, 0, 270))) //cc door
|
||||
LocalObject(642, Door.Constructor(Vector3(4023.9844f, 4212.008f, 249.32812f), Vector3(0, 0, 90))) //cc door, interior
|
||||
LocalObject(643, Door.Constructor) //cc door, exterior
|
||||
LocalObject(645, Door.Constructor) //b.door, interior
|
||||
LocalObject(646, Door.Constructor) //b.door, interior
|
||||
LocalObject(715, Door.Constructor(Vector3(3961.5938f ,4235.8125f, 266.84375f), Vector3(0, 0, 90))) //f.door
|
||||
LocalObject(751, IFFLock.Constructor)
|
||||
LocalObject(860, IFFLock.Constructor)
|
||||
LocalObject(863, IFFLock.Constructor)
|
||||
LocalObject(866, IFFLock.Constructor)
|
||||
LocalObject(868, IFFLock.Constructor)
|
||||
LocalObject(873, IFFLock.Constructor)
|
||||
LocalObject(874, IFFLock.Constructor)
|
||||
LocalObject(875, IFFLock.Constructor)
|
||||
LocalObject(876, IFFLock.Constructor)
|
||||
LocalObject(878, IFFLock.Constructor)
|
||||
LocalObject(879, IFFLock.Constructor)
|
||||
LocalObject(882, IFFLock.Constructor)
|
||||
LocalObject(884, IFFLock.Constructor)
|
||||
LocalObject(885, IFFLock.Constructor)
|
||||
LocalObject(1177, Locker.Constructor)
|
||||
LocalObject(1178, Locker.Constructor)
|
||||
LocalObject(1179, Locker.Constructor)
|
||||
|
|
@ -111,7 +125,7 @@ object Maps {
|
|||
LocalObject(2324, Door.Constructor) //spawn tube door
|
||||
LocalObject(2419, Terminal.Constructor(ground_vehicle_terminal))
|
||||
LocalObject(500,
|
||||
VehicleSpawnPad.Constructor(Vector3(3962.0f, 4334.0f, 268.0f), Vector3(0f, 0f, 180.0f))
|
||||
VehicleSpawnPad.Constructor(Vector3(3962.0f, 4334.0f, 267.75f), Vector3(0f, 0f, 180.0f))
|
||||
) //TODO guid not correct
|
||||
LocalObject(224, Terminal.Constructor(dropship_vehicle_terminal))
|
||||
LocalObject(501,
|
||||
|
|
@ -160,6 +174,20 @@ object Maps {
|
|||
ObjectToBuilding(645, 2)
|
||||
ObjectToBuilding(646, 2)
|
||||
ObjectToBuilding(715, 2)
|
||||
ObjectToBuilding(751, 2)
|
||||
ObjectToBuilding(860, 2)
|
||||
ObjectToBuilding(863, 2)
|
||||
ObjectToBuilding(866, 2)
|
||||
ObjectToBuilding(868, 2)
|
||||
ObjectToBuilding(873, 2)
|
||||
ObjectToBuilding(874, 2)
|
||||
ObjectToBuilding(875, 2)
|
||||
ObjectToBuilding(876, 2)
|
||||
ObjectToBuilding(878, 2)
|
||||
ObjectToBuilding(879, 2)
|
||||
ObjectToBuilding(882, 2)
|
||||
ObjectToBuilding(884, 2)
|
||||
ObjectToBuilding(885, 2)
|
||||
ObjectToBuilding(1177, 2)
|
||||
ObjectToBuilding(1178, 2)
|
||||
ObjectToBuilding(1179, 2)
|
||||
|
|
@ -198,6 +226,20 @@ object Maps {
|
|||
ObjectToBuilding(2419, 2)
|
||||
ObjectToBuilding(500, 2)
|
||||
ObjectToBuilding(501, 2)
|
||||
DoorToLock(375, 863)
|
||||
DoorToLock(376, 860)
|
||||
DoorToLock(384, 866)
|
||||
DoorToLock(387, 868)
|
||||
DoorToLock(393, 876)
|
||||
DoorToLock(394, 879)
|
||||
DoorToLock(404, 885)
|
||||
DoorToLock(620, 873)
|
||||
DoorToLock(622, 876)
|
||||
DoorToLock(623, 874)
|
||||
DoorToLock(630, 878)
|
||||
DoorToLock(638, 882)
|
||||
DoorToLock(642, 884)
|
||||
DoorToLock(715, 751)
|
||||
TerminalToSpawnPad(224, 501)
|
||||
TerminalToSpawnPad(2419, 500)
|
||||
}
|
||||
|
|
@ -285,7 +327,7 @@ object Maps {
|
|||
|
||||
def Building49() : Unit = {
|
||||
//North Akna Air Tower
|
||||
LocalBuilding(49, FoundationBuilder(Building.Structure(StructureType.Tower, Vector3(3864.2266f, 4518.0234f, 0))))
|
||||
LocalBuilding(49, FoundationBuilder(Building.Structure(StructureType.Tower, Vector3(4358.3203f, 3989.5625f, 0))))
|
||||
LocalObject(430, Door.Constructor(Vector3(4366.0156f, 3981.9922f, 237.96875f), Vector3(0f, 0f, 180f))) //s1
|
||||
LocalObject(431, Door.Constructor(Vector3(4366.0156f, 3981.9922f, 257.89062f), Vector3(0f, 0f, 180f))) //s2
|
||||
LocalObject(432, Door.Constructor(Vector3(4366.0156f, 3997.9297f, 237.96875f), Vector3(0f, 0f, 0f))) //n1
|
||||
|
|
@ -328,6 +370,8 @@ object Maps {
|
|||
ObjectToBuilding(1591, 49)
|
||||
ObjectToBuilding(1592, 49)
|
||||
ObjectToBuilding(1593, 49)
|
||||
ObjectToBuilding(2156, 49)
|
||||
ObjectToBuilding(2157, 49)
|
||||
ObjectToBuilding(2333, 49)
|
||||
ObjectToBuilding(2334, 49)
|
||||
DoorToLock(430, 906)
|
||||
|
|
@ -359,10 +403,12 @@ object Maps {
|
|||
Building77()
|
||||
|
||||
def Building1() : Unit = {
|
||||
//warpgate?
|
||||
LocalBuilding(1, FoundationBuilder(WarpGate.Structure))
|
||||
}
|
||||
|
||||
def Building3() : Unit = {
|
||||
//warpgate?
|
||||
LocalBuilding(3, FoundationBuilder(WarpGate.Structure))
|
||||
}
|
||||
|
||||
|
|
@ -373,7 +419,8 @@ object Maps {
|
|||
// TerminalToInterface(520, 1081)
|
||||
|
||||
def Building2() : Unit = {
|
||||
LocalBuilding(2, FoundationBuilder(Building.Structure(StructureType.Building))) //HART building C
|
||||
//HART building C
|
||||
LocalBuilding(2, FoundationBuilder(Building.Structure(StructureType.Building)))
|
||||
LocalObject(186, Terminal.Constructor(cert_terminal))
|
||||
LocalObject(187, Terminal.Constructor(cert_terminal))
|
||||
LocalObject(188, Terminal.Constructor(cert_terminal))
|
||||
|
|
@ -471,7 +518,8 @@ object Maps {
|
|||
}
|
||||
|
||||
def Building29() : Unit = {
|
||||
LocalBuilding(29, FoundationBuilder(Building.Structure(StructureType.Tower))) //South Villa Gun Tower
|
||||
//South Villa Gun Tower
|
||||
LocalBuilding(29, FoundationBuilder(Building.Structure(StructureType.Tower)))
|
||||
LocalObject(330, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 91.140625f), Vector3(0, 0, 180)))
|
||||
LocalObject(331, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 111.140625f), Vector3(0, 0, 180)))
|
||||
LocalObject(332, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 91.140625f), Vector3(0, 0, 0)))
|
||||
|
|
@ -495,7 +543,8 @@ object Maps {
|
|||
}
|
||||
|
||||
def Building42() : Unit = {
|
||||
LocalBuilding(42, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(1, 0, 0)))) //spawn building south of HART C
|
||||
//spawn building south of HART C
|
||||
LocalBuilding(42, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(1, 0, 0))))
|
||||
LocalObject(258, Door.Constructor) //spawn tube door
|
||||
LocalObject(259, Door.Constructor) //spawn tube door
|
||||
LocalObject(260, Door.Constructor) //spawn tube door
|
||||
|
|
@ -510,12 +559,12 @@ object Maps {
|
|||
LocalObject(433, Door.Constructor) //vr door
|
||||
LocalObject(434, Door.Constructor) //vr door
|
||||
LocalObject(435, Door.Constructor) //vr door
|
||||
LocalObject(744, SpawnTube.Constructor(Vector3(3684.336f, 2709.0469f, 91.859375f), Vector3(0, 0, 180)))
|
||||
LocalObject(745, SpawnTube.Constructor(Vector3(3684.336f, 2713.2344f, 91.859375f), Vector3(0, 0, 0)))
|
||||
LocalObject(746, SpawnTube.Constructor(Vector3(3691.0703f, 2709.0469f, 91.859375f), Vector3(0, 0, 180)))
|
||||
LocalObject(747, SpawnTube.Constructor(Vector3(3691.0703f, 2713.2344f, 91.859375f), Vector3(0, 0, 0)))
|
||||
LocalObject(748, SpawnTube.Constructor(Vector3(3697.711f, 2709.0469f, 91.859375f), Vector3(0, 0, 180)))
|
||||
LocalObject(749, SpawnTube.Constructor(Vector3(3697.711f, 2713.2344f, 91.859375f), Vector3(0, 0, 0)))
|
||||
LocalObject(744, SpawnTube.Constructor(Vector3(3684.336f, 2709.0469f, 91.9f), Vector3(0, 0, 180)))
|
||||
LocalObject(745, SpawnTube.Constructor(Vector3(3684.336f, 2713.75f, 91.9f), Vector3(0, 0, 0)))
|
||||
LocalObject(746, SpawnTube.Constructor(Vector3(3690.9062f, 2708.4219f, 91.9f), Vector3(0, 0, 180)))
|
||||
LocalObject(747, SpawnTube.Constructor(Vector3(3691.0703f, 2713.8672f, 91.9f), Vector3(0, 0, 0)))
|
||||
LocalObject(748, SpawnTube.Constructor(Vector3(3697.664f, 2708.3984f, 91.9f), Vector3(0, 0, 180)))
|
||||
LocalObject(749, SpawnTube.Constructor(Vector3(3697.711f, 2713.2344f, 91.9f), Vector3(0, 0, 0)))
|
||||
LocalObject(852, Terminal.Constructor(order_terminal)) //s. wall
|
||||
LocalObject(853, Terminal.Constructor(order_terminal)) //n. wall
|
||||
LocalObject(854, Terminal.Constructor(order_terminal)) //s. wall
|
||||
|
|
@ -551,7 +600,8 @@ object Maps {
|
|||
}
|
||||
|
||||
def Building51() : Unit = {
|
||||
LocalBuilding(51, FoundationBuilder(Building.Structure(StructureType.Platform))) //air terminal west of HART C
|
||||
//air terminal west of HART C
|
||||
LocalBuilding(51, FoundationBuilder(Building.Structure(StructureType.Platform)))
|
||||
LocalObject(304, Terminal.Constructor(dropship_vehicle_terminal))
|
||||
LocalObject(292,
|
||||
VehicleSpawnPad.Constructor(Vector3(3508.9844f, 2895.961f, 92.296875f), Vector3(0f, 0f, 270.0f))
|
||||
|
|
@ -562,7 +612,8 @@ object Maps {
|
|||
}
|
||||
|
||||
def Building77() : Unit = {
|
||||
LocalBuilding(77, FoundationBuilder(Building.Structure(StructureType.Platform))) //ground terminal west of HART C
|
||||
//ground terminal west of HART C
|
||||
LocalBuilding(77, FoundationBuilder(Building.Structure(StructureType.Platform)))
|
||||
LocalObject(1063, Terminal.Constructor(ground_vehicle_terminal))
|
||||
LocalObject(706,
|
||||
VehicleSpawnPad.Constructor(Vector3(3506.0f, 2820.0f, 92.0f), Vector3(0f, 0f, 270.0f))
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import MDCContextAware.Implicits._
|
|||
import net.psforever.objects.GlobalDefinitions._
|
||||
import services.ServiceManager.Lookup
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.definition.ToolDefinition
|
||||
import net.psforever.objects.definition.converter.CorpseConverter
|
||||
import net.psforever.objects.equipment._
|
||||
import net.psforever.objects.guid.{GUIDTask, Task, TaskResolver}
|
||||
|
|
@ -101,8 +102,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
}
|
||||
continent.Population ! Zone.Population.Release(avatar)
|
||||
continent.Population ! Zone.Population.Leave(avatar)
|
||||
player.Position = Vector3.Zero //save character before doing this
|
||||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ObjectDelete(player_guid, player_guid))
|
||||
taskResolver ! GUIDTask. UnregisterAvatar(player)(continent.GUID)
|
||||
taskResolver ! GUIDTask.UnregisterAvatar(player)(continent.GUID)
|
||||
//TODO normally, the actual player avatar persists a minute or so after the user disconnects
|
||||
}
|
||||
}
|
||||
|
|
@ -1026,8 +1028,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
|
||||
tplayer.Position = spawn_tube.Position
|
||||
tplayer.Orientation = spawn_tube.Orientation
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
val (target, msg) : (ActorRef, Any) = if(sameZone) {
|
||||
if(backpack) {
|
||||
//respawning from unregistered player
|
||||
|
|
@ -1052,6 +1052,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
(taskResolver, TaskBeforeZoneChange(GUIDTask.UnregisterAvatar(original)(continent.GUID), zone_id))
|
||||
}
|
||||
}
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
context.system.scheduler.scheduleOnce(respawnTime seconds, target, msg)
|
||||
|
||||
case Zone.Lattice.NoValidSpawnPoint(zone_number, None) =>
|
||||
|
|
@ -1103,6 +1105,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
player = tplayer
|
||||
val guid = tplayer.GUID
|
||||
sendResponse(SetCurrentAvatarMessage(guid,0,0))
|
||||
sendResponse(PlayerStateShiftMessage(ShiftState(1, tplayer.Position, tplayer.Orientation.z)))
|
||||
if(spectator) {
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, false, "", "on", None))
|
||||
}
|
||||
|
|
@ -1300,8 +1303,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
player = new Player(avatar)
|
||||
//player.Position = Vector3(3561.0f, 2854.0f, 90.859375f) //home3, HART C
|
||||
//player.Orientation = Vector3(0f, 0f, 90f)
|
||||
player.Position = Vector3(4266.0547f, 4046.4844f, 250.23438f) //z6, Akna.tower
|
||||
player.Orientation = Vector3(0f, 0f, 320f)
|
||||
player.Position = Vector3(4262.211f ,4067.0625f ,262.35938f) //z6, Akna.tower
|
||||
player.Orientation = Vector3(0f, 0f, 132.1875f)
|
||||
// player.ExoSuit = ExoSuitType.MAX //TODO strange issue; divide number above by 10 when uncommenting
|
||||
player.Slot(0).Equipment = SimpleItem(remote_electronics_kit) //Tool(GlobalDefinitions.StandardPistol(player.Faction))
|
||||
player.Slot(2).Equipment = Tool(punisher) //suppressor
|
||||
|
|
@ -1502,11 +1505,17 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
player.VehicleSeated match {
|
||||
case None =>
|
||||
continent.Population ! Zone.Corpse.Add(player) //TODO move back out of this match case when changing below issue
|
||||
val knife = player.Slot(4).Equipment.get
|
||||
player.Slot(4).Equipment = None
|
||||
taskResolver ! RemoveEquipmentFromSlot(player, knife, 4)
|
||||
TurnPlayerIntoCorpse(player)
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.Release(player, continent))
|
||||
FriskCorpse(player)
|
||||
if(!WellLootedCorpse(player)) {
|
||||
TurnPlayerIntoCorpse(player)
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.Release(player, continent))
|
||||
}
|
||||
else { //no items in inventory; leave no corpse
|
||||
val player_guid = player.GUID
|
||||
sendResponse(ObjectDeleteMessage(player_guid, 0))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid, 0))
|
||||
taskResolver ! GUIDTask.UnregisterPlayer(player)(continent.GUID)
|
||||
}
|
||||
|
||||
case Some(_) =>
|
||||
//TODO we do not want to delete the player if he is seated in a vehicle when releasing
|
||||
|
|
@ -1514,8 +1523,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
val player_guid = player.GUID
|
||||
sendResponse(ObjectDeleteMessage(player_guid, 0))
|
||||
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid, 0))
|
||||
self ! PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, 0, true)) //let vehicle try to clean up its fields
|
||||
taskResolver ! GUIDTask.UnregisterPlayer(player)(continent.GUID)
|
||||
self ! PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, 0, true)) //let vehicle try to clean up its fields
|
||||
//sendResponse(ObjectDetachMessage(vehicle_guid, player.GUID, Vector3.Zero, 0, 0, 0))
|
||||
//sendResponse(PlayerStateShiftMessage(ShiftState(1, Vector3.Zero, 0)))
|
||||
}
|
||||
|
|
@ -1980,9 +1989,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.UnstowEquipment(player_guid, item_guid))
|
||||
vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.StowEquipment(player_guid, source_guid, index, item2))
|
||||
//TODO visible slot verification, in the case of BFR arms
|
||||
case (_ : Player) =>
|
||||
case (obj : Player) =>
|
||||
if(source.VisibleSlots.contains(index)) {
|
||||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentInHand(source_guid, index, item2))
|
||||
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentInHand(source_guid, index, item2))
|
||||
}
|
||||
case _ => ;
|
||||
//TODO something?
|
||||
|
|
@ -2168,6 +2177,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
obj.AccessingTrunk = None
|
||||
UnAccessContents(obj)
|
||||
}
|
||||
case Some(obj : Player) =>
|
||||
TryDisposeOfLootedCorpse(obj)
|
||||
|
||||
case _ =>;
|
||||
}
|
||||
|
|
@ -2772,13 +2783,19 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Before calling `Interstellar.GetWorld` to change zones, perform the following task (which can be a nesting of subtasks).
|
||||
* @param priorTask the tasks to perform
|
||||
* @param zoneId the zone to load afterwards
|
||||
* @return a `TaskResolver.GiveTask` message
|
||||
*/
|
||||
def TaskBeforeZoneChange(priorTask : TaskResolver.GiveTask, zoneId : String) : TaskResolver.GiveTask = {
|
||||
TaskResolver.GiveTask(
|
||||
new Task() {
|
||||
private val localService = galaxy
|
||||
private val localMsg = InterstellarCluster.GetWorld(zoneId)
|
||||
|
||||
override def isComplete : Task.Resolution.Value = Task.Resolution.Success
|
||||
override def isComplete : Task.Resolution.Value = priorTask.task.isComplete
|
||||
|
||||
def Execute(resolver : ActorRef) : Unit = {
|
||||
localService ! localMsg
|
||||
|
|
@ -3493,6 +3510,31 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove items from a deceased player that is not expected to be found on a corpse.
|
||||
* Most all players have their melee slot knife (which can not be un-equipped normally) removed.
|
||||
* MAX's have their primary weapon in the designated slot removed.
|
||||
* @param obj the player to be turned into a corpse
|
||||
*/
|
||||
def FriskCorpse(obj : Player) : Unit = {
|
||||
if(obj.isBackpack) {
|
||||
obj.Slot(4).Equipment match {
|
||||
case None => ;
|
||||
case Some(knife) =>
|
||||
obj.Slot(4).Equipment = None
|
||||
taskResolver ! RemoveEquipmentFromSlot(obj, knife, 4)
|
||||
}
|
||||
obj.Slot(0).Equipment match {
|
||||
case Some(arms : Tool) =>
|
||||
if(GlobalDefinitions.isMaxArms(arms.Definition)) {
|
||||
obj.Slot(0).Equipment = None
|
||||
taskResolver ! RemoveEquipmentFromSlot(obj, arms, 0)
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a player that has the characteristics of a corpse.
|
||||
* To the game, that is a backpack (or some pastry, festive graphical modification allowing).
|
||||
|
|
@ -3505,6 +3547,34 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* If the corpse has been well-looted, it has no items in its primary holsters nor any items in its inventory.
|
||||
* @param obj the corpse
|
||||
* @return `true`, if the `obj` is actually a corpse and has no objects in its holsters or backpack;
|
||||
* `false`, otherwise
|
||||
*/
|
||||
def WellLootedCorpse(obj : Player) : Boolean = {
|
||||
obj.isBackpack && obj.Holsters().count(_.Equipment.nonEmpty) == 0 && obj.Inventory.Size == 0
|
||||
}
|
||||
|
||||
/**
|
||||
* If the corpse has been well-looted, remove it from the ground.
|
||||
* @param obj the corpse
|
||||
* @return `true`, if the `obj` is actually a corpse and has no objects in its holsters or backpack;
|
||||
* `false`, otherwise
|
||||
*/
|
||||
def TryDisposeOfLootedCorpse(obj : Player) : Boolean = {
|
||||
if(WellLootedCorpse(obj)) {
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
context.system.scheduler.scheduleOnce(1 second, avatarService, AvatarServiceMessage.RemoveSpecificCorpse(List(obj)))
|
||||
true
|
||||
}
|
||||
else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to tranfer to the player's faction-specific sanctuary continent.
|
||||
* If the server thinks the player is already on his sanctuary continent,
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ object AvatarAction {
|
|||
final case class ObjectHeld(player_guid : PlanetSideGUID, slot : Int) extends Action
|
||||
final case class PlanetsideAttribute(player_guid : PlanetSideGUID, attribute_type : Int, attribute_value : Long) extends Action
|
||||
final case class PlayerState(player_guid : PlanetSideGUID, msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Action
|
||||
final case class Release(player : Player, zone : Zone) extends Action
|
||||
final case class Release(player : Player, zone : Zone, time : Option[Long] = None) extends Action
|
||||
final case class Reload(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||
final case class WeaponDryFire(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) extends Action
|
||||
// final case class PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
package services.avatar
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import services.avatar.support.UndertakerActor
|
||||
import services.avatar.support.CorpseRemovalActor
|
||||
import services.{GenericEventBus, Service}
|
||||
|
||||
class AvatarService extends Actor {
|
||||
private val undertaker : ActorRef = context.actorOf(Props[UndertakerActor], "corpse-removal-agent")
|
||||
private val undertaker : ActorRef = context.actorOf(Props[CorpseRemovalActor], "corpse-removal-agent")
|
||||
undertaker ! "startup"
|
||||
|
||||
private [this] val log = org.log4s.getLogger
|
||||
|
|
@ -90,8 +90,11 @@ class AvatarService extends Actor {
|
|||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(msg, spectator, weapon))
|
||||
)
|
||||
case AvatarAction.Release(player, zone) =>
|
||||
undertaker ! UndertakerActor.AddCorpse(player, zone)
|
||||
case AvatarAction.Release(player, zone, time) =>
|
||||
undertaker ! (time match {
|
||||
case Some(t) => CorpseRemovalActor.AddCorpse(player, zone, t)
|
||||
case None => CorpseRemovalActor.AddCorpse(player, zone)
|
||||
})
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player.GUID, AvatarResponse.Release(player))
|
||||
)
|
||||
|
|
@ -103,9 +106,14 @@ class AvatarService extends Actor {
|
|||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.WeaponDryFire(weapon_guid))
|
||||
)
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
//message to Undertaker
|
||||
case AvatarServiceMessage.RemoveSpecificCorpse(corpses) =>
|
||||
undertaker ! AvatarServiceMessage.RemoveSpecificCorpse( corpses.filter(corpse => {corpse.HasGUID && corpse.isBackpack}) )
|
||||
|
||||
/*
|
||||
case AvatarService.PlayerStateMessage(msg) =>
|
||||
// log.info(s"NEW: ${m}")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,10 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.avatar
|
||||
|
||||
import net.psforever.objects.Player
|
||||
|
||||
final case class AvatarServiceMessage(forChannel : String, actionMessage : AvatarAction.Action)
|
||||
|
||||
object AvatarServiceMessage {
|
||||
final case class RemoveSpecificCorpse(corpse : List[Player])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,199 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.avatar.support
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Cancellable}
|
||||
import net.psforever.objects.guid.TaskResolver
|
||||
import net.psforever.objects.{DefaultCancellable, Player}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.types.Vector3
|
||||
import services.{Service, ServiceManager}
|
||||
import services.ServiceManager.Lookup
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class CorpseRemovalActor extends Actor {
|
||||
private var burial : Cancellable = DefaultCancellable.obj
|
||||
|
||||
private var corpses : List[CorpseRemovalActor.Entry] = List()
|
||||
|
||||
private var taskResolver : ActorRef = Actor.noSender
|
||||
|
||||
private[this] val log = org.log4s.getLogger
|
||||
|
||||
override def postStop() = {
|
||||
//Cart Master: See you on Thursday.
|
||||
corpses.foreach { BurialTask }
|
||||
corpses = Nil
|
||||
}
|
||||
|
||||
def receive : Receive = {
|
||||
case "startup" =>
|
||||
ServiceManager.serviceManager ! Lookup("taskResolver") //ask for a resolver to deal with the GUID system
|
||||
|
||||
case ServiceManager.LookupResult("taskResolver", endpoint) =>
|
||||
//Cart Master: Bring out your dead!
|
||||
taskResolver = endpoint
|
||||
context.become(Processing)
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def Processing : Receive = {
|
||||
case CorpseRemovalActor.AddCorpse(corpse, zone, time) =>
|
||||
if(corpse.isBackpack) {
|
||||
if(corpses.isEmpty) {
|
||||
//we were the only entry so the event must be started from scratch
|
||||
corpses = List(CorpseRemovalActor.Entry(corpse, zone, time))
|
||||
RetimeFirstTask()
|
||||
}
|
||||
else {
|
||||
//unknown number of entries; append, sort, then re-time tasking
|
||||
val oldHead = corpses.head
|
||||
corpses = (corpses :+ CorpseRemovalActor.Entry(corpse, zone, time)).sortBy(_.timeAlive)
|
||||
if(oldHead != corpses.head) {
|
||||
RetimeFirstTask()
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Cart Master: 'Ere. He says he's not dead!
|
||||
log.warn(s"$corpse does not qualify as a corpse; ignored queueing request")
|
||||
}
|
||||
|
||||
case AvatarServiceMessage.RemoveSpecificCorpse(targets) =>
|
||||
if(targets.nonEmpty) {
|
||||
//Cart Master: No, I've got to go to the Robinsons'. They've lost nine today.
|
||||
burial.cancel
|
||||
if(targets.size == 1) {
|
||||
log.debug(s"a target corpse submitted for early cleanup: ${targets.head}")
|
||||
//simple selection
|
||||
CorpseRemovalActor.recursiveFindCorpse(corpses.iterator, targets.head) match {
|
||||
case None => ;
|
||||
case Some(index) =>
|
||||
BurialTask(corpses(index))
|
||||
corpses = corpses.take(index) ++ corpses.drop(index+1)
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.debug(s"multiple target corpses submitted for early cleanup: $targets")
|
||||
//cumbersome partition
|
||||
//a - find targets from corpses
|
||||
(for {
|
||||
a <- targets
|
||||
b <- corpses
|
||||
if b.corpse == a &&
|
||||
b.corpse.Continent.equals(a.Continent) &&
|
||||
b.corpse.HasGUID && a.HasGUID && b.corpse.GUID == a.GUID
|
||||
} yield b).foreach { BurialTask }
|
||||
//b - corpses after the found targets are
|
||||
//removed (note: cull any non-GUID entries while at it)
|
||||
corpses = (for {
|
||||
a <- targets
|
||||
b <- corpses
|
||||
if b.corpse.HasGUID && a.HasGUID &&
|
||||
(b.corpse != a ||
|
||||
!b.corpse.Continent.equals(a.Continent) ||
|
||||
!b.corpse.HasGUID || !a.HasGUID || b.corpse.GUID != a.GUID)
|
||||
} yield b).sortBy(_.timeAlive)
|
||||
}
|
||||
RetimeFirstTask()
|
||||
}
|
||||
|
||||
case CorpseRemovalActor.Dispose() =>
|
||||
burial.cancel
|
||||
val now : Long = System.nanoTime
|
||||
val (buried, rotting) = corpses.partition(entry => { now - entry.time >= entry.timeAlive })
|
||||
corpses = rotting
|
||||
buried.foreach { BurialTask }
|
||||
RetimeFirstTask()
|
||||
|
||||
case CorpseRemovalActor.FailureToWork(target, zone, ex) =>
|
||||
//Cart Master: Oh, I can't take him like that. It's against regulations.
|
||||
log.error(s"corpse $target from $zone not properly unregistered - $ex")
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def RetimeFirstTask(now : Long = System.nanoTime) : Unit = {
|
||||
//Cart Master: Thursday.
|
||||
burial.cancel
|
||||
if(corpses.nonEmpty) {
|
||||
val short_timeout : FiniteDuration = math.max(1, corpses.head.timeAlive - (now - corpses.head.time)) nanoseconds
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
burial = context.system.scheduler.scheduleOnce(short_timeout, self, CorpseRemovalActor.Dispose())
|
||||
}
|
||||
}
|
||||
|
||||
def BurialTask(entry : CorpseRemovalActor.Entry) : Unit = {
|
||||
//Cart master: Nine pence.
|
||||
val target = entry.corpse
|
||||
val zone = entry.zone
|
||||
target.Position = Vector3.Zero //somewhere it will not disturb anything
|
||||
entry.zone.Population ! Zone.Corpse.Remove(target)
|
||||
context.parent ! AvatarServiceMessage(zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, target.GUID))
|
||||
taskResolver ! BurialTask(target, zone)
|
||||
}
|
||||
|
||||
def BurialTask(corpse : Player, zone : Zone) : TaskResolver.GiveTask = {
|
||||
import net.psforever.objects.guid.{GUIDTask, Task}
|
||||
TaskResolver.GiveTask (
|
||||
new Task() {
|
||||
private val localCorpse = corpse
|
||||
private val localZone = zone
|
||||
private val localAnnounce = self
|
||||
|
||||
override def isComplete : Task.Resolution.Value = if(!localCorpse.HasGUID) {
|
||||
Task.Resolution.Success
|
||||
}
|
||||
else {
|
||||
Task.Resolution.Incomplete
|
||||
}
|
||||
|
||||
def Execute(resolver : ActorRef) : Unit = {
|
||||
resolver ! scala.util.Success(this)
|
||||
}
|
||||
|
||||
override def onFailure(ex : Throwable): Unit = {
|
||||
localAnnounce ! CorpseRemovalActor.FailureToWork(localCorpse, localZone, ex)
|
||||
}
|
||||
}, List(GUIDTask.UnregisterPlayer(corpse)(zone.GUID))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object CorpseRemovalActor {
|
||||
final val time : Long = 180000000000L //3 min (180s)
|
||||
|
||||
final case class AddCorpse(corpse : Player, zone : Zone, time : Long = CorpseRemovalActor.time)
|
||||
|
||||
final case class Entry(corpse : Player, zone : Zone, timeAlive : Long = CorpseRemovalActor.time, time : Long = System.nanoTime())
|
||||
|
||||
final case class FailureToWork(corpse : Player, zone : Zone, ex : Throwable)
|
||||
|
||||
final case class Dispose()
|
||||
|
||||
/**
|
||||
* A recursive function that finds and removes a specific player from a list of players.
|
||||
* @param iter an `Iterator` of `CorpseRemovalActor.Entry` objects
|
||||
* @param player the target `Player`
|
||||
* @param index the index of the discovered `Player` object
|
||||
* @return the index of the `Player` object in the list to be removed;
|
||||
* `None`, otherwise
|
||||
*/
|
||||
@tailrec final def recursiveFindCorpse(iter : Iterator[CorpseRemovalActor.Entry], player : Player, index : Int = 0) : Option[Int] = {
|
||||
if(!iter.hasNext) {
|
||||
None
|
||||
}
|
||||
else {
|
||||
val corpse = iter.next.corpse
|
||||
if(corpse == player && corpse.Continent.equals(player.Continent) && corpse.GUID == player.GUID) {
|
||||
Some(index)
|
||||
}
|
||||
else {
|
||||
recursiveFindCorpse(iter, player, index + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.avatar.support
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Cancellable}
|
||||
import net.psforever.objects.guid.TaskResolver
|
||||
import net.psforever.objects.{DefaultCancellable, Player}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import services.{Service, ServiceManager}
|
||||
import services.ServiceManager.Lookup
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class UndertakerActor extends Actor {
|
||||
private var burial : Cancellable = DefaultCancellable.obj
|
||||
|
||||
private var corpses : List[UndertakerActor.Entry] = List()
|
||||
|
||||
private var taskResolver : ActorRef = Actor.noSender
|
||||
|
||||
private[this] val log = org.log4s.getLogger("Cart Master")
|
||||
|
||||
override def postStop() = {
|
||||
corpses.foreach { BurialTask }
|
||||
}
|
||||
|
||||
def receive : Receive = {
|
||||
case "startup" =>
|
||||
ServiceManager.serviceManager ! Lookup("taskResolver") //ask for a resolver to deal with the GUID system
|
||||
|
||||
case ServiceManager.LookupResult("taskResolver", endpoint) =>
|
||||
taskResolver = endpoint
|
||||
context.become(Processing)
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def Processing : Receive = {
|
||||
case UndertakerActor.AddCorpse(corpse, zone, time) =>
|
||||
if(corpse.isBackpack) {
|
||||
corpses = corpses :+ UndertakerActor.Entry(corpse, zone, time)
|
||||
if(corpses.size == 1) { //we were the only entry so the event must be started from scratch
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
burial = context.system.scheduler.scheduleOnce(UndertakerActor.timeout, self, UndertakerActor.Dispose())
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.warn(s"he's not dead yet - $corpse")
|
||||
}
|
||||
|
||||
case UndertakerActor.Dispose() =>
|
||||
burial.cancel
|
||||
val now : Long = System.nanoTime
|
||||
val (buried, rotting) = PartitionEntries(corpses, now)
|
||||
corpses = rotting
|
||||
buried.foreach { BurialTask }
|
||||
if(rotting.nonEmpty) {
|
||||
val short_timeout : FiniteDuration = math.max(1, UndertakerActor.timeout_time - (now - rotting.head.time)) nanoseconds
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
burial = context.system.scheduler.scheduleOnce(short_timeout, self, UndertakerActor.Dispose())
|
||||
}
|
||||
|
||||
case UndertakerActor.FailureToWork(target, zone, ex) =>
|
||||
log.error(s"$target failed to be properly cleaned up from $zone - $ex")
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def BurialTask(entry : UndertakerActor.Entry) : Unit = {
|
||||
val target = entry.corpse
|
||||
val zone = entry.zone
|
||||
entry.zone.Population ! Zone.Corpse.Remove(target)
|
||||
context.parent ! AvatarServiceMessage(zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, target.GUID)) //call up to the main event system
|
||||
taskResolver ! BurialTask(target, zone)
|
||||
}
|
||||
|
||||
def BurialTask(corpse : Player, zone : Zone) : TaskResolver.GiveTask = {
|
||||
import net.psforever.objects.guid.{GUIDTask, Task}
|
||||
TaskResolver.GiveTask (
|
||||
new Task() {
|
||||
private val localCorpse = corpse
|
||||
private val localZone = zone
|
||||
private val localAnnounce = self
|
||||
|
||||
override def isComplete : Task.Resolution.Value = Task.Resolution.Success
|
||||
|
||||
def Execute(resolver : ActorRef) : Unit = {
|
||||
resolver ! scala.util.Success(this)
|
||||
}
|
||||
|
||||
override def onFailure(ex : Throwable): Unit = {
|
||||
localAnnounce ! UndertakerActor.FailureToWork(localCorpse, localZone, ex)
|
||||
}
|
||||
}, List(GUIDTask.UnregisterPlayer(corpse)(zone.GUID))
|
||||
)
|
||||
}
|
||||
|
||||
private def PartitionEntries(list : List[UndertakerActor.Entry], now : Long) : (List[UndertakerActor.Entry], List[UndertakerActor.Entry]) = {
|
||||
val n : Int = recursivePartitionEntries(list.iterator, now, UndertakerActor.timeout_time)
|
||||
(list.take(n), list.drop(n)) //take and drop so to always return new lists
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the index where the `List` of elements can be divided into two:
|
||||
* a `List` of elements that have exceeded the time limit,
|
||||
* and a `List` of elements that still satisfy the time limit.
|
||||
* @param iter the `Iterator` of entries to divide
|
||||
* @param now the time right now (in nanoseconds)
|
||||
* @param index a persistent record of the index where list division should occur;
|
||||
* defaults to 0
|
||||
* @return the index where division will occur
|
||||
*/
|
||||
@tailrec private def recursivePartitionEntries(iter : Iterator[UndertakerActor.Entry], now : Long, duration : Long, index : Int = 0) : Int = {
|
||||
if(!iter.hasNext) {
|
||||
index
|
||||
}
|
||||
else {
|
||||
val entry = iter.next()
|
||||
if(now - entry.time >= duration) {
|
||||
recursivePartitionEntries(iter, now, duration, index + 1)
|
||||
}
|
||||
else {
|
||||
index
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object UndertakerActor {
|
||||
/** A `Long` for calculation simplicity */
|
||||
private final val timeout_time : Long = 180000000000L //3 min (180s)
|
||||
/** A `FiniteDuration` for `Executor` simplicity */
|
||||
private final val timeout : FiniteDuration = timeout_time nanoseconds
|
||||
|
||||
final case class AddCorpse(corpse : Player, zone : Zone, time : Long = System.nanoTime())
|
||||
|
||||
final case class Entry(corpse : Player, zone : Zone, time : Long = System.nanoTime())
|
||||
|
||||
final case class FailureToWork(corpse : Player, zone : Zone, ex : Throwable)
|
||||
|
||||
final case class Dispose()
|
||||
|
||||
//TODO design mass disposal cases
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import net.psforever.objects.guid.TaskResolver
|
|||
import net.psforever.objects.vehicles.Seat
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.Vector3
|
||||
import services.ServiceManager
|
||||
import services.ServiceManager.Lookup
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
|
@ -80,6 +81,7 @@ class DeconstructionActor extends Actor {
|
|||
vehiclesToScrap.foreach(entry => {
|
||||
val vehicle = entry.vehicle
|
||||
val zone = entry.zone
|
||||
vehicle.Position = Vector3.Zero //somewhere it will not disturb anything
|
||||
entry.zone.Transport ! Zone.DespawnVehicle(vehicle)
|
||||
context.parent ! DeconstructionActor.DeleteVehicle(vehicle.GUID, zone.Id) //call up to the main event system
|
||||
context.parent ! VehicleServiceMessage.RevokeActorControl(vehicle) //call up to a sibling manager
|
||||
|
|
|
|||
|
|
@ -1,15 +1,21 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
import akka.actor.Props
|
||||
import akka.routing.RandomPool
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.guid.{GUIDTask, TaskResolver}
|
||||
import net.psforever.objects.zones.{Zone, ZoneActor, ZoneMap}
|
||||
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
|
||||
import net.psforever.types.{CharacterGender, ExoSuitType, PlanetSideEmpire, Vector3}
|
||||
import services.Service
|
||||
import services.{Service, ServiceManager}
|
||||
import services.avatar._
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class AvatarService1Test extends ActorTest {
|
||||
"AvatarService" should {
|
||||
"construct" in {
|
||||
system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
assert(true)
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +24,8 @@ class AvatarService1Test extends ActorTest {
|
|||
class AvatarService2Test extends ActorTest {
|
||||
"AvatarService" should {
|
||||
"subscribe" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
assert(true)
|
||||
}
|
||||
|
|
@ -27,8 +34,9 @@ class AvatarService2Test extends ActorTest {
|
|||
|
||||
class AvatarService3Test extends ActorTest {
|
||||
"AvatarService" should {
|
||||
ServiceManager.boot(system)
|
||||
"subscribe to a specific channel" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
service ! Service.Leave()
|
||||
assert(true)
|
||||
|
|
@ -39,7 +47,8 @@ class AvatarService3Test extends ActorTest {
|
|||
class AvatarService4Test extends ActorTest {
|
||||
"AvatarService" should {
|
||||
"subscribe" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
service ! Service.LeaveAll()
|
||||
assert(true)
|
||||
|
|
@ -50,7 +59,8 @@ class AvatarService4Test extends ActorTest {
|
|||
class AvatarService5Test extends ActorTest {
|
||||
"AvatarService" should {
|
||||
"pass an unhandled message" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
service ! "hello"
|
||||
expectNoMsg()
|
||||
|
|
@ -61,7 +71,8 @@ class AvatarService5Test extends ActorTest {
|
|||
class ArmorChangedTest extends ActorTest {
|
||||
"AvatarService" should {
|
||||
"pass ArmorChanged" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], 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)))
|
||||
|
|
@ -72,7 +83,8 @@ class ArmorChangedTest extends ActorTest {
|
|||
class ConcealPlayerTest extends ActorTest {
|
||||
"AvatarService" should {
|
||||
"pass ConcealPlayer" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
service ! AvatarServiceMessage("test", AvatarAction.ConcealPlayer(PlanetSideGUID(10)))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ConcealPlayer()))
|
||||
|
|
@ -85,7 +97,8 @@ class EquipmentInHandTest extends ActorTest {
|
|||
|
||||
"AvatarService" should {
|
||||
"pass EquipmentInHand" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
service ! AvatarServiceMessage("test", AvatarAction.EquipmentInHand(PlanetSideGUID(10), 2, tool))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentInHand(2, tool)))
|
||||
|
|
@ -101,7 +114,8 @@ class EquipmentOnGroundTest extends ActorTest {
|
|||
|
||||
"AvatarService" should {
|
||||
"pass EquipmentOnGround" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
service ! AvatarServiceMessage("test", AvatarAction.EquipmentOnGround(PlanetSideGUID(10), Vector3(300f, 200f, 100f), Vector3(450f, 300f, 150f), toolDef.ObjectId, PlanetSideGUID(11), cdata))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentOnGround(Vector3(300f, 200f, 100f), Vector3(450f, 300f, 150f), toolDef.ObjectId, PlanetSideGUID(11), cdata)))
|
||||
|
|
@ -117,7 +131,8 @@ class LoadPlayerTest extends ActorTest {
|
|||
|
||||
"AvatarService" should {
|
||||
"pass LoadPlayer" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
service ! AvatarServiceMessage("test", AvatarAction.LoadPlayer(PlanetSideGUID(10), pdata))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.LoadPlayer(pdata)))
|
||||
|
|
@ -128,7 +143,8 @@ class LoadPlayerTest extends ActorTest {
|
|||
class ObjectDeleteTest extends ActorTest {
|
||||
"AvatarService" should {
|
||||
"pass ObjectDelete" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], 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)))
|
||||
|
|
@ -142,7 +158,8 @@ class ObjectDeleteTest extends ActorTest {
|
|||
class ObjectHeldTest extends ActorTest {
|
||||
"AvatarService" should {
|
||||
"pass ObjectHeld" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
service ! AvatarServiceMessage("test", AvatarAction.ObjectHeld(PlanetSideGUID(10), 1))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ObjectHeld(1)))
|
||||
|
|
@ -153,7 +170,8 @@ class ObjectHeldTest extends ActorTest {
|
|||
class PlanetsideAttributeTest extends ActorTest {
|
||||
"AvatarService" should {
|
||||
"pass PlanetsideAttribute" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], 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)))
|
||||
|
|
@ -166,7 +184,8 @@ class PlayerStateTest extends ActorTest {
|
|||
|
||||
"AvatarService" should {
|
||||
"pass PlayerState" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
service ! AvatarServiceMessage("test", AvatarAction.PlayerState(PlanetSideGUID(10), msg, false, false))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.PlayerState(msg, false, false)))
|
||||
|
|
@ -177,7 +196,8 @@ class PlayerStateTest extends ActorTest {
|
|||
class ReloadTest extends ActorTest {
|
||||
"AvatarService" should {
|
||||
"pass Reload" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], 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))))
|
||||
|
|
@ -191,7 +211,8 @@ class ChangeAmmoTest extends ActorTest {
|
|||
|
||||
"AvatarService" should {
|
||||
"pass ChangeAmmo" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], 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)))
|
||||
|
|
@ -205,7 +226,8 @@ class ChangeFireModeTest extends ActorTest {
|
|||
|
||||
"AvatarService" should {
|
||||
"pass ChangeFireMode" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], 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)))
|
||||
|
|
@ -216,7 +238,8 @@ class ChangeFireModeTest extends ActorTest {
|
|||
class ChangeFireStateStartTest extends ActorTest {
|
||||
"AvatarService" should {
|
||||
"pass ChangeFireState_Start" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], 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))))
|
||||
|
|
@ -227,7 +250,8 @@ class ChangeFireStateStartTest extends ActorTest {
|
|||
class ChangeFireStateStopTest extends ActorTest {
|
||||
"AvatarService" should {
|
||||
"pass ChangeFireState_Stop" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], 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))))
|
||||
|
|
@ -238,7 +262,8 @@ class ChangeFireStateStopTest extends ActorTest {
|
|||
class WeaponDryFireTest extends ActorTest {
|
||||
"AvatarService" should {
|
||||
"pass WeaponDryFire" in {
|
||||
val service = system.actorOf(Props[AvatarService], "service")
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], 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))))
|
||||
|
|
@ -246,6 +271,177 @@ class WeaponDryFireTest extends ActorTest {
|
|||
}
|
||||
}
|
||||
|
||||
object AvatarServiceTest {
|
||||
//decoy
|
||||
/*
|
||||
Preparation for these three Release tests is involved.
|
||||
The ServiceManager must not only be set up correctly, but must be given a TaskResolver.
|
||||
The AvatarService is started and that starts CorpseRemovalActor, an essential part of this test.
|
||||
The CorpseRemovalActor needs that TaskResolver created by the ServiceManager;
|
||||
but, another independent TaskResolver will be needed for manual parts of the test.
|
||||
(The ServiceManager's TaskResolver can be "borrowed" but that requires writing code to intercept it.)
|
||||
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 ActorTest {
|
||||
ServiceManager.boot(system) ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
|
||||
val service = system.actorOf(Props[AvatarService], "release-test-service")
|
||||
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||
val taskResolver = system.actorOf(Props[TaskResolver], "release-test-resolver")
|
||||
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "release-test-zone")
|
||||
zone.Actor ! Zone.Init()
|
||||
val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
|
||||
obj.Continent = "test"
|
||||
obj.Release
|
||||
|
||||
"AvatarService" should {
|
||||
"pass Release" in {
|
||||
expectNoMsg(100 milliseconds) //spacer
|
||||
|
||||
service ! Service.Join("test")
|
||||
taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
zone.Population ! Zone.Corpse.Add(obj)
|
||||
expectNoMsg(100 milliseconds) //spacer
|
||||
|
||||
assert(zone.Corpses.size == 1)
|
||||
assert(obj.HasGUID)
|
||||
val guid = obj.GUID
|
||||
service ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone, Some(1000000000))) //alive for one second
|
||||
|
||||
val reply1 = receiveOne(100 milliseconds)
|
||||
assert(reply1.isInstanceOf[AvatarServiceResponse])
|
||||
val reply1msg = reply1.asInstanceOf[AvatarServiceResponse]
|
||||
assert(reply1msg.toChannel == "/test/Avatar")
|
||||
assert(reply1msg.avatar_guid == guid)
|
||||
assert(reply1msg.replyMessage.isInstanceOf[AvatarResponse.Release])
|
||||
assert(reply1msg.replyMessage.asInstanceOf[AvatarResponse.Release].player == obj)
|
||||
|
||||
val reply2 = receiveOne(2 seconds)
|
||||
assert(reply2.isInstanceOf[AvatarServiceResponse])
|
||||
val reply2msg = reply2.asInstanceOf[AvatarServiceResponse]
|
||||
assert(reply2msg.toChannel.equals("/test/Avatar"))
|
||||
assert(reply2msg.avatar_guid == Service.defaultPlayerGUID)
|
||||
assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete])
|
||||
assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid)
|
||||
|
||||
expectNoMsg(200 milliseconds)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
assert(!obj.HasGUID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AvatarReleaseEarly1Test extends ActorTest {
|
||||
ServiceManager.boot(system) ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
|
||||
val service = system.actorOf(Props[AvatarService], "release-test-service")
|
||||
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||
val taskResolver = system.actorOf(Props[TaskResolver], "release-test-resolver")
|
||||
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "release-test-zone")
|
||||
zone.Actor ! Zone.Init()
|
||||
val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
|
||||
obj.Continent = "test"
|
||||
obj.Release
|
||||
|
||||
"AvatarService" should {
|
||||
"pass Release" in {
|
||||
expectNoMsg(100 milliseconds) //spacer
|
||||
|
||||
service ! Service.Join("test")
|
||||
taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
zone.Population ! Zone.Corpse.Add(obj)
|
||||
expectNoMsg(100 milliseconds) //spacer
|
||||
|
||||
assert(zone.Corpses.size == 1)
|
||||
assert(obj.HasGUID)
|
||||
val guid = obj.GUID
|
||||
service ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone)) //3+ minutes!
|
||||
|
||||
val reply1 = receiveOne(100 milliseconds)
|
||||
assert(reply1.isInstanceOf[AvatarServiceResponse])
|
||||
val reply1msg = reply1.asInstanceOf[AvatarServiceResponse]
|
||||
assert(reply1msg.toChannel == "/test/Avatar")
|
||||
assert(reply1msg.avatar_guid == guid)
|
||||
assert(reply1msg.replyMessage.isInstanceOf[AvatarResponse.Release])
|
||||
assert(reply1msg.replyMessage.asInstanceOf[AvatarResponse.Release].player == obj)
|
||||
|
||||
service ! AvatarServiceMessage.RemoveSpecificCorpse(List(obj)) //IMPORTANT: ONE ENTRY
|
||||
val reply2 = receiveOne(100 milliseconds)
|
||||
assert(reply2.isInstanceOf[AvatarServiceResponse])
|
||||
val reply2msg = reply2.asInstanceOf[AvatarServiceResponse]
|
||||
assert(reply2msg.toChannel.equals("/test/Avatar"))
|
||||
assert(reply2msg.avatar_guid == Service.defaultPlayerGUID)
|
||||
assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete])
|
||||
assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid)
|
||||
|
||||
expectNoMsg(200 milliseconds)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
assert(!obj.HasGUID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AvatarReleaseEarly2Test extends ActorTest {
|
||||
ServiceManager.boot(system) ! ServiceManager.Register(RandomPool(1).props(Props[TaskResolver]), "taskResolver")
|
||||
val service = system.actorOf(Props[AvatarService], "release-test-service")
|
||||
val zone = new Zone("test", new ZoneMap("test-map"), 0)
|
||||
val taskResolver = system.actorOf(Props[TaskResolver], "release-test-resolver")
|
||||
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "release-test-zone")
|
||||
zone.Actor ! Zone.Init()
|
||||
val objAlt = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 1, 1)) //necessary clutter
|
||||
val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
|
||||
obj.Continent = "test"
|
||||
obj.Release
|
||||
|
||||
"AvatarService" should {
|
||||
"pass Release" in {
|
||||
expectNoMsg(100 milliseconds) //spacer
|
||||
|
||||
service ! Service.Join("test")
|
||||
taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
zone.Population ! Zone.Corpse.Add(obj)
|
||||
expectNoMsg(100 milliseconds) //spacer
|
||||
|
||||
assert(zone.Corpses.size == 1)
|
||||
assert(obj.HasGUID)
|
||||
val guid = obj.GUID
|
||||
service ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone)) //3+ minutes!
|
||||
|
||||
val reply1 = receiveOne(100 milliseconds)
|
||||
assert(reply1.isInstanceOf[AvatarServiceResponse])
|
||||
val reply1msg = reply1.asInstanceOf[AvatarServiceResponse]
|
||||
assert(reply1msg.toChannel == "/test/Avatar")
|
||||
assert(reply1msg.avatar_guid == guid)
|
||||
assert(reply1msg.replyMessage.isInstanceOf[AvatarResponse.Release])
|
||||
assert(reply1msg.replyMessage.asInstanceOf[AvatarResponse.Release].player == obj)
|
||||
|
||||
service ! AvatarServiceMessage.RemoveSpecificCorpse(List(objAlt, obj)) //IMPORTANT: TWO ENTRIES
|
||||
val reply2 = receiveOne(100 milliseconds)
|
||||
assert(reply2.isInstanceOf[AvatarServiceResponse])
|
||||
val reply2msg = reply2.asInstanceOf[AvatarServiceResponse]
|
||||
assert(reply2msg.toChannel.equals("/test/Avatar"))
|
||||
assert(reply2msg.avatar_guid == Service.defaultPlayerGUID)
|
||||
assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete])
|
||||
assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid)
|
||||
|
||||
expectNoMsg(200 milliseconds)
|
||||
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()}"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue