mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-03-04 04:30:21 +00:00
Merge branch 'master' into feature/CargoPassengers
# Conflicts: # common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala # common/src/main/scala/services/avatar/AvatarAction.scala # common/src/main/scala/services/avatar/AvatarResponse.scala # pslogin/src/main/scala/WorldSessionActor.scala # pslogin/src/main/scala/services/vehicle/support/DeconstructionActor.scala
This commit is contained in:
commit
2ebebb416f
135 changed files with 6071 additions and 2968 deletions
|
|
@ -9,6 +9,7 @@ import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
|||
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType, WarpGate}
|
||||
import net.psforever.objects.serverobject.terminals.{ProximityTerminal, Terminal}
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
object Maps {
|
||||
|
|
@ -125,6 +126,7 @@ object Maps {
|
|||
LocalObject(2050, Terminal.Constructor(repair_silo)) //rearm terminal A
|
||||
LocalObject(2061, ProximityTerminal.Constructor(repair_silo)) //repair terminal B
|
||||
LocalObject(2062, Terminal.Constructor(repair_silo)) //rearm terminal B
|
||||
LocalObject(2094, ResourceSilo.Constructor) // NTU Silo
|
||||
LocalObject(2239, Terminal.Constructor(spawn_terminal))
|
||||
LocalObject(2244, Terminal.Constructor(spawn_terminal))
|
||||
LocalObject(2245, Terminal.Constructor(spawn_terminal))
|
||||
|
|
@ -238,6 +240,7 @@ object Maps {
|
|||
ObjectToBuilding(2050, 2)
|
||||
ObjectToBuilding(2061, 2)
|
||||
ObjectToBuilding(2062, 2)
|
||||
ObjectToBuilding(2094, 2)
|
||||
ObjectToBuilding(2145, 2)
|
||||
ObjectToBuilding(2146, 2)
|
||||
ObjectToBuilding(2147, 2)
|
||||
|
|
@ -473,6 +476,7 @@ object Maps {
|
|||
LocalObject(396, Door.Constructor)
|
||||
LocalObject(397, Door.Constructor)
|
||||
LocalObject(398, Door.Constructor)
|
||||
LocalObject(399, Door.Constructor)
|
||||
LocalObject(462, Door.Constructor)
|
||||
LocalObject(463, Door.Constructor)
|
||||
LocalObject(522, ImplantTerminalMech.Constructor)
|
||||
|
|
@ -520,6 +524,7 @@ object Maps {
|
|||
ObjectToBuilding(396, 2)
|
||||
ObjectToBuilding(397, 2)
|
||||
ObjectToBuilding(398, 2)
|
||||
ObjectToBuilding(399, 2)
|
||||
ObjectToBuilding(462, 2)
|
||||
ObjectToBuilding(463, 2)
|
||||
ObjectToBuilding(522, 2)
|
||||
|
|
|
|||
|
|
@ -331,8 +331,8 @@ class PacketCodingActor extends Actor with MDCContextAware {
|
|||
|
||||
/**
|
||||
* Accept a series of packets and transform it into a series of packet encodings.
|
||||
* Packets that do not encode properly are simply excluded for the product.
|
||||
* This is not treated as an error or exception; a warning will mrely be logged.
|
||||
* Packets that do not encode properly are simply excluded from the product.
|
||||
* This is not treated as an error or exception; a warning will merely be logged.
|
||||
* @param iter the `Iterator` for a series of packets
|
||||
* @param out updated series of byte stream data produced through successful packet encoding;
|
||||
* defaults to an empty list
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import org.fusesource.jansi.Ansi._
|
|||
import org.fusesource.jansi.Ansi.Color._
|
||||
import services.ServiceManager
|
||||
import services.avatar._
|
||||
import services.galaxy.GalaxyService
|
||||
import services.local._
|
||||
import services.vehicle.VehicleService
|
||||
|
||||
|
|
@ -209,7 +210,8 @@ object PsLogin {
|
|||
serviceManager ! ServiceManager.Register(Props[AvatarService], "avatar")
|
||||
serviceManager ! ServiceManager.Register(Props[LocalService], "local")
|
||||
serviceManager ! ServiceManager.Register(Props[VehicleService], "vehicle")
|
||||
serviceManager ! ServiceManager.Register(Props(classOf[InterstellarCluster], continentList), "galaxy")
|
||||
serviceManager ! ServiceManager.Register(Props[GalaxyService], "galaxy")
|
||||
serviceManager ! ServiceManager.Register(Props(classOf[InterstellarCluster], continentList), "cluster")
|
||||
|
||||
//attach event bus entry point to each zone
|
||||
import akka.pattern.ask
|
||||
|
|
@ -217,7 +219,7 @@ object PsLogin {
|
|||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
import scala.util.{Failure, Success}
|
||||
implicit val timeout = Timeout(200 milliseconds)
|
||||
implicit val timeout = Timeout(500 milliseconds)
|
||||
val requestVehicleEventBus : Future[ServiceManager.LookupResult] =
|
||||
(ServiceManager.serviceManager ask ServiceManager.Lookup("vehicle")).mapTo[ServiceManager.LookupResult]
|
||||
requestVehicleEventBus.onComplete {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,34 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services
|
||||
|
||||
import akka.event.{ActorEventBus, SubchannelClassification}
|
||||
import akka.util.Subclassification
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
||||
object Service {
|
||||
final val defaultPlayerGUID : PlanetSideGUID = PlanetSideGUID(0)
|
||||
|
||||
final case class Join(channel : String)
|
||||
final case class Leave(channel : Option[String] = None)
|
||||
final case class LeaveAll()
|
||||
}
|
||||
|
||||
trait GenericEventBusMsg {
|
||||
def toChannel : String
|
||||
}
|
||||
|
||||
class GenericEventBus[A <: GenericEventBusMsg] extends ActorEventBus with SubchannelClassification {
|
||||
type Event = A
|
||||
type Classifier = String
|
||||
|
||||
protected def classify(event: Event): Classifier = event.toChannel
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
package services
|
||||
|
||||
// Copyright (c) 2017 PSForever
|
||||
import akka.actor.{Actor, ActorIdentity, ActorRef, ActorSystem, Identify, Props}
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
object ServiceManager {
|
||||
var serviceManager = Actor.noSender
|
||||
|
||||
def boot(implicit system : ActorSystem) = {
|
||||
serviceManager = system.actorOf(Props[ServiceManager], "service")
|
||||
serviceManager
|
||||
}
|
||||
|
||||
case class Register(props : Props, name : String)
|
||||
case class Lookup(name : String)
|
||||
case class LookupResult(request : String, endpoint : ActorRef)
|
||||
}
|
||||
|
||||
class ServiceManager extends Actor {
|
||||
import ServiceManager._
|
||||
private [this] val log = org.log4s.getLogger
|
||||
|
||||
var nextLookupId : Long = 0
|
||||
val lookups : mutable.LongMap[RequestEntry] = mutable.LongMap()
|
||||
|
||||
override def preStart = {
|
||||
log.info("Starting...")
|
||||
}
|
||||
|
||||
def receive = {
|
||||
case Register(props, name) =>
|
||||
log.info(s"Registered $name service")
|
||||
context.actorOf(props, name)
|
||||
case Lookup(name) =>
|
||||
context.actorSelection(name) ! Identify(nextLookupId)
|
||||
lookups += nextLookupId -> RequestEntry(name, sender())
|
||||
nextLookupId += 1
|
||||
|
||||
case ActorIdentity(id, Some(ref)) =>
|
||||
val idNumber = id.asInstanceOf[Long]
|
||||
lookups.get(idNumber) match {
|
||||
case Some(RequestEntry(name, sender)) =>
|
||||
sender ! LookupResult(name, ref)
|
||||
lookups.remove(idNumber)
|
||||
case _ =>
|
||||
//TODO something
|
||||
}
|
||||
|
||||
case ActorIdentity(id, None) =>
|
||||
val idNumber = id.asInstanceOf[Long]
|
||||
lookups.get(idNumber) match {
|
||||
case Some(RequestEntry(name, _)) =>
|
||||
log.error(s"request #$idNumber for service `$name` came back empty; it may not exist")
|
||||
lookups.remove(idNumber)
|
||||
case _ =>
|
||||
//TODO something
|
||||
}
|
||||
|
||||
case default =>
|
||||
log.error(s"invalid message received - $default")
|
||||
}
|
||||
|
||||
protected case class RequestEntry(request : String, responder : ActorRef)
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.avatar
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.{CargoMountPointStatusMessage, PlanetSideGUID, PlayerStateMessageUpstream}
|
||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||
import net.psforever.types.{ExoSuitType, Vector3}
|
||||
|
||||
object AvatarAction {
|
||||
trait Action
|
||||
|
||||
final case class ArmorChanged(player_guid : PlanetSideGUID, suit : ExoSuitType.Value, subtype : 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 EquipmentInHand(player_guid : PlanetSideGUID, target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
|
||||
final case class EquipmentOnGround(player_guid : PlanetSideGUID, pos : Vector3, orient : Vector3, item_id : Int, item_guid : PlanetSideGUID, item_data : ConstructorData) extends Action
|
||||
final case class LoadPlayer(player_guid : PlanetSideGUID, pdata : ConstructorData) extends Action
|
||||
// final case class LoadMap(msg : PlanetSideGUID) extends Action
|
||||
// final case class unLoadMap(msg : PlanetSideGUID) 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) 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, time : Option[Long] = None) extends Action
|
||||
final case class Reload(player_guid : PlanetSideGUID, weapon_guid : PlanetSideGUID) 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 PlayerStateShift(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
||||
// final case class DestroyDisplay(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
||||
// final case class HitHintReturn(killer : PlanetSideGUID, victim : PlanetSideGUID) extends Action
|
||||
// final case class ChangeWeapon(unk1 : Int, sessionId : Long) extends Action
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.avatar
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
|
||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||
import net.psforever.types.{ExoSuitType, Vector3}
|
||||
|
||||
object AvatarResponse {
|
||||
trait Response
|
||||
|
||||
final case class ArmorChanged(suit : ExoSuitType.Value, subtype : 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 EquipmentInHand(target_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Response
|
||||
final case class EquipmentOnGround(pos : Vector3, orient : Vector3, item_id : Int, item_guid : PlanetSideGUID, item_data : ConstructorData) extends Response
|
||||
final case class LoadPlayer(pdata : ConstructorData) extends Response
|
||||
// final case class unLoadMap() extends Response
|
||||
// final case class LoadMap() extends Response
|
||||
final case class ObjectDelete(item_guid : PlanetSideGUID, unk : Int) extends Response
|
||||
final case class ObjectHeld(slot : Int) extends Response
|
||||
final case class PlanetsideAttribute(attribute_type : Int, attribute_value : Long) extends Response
|
||||
final case class PlayerState(msg : PlayerStateMessageUpstream, spectator : Boolean, weaponInHand : Boolean) extends Response
|
||||
final case class Release(player : Player) extends Response
|
||||
final case class Reload(weapon_guid : PlanetSideGUID) 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 PlayerStateShift(itemID : PlanetSideGUID) extends Response
|
||||
// final case class DestroyDisplay(itemID : PlanetSideGUID) extends Response
|
||||
// final case class HitHintReturn(itemID : PlanetSideGUID) extends Response
|
||||
// final case class ChangeWeapon(facingYaw : Int) extends Response
|
||||
}
|
||||
|
|
@ -1,204 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.avatar
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import services.avatar.support.CorpseRemovalActor
|
||||
import services.{GenericEventBus, Service}
|
||||
|
||||
class AvatarService extends Actor {
|
||||
private val undertaker : ActorRef = context.actorOf(Props[CorpseRemovalActor], "corpse-removal-agent")
|
||||
undertaker ! "startup"
|
||||
|
||||
private [this] val log = org.log4s.getLogger
|
||||
|
||||
override def preStart = {
|
||||
log.info("Starting...")
|
||||
}
|
||||
|
||||
val AvatarEvents = new GenericEventBus[AvatarServiceResponse] //AvatarEventBus
|
||||
|
||||
def receive = {
|
||||
case Service.Join(channel) =>
|
||||
val path = s"/$channel/Avatar"
|
||||
val who = sender()
|
||||
log.info(s"$who has joined $path")
|
||||
AvatarEvents.subscribe(who, path)
|
||||
|
||||
case Service.Leave(None) =>
|
||||
AvatarEvents.unsubscribe(sender())
|
||||
|
||||
case Service.Leave(Some(channel)) =>
|
||||
val path = s"/$channel/Avatar"
|
||||
val who = sender()
|
||||
log.info(s"$who has left $path")
|
||||
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.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.EquipmentInHand(player_guid, target_guid, slot, obj) =>
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.EquipmentInHand(target_guid, slot, obj))
|
||||
)
|
||||
case AvatarAction.EquipmentOnGround(player_guid, pos, orient, item_id, item_guid, item_data) =>
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.EquipmentOnGround(pos, orient, item_id, item_guid, item_data))
|
||||
)
|
||||
case AvatarAction.LoadPlayer(player_guid, pdata) =>
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.LoadPlayer(pdata))
|
||||
)
|
||||
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) =>
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.ObjectHeld(slot))
|
||||
)
|
||||
case AvatarAction.PlanetsideAttribute(guid, attribute_type, attribute_value) =>
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlanetsideAttribute(attribute_type, attribute_value))
|
||||
)
|
||||
case AvatarAction.PlayerState(guid, msg, spectator, weapon) =>
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", guid, AvatarResponse.PlayerState(msg, spectator, weapon))
|
||||
)
|
||||
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))
|
||||
)
|
||||
case AvatarAction.Reload(player_guid, weapon_guid) =>
|
||||
AvatarEvents.publish(
|
||||
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarResponse.Reload(weapon_guid))
|
||||
)
|
||||
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 _ => ;
|
||||
}
|
||||
|
||||
//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}")
|
||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(msg.avatar_guid)
|
||||
if (playerOpt.isDefined) {
|
||||
val player: PlayerAvatar = playerOpt.get
|
||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, msg.avatar_guid,
|
||||
AvatarServiceReply.PlayerStateMessage(msg.pos, msg.vel, msg.facingYaw, msg.facingPitch, msg.facingYawUpper, msg.is_crouching, msg.is_jumping, msg.jump_thrust, msg.is_cloaked)
|
||||
))
|
||||
|
||||
}
|
||||
case AvatarService.LoadMap(msg) =>
|
||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(msg.guid)
|
||||
if (playerOpt.isDefined) {
|
||||
val player: PlayerAvatar = playerOpt.get
|
||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, PlanetSideGUID(msg.guid),
|
||||
AvatarServiceReply.LoadMap()
|
||||
))
|
||||
}
|
||||
case AvatarService.unLoadMap(msg) =>
|
||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(msg.guid)
|
||||
if (playerOpt.isDefined) {
|
||||
val player: PlayerAvatar = playerOpt.get
|
||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, PlanetSideGUID(msg.guid),
|
||||
AvatarServiceReply.unLoadMap()
|
||||
))
|
||||
}
|
||||
case AvatarService.ObjectHeld(msg) =>
|
||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(msg.guid)
|
||||
if (playerOpt.isDefined) {
|
||||
val player: PlayerAvatar = playerOpt.get
|
||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, PlanetSideGUID(msg.guid),
|
||||
AvatarServiceReply.ObjectHeld()
|
||||
))
|
||||
}
|
||||
case AvatarService.PlanetsideAttribute(guid, attribute_type, attribute_value) =>
|
||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(guid)
|
||||
if (playerOpt.isDefined) {
|
||||
val player: PlayerAvatar = playerOpt.get
|
||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, guid,
|
||||
AvatarServiceReply.PlanetSideAttribute(attribute_type, attribute_value)
|
||||
))
|
||||
}
|
||||
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 AvatarService.DestroyDisplay(killer, victim) =>
|
||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(victim)
|
||||
if (playerOpt.isDefined) {
|
||||
val player: PlayerAvatar = playerOpt.get
|
||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, victim,
|
||||
AvatarServiceReply.DestroyDisplay(killer)
|
||||
))
|
||||
}
|
||||
case AvatarService.HitHintReturn(source_guid,victim_guid) =>
|
||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(source_guid)
|
||||
if (playerOpt.isDefined) {
|
||||
val player: PlayerAvatar = playerOpt.get
|
||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, victim_guid,
|
||||
AvatarServiceReply.DestroyDisplay(source_guid)
|
||||
))
|
||||
}
|
||||
case AvatarService.ChangeWeapon(unk1, sessionId) =>
|
||||
val playerOpt: Option[PlayerAvatar] = PlayerMasterList.getPlayer(sessionId)
|
||||
if (playerOpt.isDefined) {
|
||||
val player: PlayerAvatar = playerOpt.get
|
||||
AvatarEvents.publish(AvatarMessage("/Avatar/" + player.continent, PlanetSideGUID(player.guid),
|
||||
AvatarServiceReply.ChangeWeapon(unk1)
|
||||
))
|
||||
}
|
||||
*/
|
||||
case msg =>
|
||||
log.info(s"Unhandled message $msg from $sender")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
// 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])
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.avatar
|
||||
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import services.GenericEventBusMsg
|
||||
|
||||
final case class AvatarServiceResponse(toChannel : String,
|
||||
avatar_guid : PlanetSideGUID,
|
||||
replyMessage : AvatarResponse.Response
|
||||
) extends GenericEventBusMsg
|
||||
|
|
@ -1,245 +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 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 decomposition : Cancellable = DefaultCancellable.obj
|
||||
private var buriedCorpses : 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.
|
||||
super.postStop()
|
||||
burial.cancel
|
||||
decomposition.cancel
|
||||
|
||||
corpses.foreach(corpse => {
|
||||
BurialTask(corpse)
|
||||
LastRitesTask(corpse)
|
||||
})
|
||||
buriedCorpses.foreach { LastRitesTask }
|
||||
}
|
||||
|
||||
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) =>
|
||||
import CorpseRemovalActor.SimilarCorpses
|
||||
if(corpse.isBackpack && !buriedCorpses.exists(entry => SimilarCorpses(entry.corpse, corpse) )) {
|
||||
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
|
||||
if(!corpses.exists(entry => SimilarCorpses(entry.corpse, corpse))) {
|
||||
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) =>
|
||||
decomposition.cancel
|
||||
BurialTask(corpses(index))
|
||||
buriedCorpses = buriedCorpses :+ corpses(index)
|
||||
corpses = (corpses.take(index) ++ corpses.drop(index + 1)).sortBy(_.timeAlive)
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
decomposition = context.system.scheduler.scheduleOnce(500 milliseconds, self, CorpseRemovalActor.TryDelete())
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.debug(s"multiple target corpses submitted for early cleanup: $targets")
|
||||
import CorpseRemovalActor.SimilarCorpses
|
||||
decomposition.cancel
|
||||
//cumbersome partition
|
||||
//a - find targets from corpses
|
||||
val locatedTargets = for {
|
||||
a <- targets
|
||||
b <- corpses
|
||||
if b.corpse.HasGUID && a.HasGUID && SimilarCorpses(b.corpse, a)
|
||||
} yield b
|
||||
if(locatedTargets.nonEmpty) {
|
||||
decomposition.cancel
|
||||
locatedTargets.foreach { BurialTask }
|
||||
buriedCorpses = locatedTargets ++ buriedCorpses
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
decomposition = context.system.scheduler.scheduleOnce(500 milliseconds, self, CorpseRemovalActor.TryDelete())
|
||||
//b - corpses, after the found targets are removed (cull any non-GUID entries while at it)
|
||||
corpses = (for {
|
||||
a <- locatedTargets
|
||||
b <- corpses
|
||||
if b.corpse.HasGUID && a.corpse.HasGUID && !SimilarCorpses(b.corpse, a.corpse)
|
||||
} yield b).sortBy(_.timeAlive)
|
||||
}
|
||||
}
|
||||
RetimeFirstTask()
|
||||
}
|
||||
|
||||
case CorpseRemovalActor.StartDelete() =>
|
||||
burial.cancel
|
||||
decomposition.cancel
|
||||
val now : Long = System.nanoTime
|
||||
val (buried, rotting) = corpses.partition(entry => { now - entry.time >= entry.timeAlive })
|
||||
corpses = rotting
|
||||
buriedCorpses = buriedCorpses ++ buried
|
||||
buried.foreach { BurialTask }
|
||||
RetimeFirstTask()
|
||||
if(buriedCorpses.nonEmpty) {
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
burial = context.system.scheduler.scheduleOnce(500 milliseconds, self, CorpseRemovalActor.TryDelete())
|
||||
}
|
||||
|
||||
case CorpseRemovalActor.TryDelete() =>
|
||||
decomposition.cancel
|
||||
val (decomposed, rotting) = buriedCorpses.partition(entry => {
|
||||
!entry.zone.Corpses.contains(entry.corpse)
|
||||
})
|
||||
buriedCorpses = rotting
|
||||
decomposed.foreach { LastRitesTask }
|
||||
if(rotting.nonEmpty) {
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
decomposition = context.system.scheduler.scheduleOnce(500 milliseconds, self, CorpseRemovalActor.TryDelete())
|
||||
}
|
||||
|
||||
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.StartDelete())
|
||||
}
|
||||
}
|
||||
|
||||
def BurialTask(entry : CorpseRemovalActor.Entry) : Unit = {
|
||||
val target = entry.corpse
|
||||
entry.zone.Population ! Zone.Corpse.Remove(target)
|
||||
context.parent ! AvatarServiceMessage(entry.zone.Id, AvatarAction.ObjectDelete(Service.defaultPlayerGUID, target.GUID))
|
||||
}
|
||||
|
||||
def LastRitesTask(entry : CorpseRemovalActor.Entry) : Unit = {
|
||||
//Cart master: Nine pence.
|
||||
val target = entry.corpse
|
||||
target.Position = Vector3.Zero //somewhere it will not disturb anything
|
||||
taskResolver ! LastRitesTask(target, entry.zone)
|
||||
}
|
||||
|
||||
def LastRitesTask(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())
|
||||
|
||||
private final case class FailureToWork(corpse : Player, zone : Zone, ex : Throwable)
|
||||
|
||||
private final case class StartDelete()
|
||||
|
||||
private final case class TryDelete()
|
||||
|
||||
private def SimilarCorpses(obj1 : Player, obj2 : Player) : Boolean = {
|
||||
obj1 == obj2 && obj1.Continent.equals(obj2.Continent) && obj1.GUID == obj2.GUID
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.HasGUID && player.HasGUID && SimilarCorpses(corpse, player)) {
|
||||
Some(index)
|
||||
}
|
||||
else {
|
||||
recursiveFindCorpse(iter, player, index + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.local
|
||||
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.{PlanetSideGUID, TriggeredSound}
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
object LocalAction {
|
||||
trait 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 HackClear(player_guid : PlanetSideGUID, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
|
||||
final case class HackTemporarily(player_guid : PlanetSideGUID, continent : Zone, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
|
||||
final case class ProximityTerminalEffect(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, effectState : Boolean) extends Action
|
||||
final case class TriggerSound(player_guid : PlanetSideGUID, sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Action
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.local
|
||||
|
||||
import net.psforever.packet.game.{PlanetSideGUID, TriggeredSound}
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
object LocalResponse {
|
||||
trait Response
|
||||
|
||||
final case class DoorOpens(door_guid : PlanetSideGUID) extends Response
|
||||
final case class DoorCloses(door_guid : PlanetSideGUID) extends Response
|
||||
final case class HackClear(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
|
||||
final case class HackObject(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
|
||||
final case class ProximityTerminalEffect(object_guid : PlanetSideGUID, effectState : Boolean) extends Response
|
||||
final case class TriggerSound(sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Response
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.local
|
||||
|
||||
import akka.actor.{Actor, Props}
|
||||
import services.local.support.{DoorCloseActor, HackClearActor}
|
||||
import services.{GenericEventBus, Service}
|
||||
|
||||
class LocalService extends Actor {
|
||||
private val doorCloser = context.actorOf(Props[DoorCloseActor], "local-door-closer")
|
||||
private val hackClearer = context.actorOf(Props[HackClearActor], "local-hack-clearer")
|
||||
private [this] val log = org.log4s.getLogger
|
||||
|
||||
override def preStart = {
|
||||
log.info("Starting...")
|
||||
}
|
||||
|
||||
val LocalEvents = new GenericEventBus[LocalServiceResponse]
|
||||
|
||||
def receive = {
|
||||
case Service.Join(channel) =>
|
||||
val path = s"/$channel/Local"
|
||||
val who = sender()
|
||||
log.info(s"$who has joined $path")
|
||||
LocalEvents.subscribe(who, path)
|
||||
|
||||
case Service.Leave(None) =>
|
||||
LocalEvents.unsubscribe(sender())
|
||||
|
||||
case Service.Leave(Some(channel)) =>
|
||||
val path = s"/$channel/Local"
|
||||
val who = sender()
|
||||
log.info(s"$who has left $path")
|
||||
LocalEvents.unsubscribe(who, path)
|
||||
|
||||
case Service.LeaveAll() =>
|
||||
LocalEvents.unsubscribe(sender())
|
||||
|
||||
case LocalServiceMessage(forChannel, action) =>
|
||||
action match {
|
||||
case LocalAction.DoorOpens(player_guid, zone, 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.HackClear(player_guid, target, unk1, unk2) =>
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackClear(target.GUID, unk1, unk2))
|
||||
)
|
||||
case LocalAction.HackTemporarily(player_guid, zone, target, unk1, unk2) =>
|
||||
hackClearer ! HackClearActor.ObjectIsHacked(target, zone, unk1, unk2)
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackObject(target.GUID, unk1, unk2))
|
||||
)
|
||||
case LocalAction.ProximityTerminalEffect(player_guid, object_guid, effectState) =>
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.ProximityTerminalEffect(object_guid, effectState))
|
||||
)
|
||||
case LocalAction.TriggerSound(player_guid, sound, pos, unk, volume) =>
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.TriggerSound(sound, pos, unk, volume))
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
//response from DoorCloseActor
|
||||
case DoorCloseActor.CloseTheDoor(door_guid, zone_id) =>
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$zone_id/Local", Service.defaultPlayerGUID, LocalResponse.DoorCloses(door_guid))
|
||||
)
|
||||
|
||||
//response from HackClearActor
|
||||
case HackClearActor.ClearTheHack(target_guid, zone_id, unk1, unk2) =>
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$zone_id/Local", Service.defaultPlayerGUID, LocalResponse.HackClear(target_guid, unk1, unk2))
|
||||
)
|
||||
|
||||
case msg =>
|
||||
log.info(s"Unhandled message $msg from $sender")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.local
|
||||
|
||||
final case class LocalServiceMessage(forChannel : String, actionMessage : LocalAction.Action)
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.local
|
||||
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import services.GenericEventBusMsg
|
||||
|
||||
final case class LocalServiceResponse(toChannel : String,
|
||||
avatar_guid : PlanetSideGUID,
|
||||
replyMessage : LocalResponse.Response
|
||||
) extends GenericEventBusMsg
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.local.support
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.objects.DefaultCancellable
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @see `LocalService`
|
||||
*/
|
||||
class DoorCloseActor() extends Actor {
|
||||
/** The periodic `Executor` that checks for doors to be closed */
|
||||
private var doorCloserTrigger : Cancellable = DefaultCancellable.obj
|
||||
/** A `List` of currently open doors */
|
||||
private var openDoors : List[DoorCloseActor.DoorEntry] = Nil
|
||||
//private[this] val log = org.log4s.getLogger
|
||||
|
||||
def receive : Receive = {
|
||||
case DoorCloseActor.DoorIsOpen(door, zone, time) =>
|
||||
openDoors = openDoors :+ DoorCloseActor.DoorEntry(door, zone, time)
|
||||
if(openDoors.size == 1) { //we were the only entry so the event must be started from scratch
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
doorCloserTrigger = context.system.scheduler.scheduleOnce(DoorCloseActor.timeout, self, DoorCloseActor.TryCloseDoors())
|
||||
}
|
||||
|
||||
case DoorCloseActor.TryCloseDoors() =>
|
||||
doorCloserTrigger.cancel
|
||||
val now : Long = System.nanoTime
|
||||
val (doorsToClose1, doorsLeftOpen1) = PartitionEntries(openDoors, now)
|
||||
val (doorsToClose2, doorsLeftOpen2) = doorsToClose1.partition(entry => {
|
||||
entry.door.Open match {
|
||||
case Some(player) =>
|
||||
Vector3.MagnitudeSquared(entry.door.Position - player.Position) > 15
|
||||
case None =>
|
||||
true
|
||||
}
|
||||
})
|
||||
openDoors = (doorsLeftOpen1 ++ doorsLeftOpen2).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
|
||||
})
|
||||
|
||||
if(openDoors.nonEmpty) {
|
||||
val short_timeout : FiniteDuration = math.max(1, DoorCloseActor.timeout_time - (now - openDoors.head.time)) nanoseconds
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
doorCloserTrigger = context.system.scheduler.scheduleOnce(short_timeout, self, DoorCloseActor.TryCloseDoors())
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over entries in a `List` until an entry that does not exceed the time limit is discovered.
|
||||
* Separate the original `List` into two:
|
||||
* a `List` of elements that have exceeded the time limit,
|
||||
* and a `List` of elements that still satisfy the time limit.
|
||||
* As newer entries to the `List` will always resolve later than old ones,
|
||||
* and newer entries are always added to the end of the main `List`,
|
||||
* processing in order is always correct.
|
||||
* @param list the `List` of entries to divide
|
||||
* @param now the time right now (in nanoseconds)
|
||||
* @see `List.partition`
|
||||
* @return a `Tuple` of two `Lists`, whose qualifications are explained above
|
||||
*/
|
||||
private def PartitionEntries(list : List[DoorCloseActor.DoorEntry], now : Long) : (List[DoorCloseActor.DoorEntry], List[DoorCloseActor.DoorEntry]) = {
|
||||
val n : Int = recursivePartitionEntries(list.iterator, now)
|
||||
(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[DoorCloseActor.DoorEntry], now : Long, index : Int = 0) : Int = {
|
||||
if(!iter.hasNext) {
|
||||
index
|
||||
}
|
||||
else {
|
||||
val entry = iter.next()
|
||||
if(now - entry.time >= DoorCloseActor.timeout_time) {
|
||||
recursivePartitionEntries(iter, now, index + 1)
|
||||
}
|
||||
else {
|
||||
index
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object DoorCloseActor {
|
||||
/** The wait before an open door closes; as a Long for calculation simplicity */
|
||||
private final val timeout_time : Long = 5000000000L //nanoseconds (5s)
|
||||
/** The wait before an open door closes; as a `FiniteDuration` for `Executor` simplicity */
|
||||
private final val timeout : FiniteDuration = timeout_time nanoseconds
|
||||
|
||||
/**
|
||||
* Message that carries information about a door that has been opened.
|
||||
* @param door the door object
|
||||
* @param zone the zone in which the door resides
|
||||
* @param time when the door was opened
|
||||
* @see `DoorEntry`
|
||||
*/
|
||||
final case class DoorIsOpen(door : Door, zone : Zone, time : Long = System.nanoTime())
|
||||
/**
|
||||
* Message that carries information about a door that needs to close.
|
||||
* Prompting, as compared to `DoorIsOpen` which is reactionary.
|
||||
* @param door_guid the door
|
||||
* @param zone_id the zone in which the door resides
|
||||
*/
|
||||
final case class CloseTheDoor(door_guid : PlanetSideGUID, zone_id : String)
|
||||
/**
|
||||
* Internal message used to signal a test of the queued door information.
|
||||
*/
|
||||
private final case class TryCloseDoors()
|
||||
|
||||
/**
|
||||
* Entry of door information.
|
||||
* The `zone` is maintained separately to ensure that any message resulting in an attempt to close doors is targetted.
|
||||
* @param door the door object
|
||||
* @param zone the zone in which the door resides
|
||||
* @param time when the door was opened
|
||||
* @see `DoorIsOpen`
|
||||
*/
|
||||
private final case class DoorEntry(door : Door, zone : Zone, time : Long)
|
||||
}
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.local.support
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.objects.DefaultCancellable
|
||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @see `LocalService`
|
||||
*/
|
||||
class HackClearActor() extends Actor {
|
||||
/** The periodic `Executor` that checks for server objects to be unhacked */
|
||||
private var clearTrigger : Cancellable = DefaultCancellable.obj
|
||||
/** A `List` of currently hacked server objects */
|
||||
private var hackedObjects : List[HackClearActor.HackEntry] = Nil
|
||||
//private[this] val log = org.log4s.getLogger
|
||||
|
||||
def receive : Receive = {
|
||||
case HackClearActor.ObjectIsHacked(target, zone, unk1, unk2, time) =>
|
||||
hackedObjects = hackedObjects :+ HackClearActor.HackEntry(target, zone, unk1, unk2, time)
|
||||
if(hackedObjects.size == 1) { //we were the only entry so the event must be started from scratch
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
clearTrigger = context.system.scheduler.scheduleOnce(HackClearActor.timeout, self, HackClearActor.TryClearHacks())
|
||||
}
|
||||
|
||||
case HackClearActor.TryClearHacks() =>
|
||||
clearTrigger.cancel
|
||||
val now : Long = System.nanoTime
|
||||
//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.ClearTheHack(entry.target.GUID, entry.zone.Id, entry.unk1, entry.unk2) //call up to the main event system
|
||||
})
|
||||
|
||||
if(stillHackedObjects.nonEmpty) {
|
||||
val short_timeout : FiniteDuration = math.max(1, HackClearActor.timeout_time - (now - stillHackedObjects.head.time)) nanoseconds
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
clearTrigger = context.system.scheduler.scheduleOnce(short_timeout, self, HackClearActor.TryClearHacks())
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over entries in a `List` until an entry that does not exceed the time limit is discovered.
|
||||
* Separate the original `List` into two:
|
||||
* a `List` of elements that have exceeded the time limit,
|
||||
* and a `List` of elements that still satisfy the time limit.
|
||||
* As newer entries to the `List` will always resolve later than old ones,
|
||||
* and newer entries are always added to the end of the main `List`,
|
||||
* processing in order is always correct.
|
||||
* @param list the `List` of entries to divide
|
||||
* @param now the time right now (in nanoseconds)
|
||||
* @see `List.partition`
|
||||
* @return a `Tuple` of two `Lists`, whose qualifications are explained above
|
||||
*/
|
||||
private def PartitionEntries(list : List[HackClearActor.HackEntry], now : Long) : (List[HackClearActor.HackEntry], List[HackClearActor.HackEntry]) = {
|
||||
val n : Int = recursivePartitionEntries(list.iterator, now)
|
||||
(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[HackClearActor.HackEntry], now : Long, index : Int = 0) : Int = {
|
||||
if(!iter.hasNext) {
|
||||
index
|
||||
}
|
||||
else {
|
||||
val entry = iter.next()
|
||||
if(now - entry.time >= HackClearActor.timeout_time) {
|
||||
recursivePartitionEntries(iter, now, index + 1)
|
||||
}
|
||||
else {
|
||||
index
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object HackClearActor {
|
||||
/** The wait before a server object is to unhack; as a Long for calculation simplicity */
|
||||
private final val timeout_time : Long = 60000000000L //nanoseconds (60s)
|
||||
/** The wait before a server object is to unhack; as a `FiniteDuration` for `Executor` simplicity */
|
||||
private final val timeout : FiniteDuration = timeout_time nanoseconds
|
||||
|
||||
/**
|
||||
* Message that carries information about a server object that has been hacked.
|
||||
* @param target the server object
|
||||
* @param zone the zone in which the object resides
|
||||
* @param time when the object was hacked
|
||||
* @see `HackEntry`
|
||||
*/
|
||||
final case class ObjectIsHacked(target : PlanetSideServerObject, zone : Zone, unk1 : Long, unk2 : Long, time : Long = System.nanoTime())
|
||||
/**
|
||||
* Message that carries information about a server object that needs its functionality restored.
|
||||
* Prompting, as compared to `ObjectIsHacked` which is reactionary.
|
||||
* @param door_guid the server object
|
||||
* @param zone_id the zone in which the object resides
|
||||
*/
|
||||
final case class ClearTheHack(door_guid : PlanetSideGUID, zone_id : String, unk1 : Long, unk2 : Long)
|
||||
/**
|
||||
* Internal message used to signal a test of the queued door information.
|
||||
*/
|
||||
private final case class TryClearHacks()
|
||||
|
||||
/**
|
||||
* Entry of hacked server object information.
|
||||
* The `zone` is maintained separately to ensure that any message resulting in an attempt to close doors is targetted.
|
||||
* @param target the server object
|
||||
* @param zone the zone in which the object resides
|
||||
* @param time when the object was hacked
|
||||
* @see `ObjectIsHacked`
|
||||
*/
|
||||
private final case class HackEntry(target : PlanetSideServerObject, zone : Zone, unk1 : Long, unk2 : Long, time : Long)
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.vehicle
|
||||
|
||||
import net.psforever.objects.{PlanetSideGameObject, Vehicle}
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||
import net.psforever.types.{BailType, DriveState, Vector3}
|
||||
|
||||
object VehicleAction {
|
||||
trait Action
|
||||
|
||||
final case class Awareness(player_guid : PlanetSideGUID, vehicle_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 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 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, continent : Zone, vehicle : Vehicle) 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 UpdateAmsSpawnPoint(zone : Zone) extends Action
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.vehicle
|
||||
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.objects.{PlanetSideGameObject, Vehicle}
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.packet.game.objectcreate.ConstructorData
|
||||
import net.psforever.types.{BailType, DriveState, Vector3}
|
||||
|
||||
object VehicleResponse {
|
||||
trait Response
|
||||
|
||||
final case class AttachToRails(vehicle_guid : PlanetSideGUID, rails_guid : PlanetSideGUID) extends Response
|
||||
final case class Awareness(vehicle_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 DetachFromRails(vehicle_guid : PlanetSideGUID, rails_guid : PlanetSideGUID, rails_pos : Vector3, rails_rot : Float) extends Response
|
||||
final case class DismountVehicle(bailType : BailType.Value , unk2 : Boolean) 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 ResetSpawnPad(pad_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 UnloadVehicle(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
|
||||
}
|
||||
|
|
@ -1,193 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.vehicle
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
||||
import net.psforever.objects.zones.Zone
|
||||
import services.vehicle.support.{DeconstructionActor, DelayedDeconstructionActor}
|
||||
import net.psforever.types.DriveState
|
||||
|
||||
import services.{GenericEventBus, Service}
|
||||
|
||||
class VehicleService extends Actor {
|
||||
private val vehicleDecon : ActorRef = context.actorOf(Props[DeconstructionActor], "vehicle-decon-agent")
|
||||
private val vehicleDelayedDecon : ActorRef = context.actorOf(Props[DelayedDeconstructionActor], "vehicle-delayed-decon-agent")
|
||||
vehicleDecon ! DeconstructionActor.RequestTaskResolver
|
||||
private [this] val log = org.log4s.getLogger
|
||||
|
||||
override def preStart = {
|
||||
log.info("Starting...")
|
||||
}
|
||||
|
||||
val VehicleEvents = new GenericEventBus[VehicleServiceResponse]
|
||||
|
||||
def receive = {
|
||||
case Service.Join(channel) =>
|
||||
val path = s"/$channel/Vehicle"
|
||||
val who = sender()
|
||||
log.info(s"$who has joined $path")
|
||||
VehicleEvents.subscribe(who, path)
|
||||
|
||||
case Service.Leave(None) =>
|
||||
VehicleEvents.unsubscribe(sender())
|
||||
|
||||
case Service.Leave(Some(channel)) =>
|
||||
val path = s"/$channel/Vehicle"
|
||||
val who = sender()
|
||||
log.info(s"$who has left $path")
|
||||
VehicleEvents.unsubscribe(who, path)
|
||||
|
||||
case Service.LeaveAll() =>
|
||||
VehicleEvents.unsubscribe(sender())
|
||||
|
||||
case VehicleServiceMessage(forChannel, action) =>
|
||||
action match {
|
||||
case VehicleAction.Awareness(player_guid, vehicle_guid) =>
|
||||
VehicleEvents.publish(
|
||||
VehicleServiceResponse(s"/$forChannel/Vehicle", player_guid, VehicleResponse.Awareness(vehicle_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.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.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.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.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 _ => ;
|
||||
}
|
||||
|
||||
//message to DeconstructionActor
|
||||
case VehicleServiceMessage.RequestDeleteVehicle(vehicle, continent) =>
|
||||
vehicleDecon ! DeconstructionActor.RequestDeleteVehicle(vehicle, continent)
|
||||
|
||||
//message to DelayedDeconstructionActor
|
||||
case VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, zone, timeAlive) =>
|
||||
vehicleDelayedDecon ! DelayedDeconstructionActor.ScheduleDeconstruction(vehicle, zone, timeAlive)
|
||||
|
||||
//message to DelayedDeconstructionActor
|
||||
case VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) =>
|
||||
vehicleDelayedDecon ! DelayedDeconstructionActor.UnscheduleDeconstruction(vehicle_guid)
|
||||
|
||||
//response from DeconstructionActor
|
||||
case DeconstructionActor.DeleteVehicle(vehicle_guid, zone_id) =>
|
||||
VehicleEvents.publish(
|
||||
VehicleServiceResponse(s"/$zone_id/Vehicle", Service.defaultPlayerGUID, VehicleResponse.UnloadVehicle(vehicle_guid))
|
||||
)
|
||||
|
||||
//from VehicleSpawnControl
|
||||
case VehicleSpawnPad.ConcealPlayer(player_guid, zone_id) =>
|
||||
VehicleEvents.publish(
|
||||
VehicleServiceResponse(s"/$zone_id/Vehicle", Service.defaultPlayerGUID, VehicleResponse.ConcealPlayer(player_guid))
|
||||
)
|
||||
|
||||
//from VehicleSpawnControl
|
||||
case VehicleSpawnPad.AttachToRails(vehicle, pad, zone_id) =>
|
||||
VehicleEvents.publish(
|
||||
VehicleServiceResponse(s"/$zone_id/Vehicle", Service.defaultPlayerGUID, VehicleResponse.AttachToRails(vehicle.GUID, pad.GUID))
|
||||
)
|
||||
|
||||
//from VehicleSpawnControl
|
||||
case VehicleSpawnPad.DetachFromRails(vehicle, pad, zone_id) =>
|
||||
VehicleEvents.publish(
|
||||
VehicleServiceResponse(s"/$zone_id/Vehicle", Service.defaultPlayerGUID, VehicleResponse.DetachFromRails(vehicle.GUID, pad.GUID, pad.Position, pad.Orientation.z))
|
||||
)
|
||||
|
||||
//from VehicleSpawnControl
|
||||
case VehicleSpawnPad.ResetSpawnPad(pad, zone_id) =>
|
||||
VehicleEvents.publish(
|
||||
VehicleServiceResponse(s"/$zone_id/Vehicle", Service.defaultPlayerGUID, VehicleResponse.ResetSpawnPad(pad.GUID))
|
||||
)
|
||||
|
||||
case VehicleSpawnPad.RevealPlayer(player_guid, zone_id) =>
|
||||
VehicleEvents.publish(
|
||||
VehicleServiceResponse(s"/$zone_id/Vehicle", Service.defaultPlayerGUID, VehicleResponse.RevealPlayer(player_guid))
|
||||
)
|
||||
|
||||
//from VehicleSpawnControl
|
||||
case VehicleSpawnPad.LoadVehicle(vehicle, zone) =>
|
||||
val definition = vehicle.Definition
|
||||
val vtype = definition.ObjectId
|
||||
val vguid = vehicle.GUID
|
||||
val vdata = definition.Packet.ConstructorData(vehicle).get
|
||||
zone.Transport ! Zone.Vehicle.Spawn(vehicle)
|
||||
VehicleEvents.publish(
|
||||
VehicleServiceResponse(s"/${zone.Id}/Vehicle", Service.defaultPlayerGUID, VehicleResponse.LoadVehicle(vehicle, vtype, vguid, vdata))
|
||||
)
|
||||
vehicleDelayedDecon ! DelayedDeconstructionActor.UnscheduleDeconstruction(vguid)
|
||||
vehicleDelayedDecon ! DelayedDeconstructionActor.ScheduleDeconstruction(vehicle, zone, 600L) //10min
|
||||
|
||||
//from VehicleSpawnControl
|
||||
case VehicleSpawnPad.DisposeVehicle(vehicle, zone) =>
|
||||
vehicleDelayedDecon ! DelayedDeconstructionActor.UnscheduleDeconstruction(vehicle.GUID)
|
||||
vehicleDecon ! DeconstructionActor.RequestDeleteVehicle(vehicle, zone)
|
||||
|
||||
//correspondence from WorldSessionActor
|
||||
case VehicleServiceMessage.AMSDeploymentChange(zone) =>
|
||||
VehicleEvents.publish(
|
||||
VehicleServiceResponse(s"/${zone.Id}/Vehicle", Service.defaultPlayerGUID, VehicleResponse.UpdateAmsSpawnPoint(AmsSpawnPoints(zone)))
|
||||
)
|
||||
|
||||
case msg =>
|
||||
log.info(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.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])
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.vehicle
|
||||
|
||||
import net.psforever.objects.Vehicle
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
||||
final case class VehicleServiceMessage(forChannel : String, actionMessage : VehicleAction.Action)
|
||||
|
||||
object VehicleServiceMessage {
|
||||
final case class DelayedVehicleDeconstruction(vehicle : Vehicle, continent : Zone, timeAlive : Long)
|
||||
final case class GiveActorControl(vehicle : Vehicle, actorName : String)
|
||||
final case class RevokeActorControl(vehicle : Vehicle)
|
||||
final case class RequestDeleteVehicle(vehicle : Vehicle, continent : Zone)
|
||||
final case class UnscheduleDeconstruction(vehicle_guid : PlanetSideGUID)
|
||||
|
||||
final case class AMSDeploymentChange(zone : Zone)
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.vehicle
|
||||
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import services.GenericEventBusMsg
|
||||
|
||||
final case class VehicleServiceResponse(toChannel : String,
|
||||
avatar_guid : PlanetSideGUID,
|
||||
replyMessage : VehicleResponse.Response
|
||||
) extends GenericEventBusMsg
|
||||
|
|
@ -1,292 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.vehicle.support
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Cancellable}
|
||||
import net.psforever.objects.{DefaultCancellable, GlobalDefinitions, Vehicle}
|
||||
import net.psforever.objects.guid.TaskResolver
|
||||
import net.psforever.objects.vehicles.Seat
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.{DismountVehicleCargoMsg, PlanetSideGUID}
|
||||
import net.psforever.types.Vector3
|
||||
import services.ServiceManager
|
||||
import services.ServiceManager.Lookup
|
||||
import services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* Manage a previously-functioning vehicle as it is being deconstructed.<br>
|
||||
* <br>
|
||||
* A reference to a vehicle should be passed to this object as soon as it is going to be cleaned-up from the game world.
|
||||
* Once accepted, only a few seconds will remain before the vehicle is deleted.
|
||||
* To ensure that no players are lost in the deletion, all occupants of the vehicle are kicked out.
|
||||
* Furthermore, the vehicle is rendered "dead" and inaccessible right up to the point where it is removed.<br>
|
||||
* <br>
|
||||
* This `Actor` is intended to sit on top of the event system that handles broadcast messaging.
|
||||
*/
|
||||
class DeconstructionActor extends Actor {
|
||||
/** The periodic `Executor` that scraps the next vehicle on the list */
|
||||
private var scrappingProcess : Cancellable = DefaultCancellable.obj
|
||||
/** A `List` of currently doomed vehicles */
|
||||
private var vehicles : List[DeconstructionActor.VehicleEntry] = Nil
|
||||
/** The periodic `Executor` that cleans up the next vehicle on the list */
|
||||
private var heapEmptyProcess : Cancellable = DefaultCancellable.obj
|
||||
/** A `List` of vehicles that have been removed from the game world and are awaiting deconstruction. */
|
||||
private var vehicleScrapHeap : List[DeconstructionActor.VehicleEntry] = Nil
|
||||
/** The manager that helps unregister the vehicle from its current GUID scope */
|
||||
private var taskResolver : ActorRef = Actor.noSender
|
||||
//private[this] val log = org.log4s.getLogger
|
||||
|
||||
override def postStop() : Unit = {
|
||||
super.postStop()
|
||||
scrappingProcess.cancel
|
||||
heapEmptyProcess.cancel
|
||||
|
||||
vehicles.foreach(entry => {
|
||||
RetirementTask(entry)
|
||||
DestructionTask(entry)
|
||||
})
|
||||
vehicleScrapHeap.foreach { DestructionTask }
|
||||
}
|
||||
|
||||
def receive : Receive = {
|
||||
/*
|
||||
ask for a resolver to deal with the GUID system
|
||||
when the TaskResolver is finally delivered, switch over to a behavior that actually deals with submitted vehicles
|
||||
*/
|
||||
case DeconstructionActor.RequestTaskResolver =>
|
||||
ServiceManager.serviceManager ! Lookup("taskResolver")
|
||||
|
||||
case ServiceManager.LookupResult("taskResolver", endpoint) =>
|
||||
taskResolver = endpoint
|
||||
context.become(Processing)
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def Processing : Receive = {
|
||||
case DeconstructionActor.RequestDeleteVehicle(vehicle, zone, time) =>
|
||||
if(!vehicles.exists(_.vehicle == vehicle) && !vehicleScrapHeap.exists(_.vehicle == vehicle)) {
|
||||
vehicles = vehicles :+ DeconstructionActor.VehicleEntry(vehicle, zone, time)
|
||||
vehicle.Actor ! Vehicle.PrepareForDeletion
|
||||
//kick everyone out; this is a no-blocking manual form of MountableBehavior ! Mountable.TryDismount
|
||||
vehicle.Definition.MountPoints.values.foreach(seat_num => {
|
||||
val zone_id : String = zone.Id
|
||||
vehicle.Seat(seat_num) match {
|
||||
case Some(seat) =>
|
||||
seat.Occupant match {
|
||||
case Some(tplayer) =>
|
||||
seat.Occupant = None
|
||||
tplayer.VehicleSeated = None
|
||||
if(tplayer.HasGUID) {
|
||||
context.parent ! VehicleServiceMessage(zone_id, VehicleAction.KickPassenger(tplayer.GUID, 4, false, vehicle.GUID))
|
||||
}
|
||||
case None => ; // No player seated
|
||||
}
|
||||
case None => ; // Not a seat mount point
|
||||
}
|
||||
vehicle.CargoHold(seat_num) match {
|
||||
case Some(cargo) =>
|
||||
cargo.Occupant match {
|
||||
case Some(vehicle) =>
|
||||
//todo: this probably doesn't work for passengers within the cargo vehicle
|
||||
// Instruct client to start bail dismount procedure
|
||||
// player_num set to 0 as it's not easily available in the context and currently isn't used by DismountVehicleCargoMsg
|
||||
context.parent ! DismountVehicleCargoMsg(PlanetSideGUID(0), vehicle.GUID, true, false, false)
|
||||
case None => ; // No vehicle in cargo
|
||||
}
|
||||
case None => ; // Not a cargo mounting point
|
||||
}
|
||||
|
||||
})
|
||||
if(vehicles.size == 1) {
|
||||
//we were the only entry so the event must be started from scratch
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
scrappingProcess = context.system.scheduler.scheduleOnce(DeconstructionActor.timeout, self, DeconstructionActor.StartDeleteVehicle())
|
||||
}
|
||||
}
|
||||
|
||||
case DeconstructionActor.StartDeleteVehicle() =>
|
||||
scrappingProcess.cancel
|
||||
heapEmptyProcess.cancel
|
||||
val now : Long = System.nanoTime
|
||||
val (vehiclesToScrap, vehiclesRemain) = PartitionEntries(vehicles, now)
|
||||
vehicles = vehiclesRemain
|
||||
vehicleScrapHeap = vehicleScrapHeap ++ vehiclesToScrap //may include existing entries
|
||||
vehiclesToScrap.foreach(entry => {
|
||||
val vehicle = entry.vehicle
|
||||
val zone = entry.zone
|
||||
RetirementTask(entry)
|
||||
if(vehicle.Definition == GlobalDefinitions.ams) {
|
||||
import net.psforever.types.DriveState
|
||||
vehicle.DeploymentState = DriveState.Mobile //internally undeployed //TODO this should be temporary?
|
||||
context.parent ! VehicleServiceMessage.AMSDeploymentChange(zone)
|
||||
}
|
||||
taskResolver ! DeconstructionTask(vehicle, zone)
|
||||
})
|
||||
|
||||
if(vehiclesRemain.nonEmpty) {
|
||||
val short_timeout : FiniteDuration = math.max(1, DeconstructionActor.timeout_time - (now - vehiclesRemain.head.time)) nanoseconds
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
scrappingProcess = context.system.scheduler.scheduleOnce(short_timeout, self, DeconstructionActor.StartDeleteVehicle())
|
||||
}
|
||||
if(vehicleScrapHeap.nonEmpty) {
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
heapEmptyProcess = context.system.scheduler.scheduleOnce(500 milliseconds, self, DeconstructionActor.TryDeleteVehicle())
|
||||
}
|
||||
|
||||
case DeconstructionActor.TryDeleteVehicle() =>
|
||||
heapEmptyProcess.cancel
|
||||
val (vehiclesToScrap, vehiclesRemain) = vehicleScrapHeap.partition(entry => !entry.zone.Vehicles.contains(entry.vehicle))
|
||||
vehicleScrapHeap = vehiclesRemain
|
||||
vehiclesToScrap.foreach { DestructionTask }
|
||||
if(vehiclesRemain.nonEmpty) {
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
heapEmptyProcess = context.system.scheduler.scheduleOnce(500 milliseconds, self, DeconstructionActor.TryDeleteVehicle())
|
||||
}
|
||||
|
||||
case DeconstructionActor.FailureToDeleteVehicle(localVehicle, localZone, ex) =>
|
||||
org.log4s.getLogger.error(s"vehicle deconstruction: $localVehicle failed to be properly cleaned up from zone $localZone - $ex")
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def RetirementTask(entry : DeconstructionActor.VehicleEntry) : Unit = {
|
||||
val vehicle = entry.vehicle
|
||||
val zone = entry.zone
|
||||
vehicle.Position = Vector3.Zero //somewhere it will not disturb anything
|
||||
zone.Transport ! Zone.Vehicle.Despawn(vehicle)
|
||||
context.parent ! DeconstructionActor.DeleteVehicle(vehicle.GUID, zone.Id) //call up to the main event system
|
||||
}
|
||||
|
||||
def DestructionTask(entry : DeconstructionActor.VehicleEntry) : Unit = {
|
||||
val vehicle = entry.vehicle
|
||||
val zone = entry.zone
|
||||
taskResolver ! DeconstructionTask(vehicle, zone)
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a middleman `Task` intended to return error messages to the `DeconstructionActor`.
|
||||
* @param vehicle the `Vehicle` object
|
||||
* @param zone the `Zone` in which the vehicle resides
|
||||
* @return a `TaskResolver.GiveTask` message
|
||||
*/
|
||||
def DeconstructionTask(vehicle : Vehicle, zone : Zone) : TaskResolver.GiveTask = {
|
||||
import net.psforever.objects.guid.{GUIDTask, Task}
|
||||
TaskResolver.GiveTask (
|
||||
new Task() {
|
||||
private val localVehicle = vehicle
|
||||
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 ! DeconstructionActor.FailureToDeleteVehicle(localVehicle, localZone, ex)
|
||||
}
|
||||
}, List(GUIDTask.UnregisterVehicle(vehicle)(zone.GUID))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over entries in a `List` until an entry that does not exceed the time limit is discovered.
|
||||
* Separate the original `List` into two:
|
||||
* a `List` of elements that have exceeded the time limit,
|
||||
* and a `List` of elements that still satisfy the time limit.
|
||||
* As newer entries to the `List` will always resolve later than old ones,
|
||||
* and newer entries are always added to the end of the main `List`,
|
||||
* processing in order is always correct.
|
||||
* @param list the `List` of entries to divide
|
||||
* @param now the time right now (in nanoseconds)
|
||||
* @see `List.partition`
|
||||
* @return a `Tuple` of two `Lists`, whose qualifications are explained above
|
||||
*/
|
||||
private def PartitionEntries(list : List[DeconstructionActor.VehicleEntry], now : Long) : (List[DeconstructionActor.VehicleEntry], List[DeconstructionActor.VehicleEntry]) = {
|
||||
val n : Int = recursivePartitionEntries(list.iterator, now)
|
||||
(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[DeconstructionActor.VehicleEntry], now : Long, index : Int = 0) : Int = {
|
||||
if(!iter.hasNext) {
|
||||
index
|
||||
}
|
||||
else {
|
||||
val entry = iter.next()
|
||||
if(now - entry.time >= DeconstructionActor.timeout_time) {
|
||||
recursivePartitionEntries(iter, now, index + 1)
|
||||
}
|
||||
else {
|
||||
index
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object DeconstructionActor {
|
||||
/** The wait before completely deleting a vehicle; as a Long for calculation simplicity */
|
||||
private final val timeout_time : Long = 5000000000L //nanoseconds (5s)
|
||||
/** The wait before completely deleting a vehicle; as a `FiniteDuration` for `Executor` simplicity */
|
||||
private final val timeout : FiniteDuration = timeout_time nanoseconds
|
||||
|
||||
final case class RequestTaskResolver()
|
||||
|
||||
/**
|
||||
* Message that carries information about a vehicle to be deconstructed.
|
||||
* @param vehicle the `Vehicle` object
|
||||
* @param zone the `Zone` in which the vehicle resides
|
||||
* @param time when the vehicle was doomed
|
||||
* @see `VehicleEntry`
|
||||
*/
|
||||
final case class RequestDeleteVehicle(vehicle : Vehicle, zone : Zone, time : Long = System.nanoTime())
|
||||
/**
|
||||
* Message that carries information about a vehicle to be deconstructed.
|
||||
* Prompting, as compared to `RequestDeleteVehicle` which is reactionary.
|
||||
* @param vehicle_guid the vehicle
|
||||
* @param zone_id the `Zone` in which the vehicle resides
|
||||
*/
|
||||
final case class DeleteVehicle(vehicle_guid : PlanetSideGUID, zone_id : String)
|
||||
/**
|
||||
* Internal message used to signal a test of the queued vehicle information.
|
||||
* Remove all deconstructing vehicles from the game world.
|
||||
*/
|
||||
private final case class StartDeleteVehicle()
|
||||
/**
|
||||
* Internal message used to signal a test of the queued vehicle information.
|
||||
* Remove all deconstructing vehicles from the zone's globally unique identifier system.
|
||||
*/
|
||||
private final case class TryDeleteVehicle()
|
||||
|
||||
/**
|
||||
* Error-passing message carrying information out of the final deconstruction GUID unregistering task.
|
||||
* @param vehicle the `Vehicle` object
|
||||
* @param zone the `Zone` in which the vehicle may or may not reside
|
||||
* @param ex information regarding what happened
|
||||
*/
|
||||
private final case class FailureToDeleteVehicle(vehicle : Vehicle, zone : Zone, ex : Throwable)
|
||||
|
||||
/**
|
||||
* Entry of vehicle information.
|
||||
* The `zone` is maintained separately as a necessity, required to complete the deletion of the vehicle
|
||||
* via unregistering of the vehicle and all related, registered objects.
|
||||
* @param vehicle the `Vehicle` object
|
||||
* @param zone the `Zone` in which the vehicle resides
|
||||
* @param time when the vehicle was doomed
|
||||
* @see `RequestDeleteVehicle`
|
||||
*/
|
||||
private final case class VehicleEntry(vehicle : Vehicle, zone : Zone, time : Long)
|
||||
}
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.vehicle.support
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.objects.{DefaultCancellable, Vehicle}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import services.vehicle.VehicleServiceMessage
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/**
|
||||
* Maintain and curate a list of timed `vehicle` object deconstruction tasks.<br>
|
||||
* <br>
|
||||
* These tasks are queued or dismissed by player activity but they are executed independent of player activity.
|
||||
* A common disconnected cause of deconstruction is neglect for an extended period of time.
|
||||
* At that point, the original owner of the vehicle no longer matters.
|
||||
* Deconstruction neglect, however, is averted by having someone become seated.
|
||||
* A realized deconstruction is entirely based on a fixed interval after an unresolved request has been received.
|
||||
* The actual process of deconstructing the vehicle and cleaning up its resources is performed by an external agent.<br>
|
||||
* <br>
|
||||
* This `Actor` is intended to sit on top of the event system that handles broadcast messaging.
|
||||
*/
|
||||
class DelayedDeconstructionActor extends Actor {
|
||||
/** The periodic `Executor` that scraps the next vehicle on the list */
|
||||
private var monitor : Cancellable = DefaultCancellable.obj
|
||||
/** A `List` of currently doomed vehicles */
|
||||
private var vehicles : List[DelayedDeconstructionActor.VehicleEntry] = Nil
|
||||
private[this] val log = org.log4s.getLogger
|
||||
private[this] def trace(msg : String) : Unit = log.trace(msg)
|
||||
|
||||
|
||||
def receive : Receive = {
|
||||
case DelayedDeconstructionActor.ScheduleDeconstruction(vehicle, zone, timeAlive) =>
|
||||
trace(s"delayed deconstruction order for $vehicle in $timeAlive")
|
||||
val oldHead = vehicles.headOption
|
||||
val now : Long = System.nanoTime
|
||||
vehicles = (vehicles :+ DelayedDeconstructionActor.VehicleEntry(vehicle, zone, timeAlive * 1000000000L))
|
||||
.sortBy(entry => entry.survivalTime - (now - entry.logTime))
|
||||
if(vehicles.size == 1 || oldHead != vehicles.headOption) { //we were the only entry so the event must be started from scratch
|
||||
RetimePeriodicTest()
|
||||
}
|
||||
|
||||
case DelayedDeconstructionActor.UnscheduleDeconstruction(vehicle_guid) =>
|
||||
//all tasks for this vehicle are cleared from the queue
|
||||
//clear any task that is no longer valid by determination of unregistered GUID
|
||||
val before = vehicles.length
|
||||
val now : Long = System.nanoTime
|
||||
vehicles = vehicles.filter(entry => { entry.vehicle.HasGUID && entry.vehicle.GUID != vehicle_guid })
|
||||
.sortBy(entry => entry.survivalTime - (now - entry.logTime))
|
||||
trace(s"attempting to clear deconstruction order for vehicle $vehicle_guid, found ${before - vehicles.length}")
|
||||
RetimePeriodicTest()
|
||||
|
||||
case DelayedDeconstructionActor.PeriodicTaskCulling =>
|
||||
//filter the list of deconstruction tasks for any that are need to be triggered
|
||||
monitor.cancel
|
||||
val now : Long = System.nanoTime
|
||||
val (vehiclesToDecon, vehiclesRemain) = vehicles.partition(entry => { now - entry.logTime >= entry.survivalTime })
|
||||
vehicles = vehiclesRemain.sortBy(_.survivalTime)
|
||||
trace(s"vehicle culling - ${vehiclesToDecon.length} deconstruction tasks found; ${vehiclesRemain.length} tasks remain")
|
||||
vehiclesToDecon.foreach(entry => { context.parent ! VehicleServiceMessage.RequestDeleteVehicle(entry.vehicle, entry.zone) })
|
||||
RetimePeriodicTest()
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
def RetimePeriodicTest() : Unit = {
|
||||
monitor.cancel
|
||||
vehicles.headOption match {
|
||||
case None => ;
|
||||
case Some(entry) =>
|
||||
val retime = math.max(1, entry.survivalTime - (System.nanoTime - entry.logTime)) nanoseconds
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
monitor = context.system.scheduler.scheduleOnce(retime, self, DelayedDeconstructionActor.PeriodicTaskCulling)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object DelayedDeconstructionActor {
|
||||
/**
|
||||
* Timer for the repeating executor.
|
||||
*/
|
||||
private final val periodicTest : FiniteDuration = 5000000000L nanoseconds //5s
|
||||
|
||||
/**
|
||||
* Queue a future vehicle deconstruction action.
|
||||
* @param vehicle the `Vehicle` object
|
||||
* @param zone the `Zone` that the vehicle currently occupies
|
||||
* @param survivalTime how long until the vehicle will be deconstructed in seconds
|
||||
*/
|
||||
final case class ScheduleDeconstruction(vehicle : Vehicle, zone : Zone, survivalTime : Long)
|
||||
|
||||
/**
|
||||
* Dequeue a vehicle from being deconstructed.
|
||||
* @param vehicle_guid the vehicle
|
||||
*/
|
||||
final case class UnscheduleDeconstruction(vehicle_guid : PlanetSideGUID)
|
||||
|
||||
/**
|
||||
* A message the `Actor` sends to itself.
|
||||
* The trigger for the periodic deconstruction task.
|
||||
*/
|
||||
private final case class PeriodicTaskCulling()
|
||||
|
||||
/**
|
||||
* An entry that stores vehicle deconstruction tasks.
|
||||
* @param vehicle the `Vehicle` object
|
||||
* @param zone the `Zone` that the vehicle currently occupies
|
||||
* @param survivalTime how long until the vehicle will be deconstructed in nanoseconds
|
||||
* @param logTime when this deconstruction request was initially created in nanoseconds;
|
||||
* initialized by default to a "now"
|
||||
*/
|
||||
private final case class VehicleEntry(vehicle : Vehicle, zone : Zone, survivalTime : Long, logTime : Long = System.nanoTime())
|
||||
}
|
||||
|
|
@ -4,9 +4,10 @@ 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, ServiceManager}
|
||||
import net.psforever.packet.game.objectcreate.{DroppedItemData, ObjectClass, ObjectCreateMessageParent, PlacementData}
|
||||
import net.psforever.packet.game.{ObjectCreateMessage, PlanetSideGUID, PlayerStateMessageUpstream}
|
||||
import net.psforever.types._
|
||||
import services.{RemoverActor, Service, ServiceManager}
|
||||
import services.avatar._
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -93,49 +94,89 @@ class ConcealPlayerTest extends ActorTest {
|
|||
}
|
||||
|
||||
class EquipmentInHandTest extends ActorTest {
|
||||
val tool = Tool(GlobalDefinitions.beamer)
|
||||
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")
|
||||
|
||||
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 {
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
service ! AvatarServiceMessage("test", AvatarAction.EquipmentInHand(PlanetSideGUID(10), PlanetSideGUID(11), 2, tool))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentInHand(PlanetSideGUID(11), 2, tool)))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentInHand(pkt)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EquipmentOnGroundTest extends ActorTest {
|
||||
class DroptItemTest 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], "drop-item-test-resolver")
|
||||
zone.Actor = system.actorOf(Props(classOf[ZoneActor], zone), "drop-item-test-zone")
|
||||
zone.Actor ! Zone.Init()
|
||||
|
||||
val toolDef = GlobalDefinitions.beamer
|
||||
val tool = Tool(toolDef)
|
||||
tool.AmmoSlots.head.Box.GUID = PlanetSideGUID(1)
|
||||
val cdata = toolDef.Packet.ConstructorData(tool).get
|
||||
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 EquipmentOnGround" in {
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
"pass DropItem" in {
|
||||
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)))
|
||||
service ! AvatarServiceMessage("test", AvatarAction.DropItem(PlanetSideGUID(10), tool, zone))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.DropItem(pkt)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LoadPlayerTest extends ActorTest {
|
||||
val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, 1))
|
||||
val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
|
||||
obj.GUID = PlanetSideGUID(10)
|
||||
obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(11)
|
||||
val pdata = obj.Definition.Packet.DetailedConstructorData(obj).get
|
||||
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[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
service ! AvatarServiceMessage("test", AvatarAction.LoadPlayer(PlanetSideGUID(10), pdata))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.LoadPlayer(pdata)))
|
||||
//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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -193,6 +234,45 @@ class PlayerStateTest extends ActorTest {
|
|||
}
|
||||
}
|
||||
|
||||
class PickupItemATest extends ActorTest {
|
||||
val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
|
||||
obj.GUID = PlanetSideGUID(10)
|
||||
obj.Slot(5).Equipment.get.GUID = PlanetSideGUID(11)
|
||||
|
||||
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(10), 0),
|
||||
toolDef.Packet.ConstructorData(tool).get
|
||||
)
|
||||
|
||||
"pass PickUpItem as EquipmentInHand (visible pistol slot)" in {
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
service ! AvatarServiceMessage("test", AvatarAction.PickupItem(PlanetSideGUID(10), Zone.Nowhere, obj, 0, tool))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.EquipmentInHand(pkt)))
|
||||
}
|
||||
}
|
||||
|
||||
class PickupItemBTest extends ActorTest {
|
||||
val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
|
||||
val tool = Tool(GlobalDefinitions.beamer)
|
||||
tool.GUID = PlanetSideGUID(40)
|
||||
|
||||
"pass PickUpItem as ObjectDelete (not visible inventory space)" in {
|
||||
ServiceManager.boot(system)
|
||||
val service = system.actorOf(Props[AvatarService], AvatarServiceTest.TestName)
|
||||
service ! Service.Join("test")
|
||||
service ! AvatarServiceMessage("test", AvatarAction.PickupItem(PlanetSideGUID(10), Zone.Nowhere, obj, 6, tool))
|
||||
expectMsg(AvatarServiceResponse("/test/Avatar", PlanetSideGUID(10), AvatarResponse.ObjectDelete(tool.GUID, 0)))
|
||||
}
|
||||
}
|
||||
|
||||
class ReloadTest extends ActorTest {
|
||||
"AvatarService" should {
|
||||
"pass Reload" in {
|
||||
|
|
@ -308,7 +388,7 @@ class AvatarReleaseTest extends ActorTest {
|
|||
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))
|
||||
val obj = Player(Avatar("TestCharacter", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
|
||||
obj.Continent = "test"
|
||||
obj.Release
|
||||
|
||||
|
|
@ -320,14 +400,14 @@ class AvatarReleaseTest extends ActorTest {
|
|||
taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
zone.Population ! Zone.Corpse.Add(obj)
|
||||
expectNoMsg(100 milliseconds) //spacer
|
||||
expectNoMsg(200 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
|
||||
service ! AvatarServiceMessage("test", AvatarAction.Release(obj, zone, Some(1 second))) //alive for one second
|
||||
|
||||
val reply1 = receiveOne(100 milliseconds)
|
||||
val reply1 = receiveOne(200 milliseconds)
|
||||
assert(reply1.isInstanceOf[AvatarServiceResponse])
|
||||
val reply1msg = reply1.asInstanceOf[AvatarServiceResponse]
|
||||
assert(reply1msg.toChannel == "/test/Avatar")
|
||||
|
|
@ -343,7 +423,7 @@ class AvatarReleaseTest extends ActorTest {
|
|||
assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete])
|
||||
assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid)
|
||||
|
||||
expectNoMsg(1000 milliseconds)
|
||||
expectNoMsg(1 seconds)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
assert(!obj.HasGUID)
|
||||
}
|
||||
|
|
@ -357,7 +437,7 @@ class AvatarReleaseEarly1Test extends ActorTest {
|
|||
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))
|
||||
val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
|
||||
obj.Continent = "test"
|
||||
obj.Release
|
||||
|
||||
|
|
@ -369,14 +449,14 @@ class AvatarReleaseEarly1Test extends ActorTest {
|
|||
taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
zone.Population ! Zone.Corpse.Add(obj)
|
||||
expectNoMsg(100 milliseconds) //spacer
|
||||
expectNoMsg(200 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)
|
||||
val reply1 = receiveOne(200 milliseconds)
|
||||
assert(reply1.isInstanceOf[AvatarServiceResponse])
|
||||
val reply1msg = reply1.asInstanceOf[AvatarServiceResponse]
|
||||
assert(reply1msg.toChannel == "/test/Avatar")
|
||||
|
|
@ -384,8 +464,8 @@ class AvatarReleaseEarly1Test extends ActorTest {
|
|||
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)
|
||||
service ! AvatarServiceMessage.Corpse(RemoverActor.HurrySpecific(List(obj), zone)) //IMPORTANT: ONE ENTRY
|
||||
val reply2 = receiveOne(200 milliseconds)
|
||||
assert(reply2.isInstanceOf[AvatarServiceResponse])
|
||||
val reply2msg = reply2.asInstanceOf[AvatarServiceResponse]
|
||||
assert(reply2msg.toChannel.equals("/test/Avatar"))
|
||||
|
|
@ -393,7 +473,7 @@ class AvatarReleaseEarly1Test extends ActorTest {
|
|||
assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete])
|
||||
assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid)
|
||||
|
||||
expectNoMsg(600 milliseconds)
|
||||
expectNoMsg(1 seconds)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
assert(!obj.HasGUID)
|
||||
}
|
||||
|
|
@ -407,8 +487,8 @@ class AvatarReleaseEarly2Test extends ActorTest {
|
|||
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))
|
||||
val objAlt = Player(Avatar("TestCharacter2", PlanetSideEmpire.NC, CharacterGender.Male, 1, CharacterVoice.Voice1)) //necessary clutter
|
||||
val obj = Player(Avatar("TestCharacter1", PlanetSideEmpire.VS, CharacterGender.Female, 1, CharacterVoice.Voice1))
|
||||
obj.Continent = "test"
|
||||
obj.Release
|
||||
|
||||
|
|
@ -420,14 +500,14 @@ class AvatarReleaseEarly2Test extends ActorTest {
|
|||
taskResolver ! GUIDTask.RegisterObjectTask(obj)(zone.GUID)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
zone.Population ! Zone.Corpse.Add(obj)
|
||||
expectNoMsg(100 milliseconds) //spacer
|
||||
expectNoMsg(200 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)
|
||||
val reply1 = receiveOne(200 milliseconds)
|
||||
assert(reply1.isInstanceOf[AvatarServiceResponse])
|
||||
val reply1msg = reply1.asInstanceOf[AvatarServiceResponse]
|
||||
assert(reply1msg.toChannel == "/test/Avatar")
|
||||
|
|
@ -435,7 +515,7 @@ class AvatarReleaseEarly2Test extends ActorTest {
|
|||
assert(reply1msg.replyMessage.isInstanceOf[AvatarResponse.Release])
|
||||
assert(reply1msg.replyMessage.asInstanceOf[AvatarResponse.Release].player == obj)
|
||||
|
||||
service ! AvatarServiceMessage.RemoveSpecificCorpse(List(objAlt, obj)) //IMPORTANT: TWO ENTRIES
|
||||
service ! AvatarServiceMessage.Corpse(RemoverActor.HurrySpecific(List(objAlt, obj), zone)) //IMPORTANT: TWO ENTRIES
|
||||
val reply2 = receiveOne(100 milliseconds)
|
||||
assert(reply2.isInstanceOf[AvatarServiceResponse])
|
||||
val reply2msg = reply2.asInstanceOf[AvatarServiceResponse]
|
||||
|
|
@ -444,7 +524,7 @@ class AvatarReleaseEarly2Test extends ActorTest {
|
|||
assert(reply2msg.replyMessage.isInstanceOf[AvatarResponse.ObjectDelete])
|
||||
assert(reply2msg.replyMessage.asInstanceOf[AvatarResponse.ObjectDelete].item_guid == guid)
|
||||
|
||||
expectNoMsg(600 milliseconds)
|
||||
expectNoMsg(1 seconds)
|
||||
assert(zone.Corpses.isEmpty)
|
||||
assert(!obj.HasGUID)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -452,45 +452,44 @@ class PacketCodingActorHTest extends ActorTest {
|
|||
}
|
||||
|
||||
class PacketCodingActorITest extends ActorTest {
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
val pos : PlacementData = PlacementData(Vector3.Zero, Vector3.Zero)
|
||||
val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
|
||||
BasicCharacterData("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, CharacterVoice.Voice1),
|
||||
3,
|
||||
false,
|
||||
false,
|
||||
ExoSuitType.Standard,
|
||||
"",
|
||||
0,
|
||||
false,
|
||||
2.8125f, 210.9375f,
|
||||
true,
|
||||
GrenadeState.None,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
RibbonBars()
|
||||
)
|
||||
var char : (Option[Int])=>DetailedCharacterData = DetailedCharacterData(
|
||||
0,
|
||||
0,
|
||||
100, 100,
|
||||
50,
|
||||
1, 7, 7,
|
||||
100, 100,
|
||||
List(CertificationType.StandardAssault, CertificationType.MediumAssault, CertificationType.ATV, CertificationType.Harasser, CertificationType.StandardExoSuit, CertificationType.AgileExoSuit, CertificationType.ReinforcedExoSuit),
|
||||
List(),
|
||||
List(),
|
||||
List.empty,
|
||||
None
|
||||
)
|
||||
val obj = DetailedPlayerData(pos, app, char, InventoryData(Nil), DrawnSlot.None)
|
||||
val pkt = MultiPacketBundle(List(ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), obj)))
|
||||
val string_hex = hex"000900001879060000bc84b000000000000000000002040000097049006c006c006c004900490049006c006c006c0049006c0049006c006c0049006c006c006c0049006c006c0049008452700000000000000000000000000000002000000fe6a703fffffffffffffffffffffffffffffffc00000000000000000000000000000000000000019001900064000001007ec800c80000000000000000000000000000000000000001c00042c54686c7000000000000000000000000000000000000000000000000000000000000000000000000200700"
|
||||
|
||||
"PacketCodingActor" should {
|
||||
"bundle an r-originating packet into an l-facing SlottedMetaPacket byte stream data (SlottedMetaPacket)" in {
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
val obj = DetailedCharacterData(
|
||||
CharacterAppearanceData(
|
||||
PlacementData(Vector3.Zero, Vector3.Zero),
|
||||
BasicCharacterData("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1),
|
||||
3,
|
||||
false,
|
||||
false,
|
||||
ExoSuitType.Standard,
|
||||
"",
|
||||
0,
|
||||
false,
|
||||
2.8125f, 210.9375f,
|
||||
true,
|
||||
GrenadeState.None,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
RibbonBars()
|
||||
),
|
||||
0,
|
||||
0,
|
||||
100, 100,
|
||||
50,
|
||||
1, 7, 7,
|
||||
100, 100,
|
||||
List(CertificationType.StandardAssault,CertificationType.MediumAssault,CertificationType.ATV,CertificationType.Harasser,CertificationType.StandardExoSuit,CertificationType.AgileExoSuit,CertificationType.ReinforcedExoSuit),
|
||||
List(),
|
||||
List(),
|
||||
List.empty,
|
||||
None,
|
||||
Some(InventoryData(Nil)),
|
||||
DrawnSlot.None
|
||||
)
|
||||
val pkt = MultiPacketBundle(List(ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), obj)))
|
||||
val string_hex = hex"000900001879060000bc84b000000000000000000002040000097049006c006c006c004900490049006c006c006c0049006c0049006c006c0049006c006c006c0049006c006c0049008452700000000000000000000000000000002000000fe6a703fffffffffffffffffffffffffffffffc00000000000000000000000000000000000000019001900064000001007ec800c80000000000000000000000000000000000000001c00042c54686c7000000000000000000000000000000000000000000000000000000000000000000000000200700"
|
||||
|
||||
val probe1 = TestProbe()
|
||||
val probe2 = system.actorOf(Props(classOf[ActorTest.MDCTestProbe], probe1), "mdc-probe")
|
||||
val pca : ActorRef = system.actorOf(Props[PacketCodingActor], "pca")
|
||||
|
|
@ -547,25 +546,25 @@ class PacketCodingActorJTest extends ActorTest {
|
|||
|
||||
class PacketCodingActorKTest extends ActorTest {
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
val obj = DetailedCharacterData(
|
||||
CharacterAppearanceData(
|
||||
PlacementData(Vector3.Zero, Vector3.Zero),
|
||||
BasicCharacterData("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, 1),
|
||||
3,
|
||||
false,
|
||||
false,
|
||||
ExoSuitType.Standard,
|
||||
"",
|
||||
0,
|
||||
false,
|
||||
2.8125f, 210.9375f,
|
||||
true,
|
||||
GrenadeState.None,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
RibbonBars()
|
||||
),
|
||||
val pos : PlacementData = PlacementData(Vector3.Zero, Vector3.Zero)
|
||||
val app : (Int)=>CharacterAppearanceData = CharacterAppearanceData(
|
||||
BasicCharacterData("IlllIIIlllIlIllIlllIllI", PlanetSideEmpire.VS, CharacterGender.Female, 41, CharacterVoice.Voice1),
|
||||
3,
|
||||
false,
|
||||
false,
|
||||
ExoSuitType.Standard,
|
||||
"",
|
||||
0,
|
||||
false,
|
||||
2.8125f, 210.9375f,
|
||||
true,
|
||||
GrenadeState.None,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
RibbonBars()
|
||||
)
|
||||
var char : (Option[Int])=>DetailedCharacterData = DetailedCharacterData(
|
||||
0,
|
||||
0,
|
||||
100, 100,
|
||||
|
|
@ -576,10 +575,9 @@ class PacketCodingActorKTest extends ActorTest {
|
|||
List(),
|
||||
List("xpe_sanctuary_help", "xpe_th_firemodes", "used_beamer", "map13"),
|
||||
List.empty,
|
||||
None,
|
||||
Some(InventoryData(Nil)),
|
||||
DrawnSlot.None
|
||||
None
|
||||
)
|
||||
val obj = DetailedPlayerData(pos, app, char, InventoryData(Nil), DrawnSlot.None)
|
||||
val list = List(
|
||||
ObjectCreateDetailedMessage(0x79, PlanetSideGUID(75), obj),
|
||||
ObjectDeleteMessage(PlanetSideGUID(1103), 2),
|
||||
|
|
|
|||
537
pslogin/src/test/scala/RemoverActorTest.scala
Normal file
537
pslogin/src/test/scala/RemoverActorTest.scala
Normal file
|
|
@ -0,0 +1,537 @@
|
|||
//// Copyright (c) 2017 PSForever
|
||||
//import akka.actor.{ActorRef, Props}
|
||||
//import akka.routing.RandomPool
|
||||
//import akka.testkit.TestProbe
|
||||
//import net.psforever.objects.PlanetSideGameObject
|
||||
//import net.psforever.objects.definition.{EquipmentDefinition, ObjectDefinition}
|
||||
//import net.psforever.objects.equipment.Equipment
|
||||
//import net.psforever.objects.guid.TaskResolver
|
||||
//import net.psforever.objects.zones.{Zone, ZoneMap}
|
||||
//import net.psforever.packet.game.PlanetSideGUID
|
||||
//import services.{RemoverActor, ServiceManager}
|
||||
//
|
||||
//import scala.concurrent.duration._
|
||||
//
|
||||
//class StandardRemoverActorTest extends ActorTest {
|
||||
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
|
||||
//
|
||||
// "RemoverActor" should {
|
||||
// "handle a simple task" in {
|
||||
// expectNoMsg(500 milliseconds)
|
||||
// val probe = TestProbe()
|
||||
// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
|
||||
// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere)
|
||||
//
|
||||
// val reply1 = probe.receiveOne(200 milliseconds)
|
||||
// assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
|
||||
// val reply2 = probe.receiveOne(200 milliseconds)
|
||||
// assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
|
||||
// probe.expectNoMsg(1 seconds) //delay
|
||||
// val reply3 = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
|
||||
// val reply4 = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
|
||||
// val reply5 = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
|
||||
// val reply6 = probe.receiveOne(500 milliseconds)
|
||||
// assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
|
||||
// val reply7 = probe.receiveOne(500 milliseconds)
|
||||
// assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//class DelayedRemoverActorTest extends ActorTest {
|
||||
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
|
||||
//
|
||||
// "RemoverActor" should {
|
||||
// "handle a simple task (timed)" in {
|
||||
// expectNoMsg(500 milliseconds)
|
||||
// val probe = TestProbe()
|
||||
// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
|
||||
// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(100 milliseconds))
|
||||
//
|
||||
// val reply1 = probe.receiveOne(200 milliseconds)
|
||||
// assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
|
||||
// val reply2 = probe.receiveOne(200 milliseconds)
|
||||
// assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
|
||||
// //no delay
|
||||
// val reply3 = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
|
||||
// val reply4 = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
|
||||
// val reply5 = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
|
||||
// val reply6 = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
|
||||
// val reply7 = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//class ExcludedRemoverActorTest extends ActorTest {
|
||||
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
|
||||
// val AlternateTestObject = new PlanetSideGameObject() { def Definition = new ObjectDefinition(0) { } }
|
||||
//
|
||||
// "RemoverActor" should {
|
||||
// "allow only specific objects" in {
|
||||
// expectNoMsg(500 milliseconds)
|
||||
// val probe = TestProbe()
|
||||
// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
|
||||
// remover ! RemoverActor.AddTask(AlternateTestObject, Zone.Nowhere)
|
||||
//
|
||||
// val reply1 = probe.receiveOne(200 milliseconds)
|
||||
// assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
|
||||
// expectNoMsg(2 seconds)
|
||||
// //RemoverActor is stalled because it received an object that it was not allowed to act upon
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//class MultipleRemoverActorTest extends ActorTest {
|
||||
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
|
||||
// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
|
||||
//
|
||||
// "RemoverActor" should {
|
||||
// "work on parallel tasks" in {
|
||||
// expectNoMsg(500 milliseconds)
|
||||
// val probe = TestProbe()
|
||||
// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
|
||||
// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere)
|
||||
// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere)
|
||||
//
|
||||
// val replies = probe.receiveN(14, 5 seconds)
|
||||
// var ita : Int = 0
|
||||
// var ija : Int = 0
|
||||
// var fja : Int = 0
|
||||
// var cta : Int = 0
|
||||
// var sja : Int = 0
|
||||
// var dta : Int = 0
|
||||
// var dtr : Int = 0
|
||||
// replies.collect {
|
||||
// case RemoverActorTest.InclusionTestAlert() => ita += 1
|
||||
// case RemoverActorTest.InitialJobAlert() => ija += 1
|
||||
// case RemoverActorTest.FirstJobAlert() => fja += 1
|
||||
// case RemoverActorTest.ClearanceTestAlert() => cta += 1
|
||||
// case RemoverActorTest.SecondJobAlert() => sja += 1
|
||||
// case RemoverActorTest.DeletionTaskAlert() => dta += 1
|
||||
// case RemoverActorTest.DeletionTaskRunAlert() => dtr += 1
|
||||
// case msg => assert(false, s"$msg")
|
||||
// }
|
||||
// assert(ita == 2 && ija == 2 && fja == 2 && cta == 2 && sja == 2 && dta == 2 && dtr == 2)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//class HurrySpecificRemoverActorTest extends ActorTest {
|
||||
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
|
||||
//
|
||||
// "RemoverActor" should {
|
||||
// "be able to hurry certain tasks" in {
|
||||
// expectNoMsg(500 milliseconds)
|
||||
// val probe = TestProbe()
|
||||
// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
|
||||
// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(10 minutes)) //TEN MINUTE WAIT
|
||||
//
|
||||
// val reply1 = probe.receiveOne(200 milliseconds)
|
||||
// assert(reply1.isInstanceOf[RemoverActorTest.InclusionTestAlert])
|
||||
// val reply2 = probe.receiveOne(200 milliseconds)
|
||||
// assert(reply2.isInstanceOf[RemoverActorTest.InitialJobAlert])
|
||||
// probe.expectNoMsg(3 seconds) //long delay, longer than standard but not yet 10 minutes
|
||||
// remover ! RemoverActor.HurrySpecific(List(RemoverActorTest.TestObject), Zone.Nowhere) //hurried
|
||||
// val reply3 = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
|
||||
// val reply4 = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
|
||||
// val reply5 = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
|
||||
// val reply6 = probe.receiveOne(500 milliseconds)
|
||||
// assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
|
||||
// val reply7 = probe.receiveOne(500 milliseconds)
|
||||
// assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//class HurrySelectionRemoverActorTest extends ActorTest {
|
||||
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
|
||||
// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
|
||||
//
|
||||
// "RemoverActor" should {
|
||||
// "be able to hurry certain tasks, but let others finish normally" in {
|
||||
// expectNoMsg(500 milliseconds)
|
||||
// val probe = TestProbe()
|
||||
// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
|
||||
// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
|
||||
// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(10 seconds))
|
||||
//
|
||||
// val replies = probe.receiveN(4, 5 seconds)
|
||||
// var ita : Int = 0
|
||||
// var ija : Int = 0
|
||||
// replies.collect {
|
||||
// case RemoverActorTest.InclusionTestAlert() => ita += 1
|
||||
// case RemoverActorTest.InitialJobAlert() => ija += 1
|
||||
// case msg => assert(false, s"$msg")
|
||||
// }
|
||||
// assert(ita == 2 && ija == 2)
|
||||
// probe.expectNoMsg(3 seconds) //long delay, longer than standard but not yet 5 seconds
|
||||
// remover ! RemoverActor.HurrySpecific(List(RemoverActorTest.TestObject), Zone.Nowhere) //hurried
|
||||
// //first
|
||||
// val reply3a = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply3a.isInstanceOf[RemoverActorTest.FirstJobAlert])
|
||||
// val reply4a = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply4a.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
|
||||
// val reply5a = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply5a.isInstanceOf[RemoverActorTest.SecondJobAlert])
|
||||
// val reply6a = probe.receiveOne(500 milliseconds)
|
||||
// assert(reply6a.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
|
||||
// val reply7a = probe.receiveOne(500 milliseconds)
|
||||
// assert(reply7a.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
|
||||
// //second
|
||||
// remover ! RemoverActor.HurrySpecific(List(TestObject2), Zone.Nowhere) //hurried
|
||||
// val reply3b = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply3b.isInstanceOf[RemoverActorTest.FirstJobAlert])
|
||||
// val reply4b = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply4b.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
|
||||
// val reply5b = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply5b.isInstanceOf[RemoverActorTest.SecondJobAlert])
|
||||
// val reply6b = probe.receiveOne(500 milliseconds)
|
||||
// assert(reply6b.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
|
||||
// val reply7b = probe.receiveOne(500 milliseconds)
|
||||
// assert(reply7b.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//class HurryMultipleRemoverActorTest extends ActorTest {
|
||||
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
|
||||
// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
|
||||
// final val TestObject3 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(3) } }
|
||||
//
|
||||
// "RemoverActor" should {
|
||||
// "be able to hurry certain tasks, but only valid ones" in {
|
||||
// expectNoMsg(500 milliseconds)
|
||||
// val probe = TestProbe()
|
||||
// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
|
||||
// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
|
||||
// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(5 seconds))
|
||||
//
|
||||
// val replies = probe.receiveN(4, 5 seconds)
|
||||
// var ita : Int = 0
|
||||
// var ija : Int = 0
|
||||
// replies.collect {
|
||||
// case RemoverActorTest.InclusionTestAlert() => ita += 1
|
||||
// case RemoverActorTest.InitialJobAlert() => ija += 1
|
||||
// case msg => assert(false, s"$msg")
|
||||
// }
|
||||
// assert(ita == 2 && ija == 2)
|
||||
// probe.expectNoMsg(3 seconds) //long delay, longer than standard but not yet 5 seconds
|
||||
// remover ! RemoverActor.HurrySpecific(List(RemoverActorTest.TestObject, TestObject3), Zone.Nowhere) //multiple hurried, only one valid
|
||||
// //first
|
||||
// val reply3a = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply3a.isInstanceOf[RemoverActorTest.FirstJobAlert])
|
||||
// val reply4a = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply4a.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
|
||||
// val reply5a = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply5a.isInstanceOf[RemoverActorTest.SecondJobAlert])
|
||||
// val reply6a = probe.receiveOne(500 milliseconds)
|
||||
// assert(reply6a.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
|
||||
// val reply7a = probe.receiveOne(500 milliseconds)
|
||||
// assert(reply7a.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
|
||||
// //second
|
||||
// remover ! RemoverActor.HurrySpecific(List(TestObject2), Zone.Nowhere) //hurried
|
||||
// val reply3b = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply3b.isInstanceOf[RemoverActorTest.FirstJobAlert])
|
||||
// val reply4b = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply4b.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
|
||||
// val reply5b = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply5b.isInstanceOf[RemoverActorTest.SecondJobAlert])
|
||||
// val reply6b = probe.receiveOne(500 milliseconds)
|
||||
// assert(reply6b.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
|
||||
// val reply7b = probe.receiveOne(500 milliseconds)
|
||||
// assert(reply7b.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//class HurryByZoneRemoverActorTest extends ActorTest {
|
||||
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
|
||||
// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
|
||||
// final val TestObject3 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(3) } }
|
||||
// final val zone = new Zone("test", new ZoneMap("test-map"), 11)
|
||||
//
|
||||
// "RemoverActor" should {
|
||||
// "be able to hurry certain tasks by their zone" in {
|
||||
// expectNoMsg(500 milliseconds)
|
||||
// val probe = TestProbe()
|
||||
// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
|
||||
// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
|
||||
// remover ! RemoverActor.AddTask(TestObject2, zone, Some(5 seconds))
|
||||
// remover ! RemoverActor.AddTask(TestObject3, Zone.Nowhere, Some(5 seconds))
|
||||
//
|
||||
// val replies1 = probe.receiveN(6, 5 seconds)
|
||||
// var ita : Int = 0
|
||||
// var ija : Int = 0
|
||||
// replies1.collect {
|
||||
// case RemoverActorTest.InclusionTestAlert() => ita += 1
|
||||
// case RemoverActorTest.InitialJobAlert() => ija += 1
|
||||
// case msg => assert(false, s"$msg")
|
||||
// }
|
||||
// assert(ita == 3 && ija == 3)
|
||||
// probe.expectNoMsg(3 seconds) //long delay, longer than standard but not yet 5 seconds
|
||||
// remover ! RemoverActor.HurrySpecific(List(), Zone.Nowhere) //multiple hurried, only the two entries with Zone.Nowhere
|
||||
// //
|
||||
// val replies2 = probe.receiveN(10, 5 seconds)
|
||||
// var fja : Int = 0
|
||||
// var cta : Int = 0
|
||||
// var sja : Int = 0
|
||||
// var dta : Int = 0
|
||||
// var dtr : Int = 0
|
||||
// replies2.collect {
|
||||
// case RemoverActorTest.InclusionTestAlert() => ita += 1
|
||||
// case RemoverActorTest.InitialJobAlert() => ija += 1
|
||||
// case RemoverActorTest.FirstJobAlert() => fja += 1
|
||||
// case RemoverActorTest.ClearanceTestAlert() => cta += 1
|
||||
// case RemoverActorTest.SecondJobAlert() => sja += 1
|
||||
// case RemoverActorTest.DeletionTaskAlert() => dta += 1
|
||||
// case RemoverActorTest.DeletionTaskRunAlert() => dtr += 1
|
||||
// case msg => assert(false, s"$msg")
|
||||
// }
|
||||
// assert(fja == 2 && cta == 2 && sja == 2 && dta == 2 && dtr == 2)
|
||||
// //final
|
||||
// remover ! RemoverActor.HurrySpecific(List(), zone) //hurried
|
||||
// val reply3b = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply3b.isInstanceOf[RemoverActorTest.FirstJobAlert])
|
||||
// val reply4b = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply4b.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
|
||||
// val reply5b = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply5b.isInstanceOf[RemoverActorTest.SecondJobAlert])
|
||||
// val reply6b = probe.receiveOne(500 milliseconds)
|
||||
// assert(reply6b.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
|
||||
// val reply7b = probe.receiveOne(500 milliseconds)
|
||||
// assert(reply7b.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//class HurryAllRemoverActorTest extends ActorTest {
|
||||
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
|
||||
// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
|
||||
// final val TestObject3 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(3) } }
|
||||
//
|
||||
// "RemoverActor" should {
|
||||
// "be able to hurry all tasks to completion" in {
|
||||
// expectNoMsg(500 milliseconds)
|
||||
// val probe = TestProbe()
|
||||
// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
|
||||
// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(20 seconds))
|
||||
// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(15 seconds))
|
||||
// remover ! RemoverActor.AddTask(TestObject3, Zone.Nowhere, Some(10 seconds))
|
||||
//
|
||||
// val replies1 = probe.receiveN(6, 5 seconds)
|
||||
// var ita : Int = 0
|
||||
// var ija : Int = 0
|
||||
// replies1.collect {
|
||||
// case RemoverActorTest.InclusionTestAlert() => ita += 1
|
||||
// case RemoverActorTest.InitialJobAlert() => ija += 1
|
||||
// case msg => assert(false, s"$msg")
|
||||
// }
|
||||
// assert(ita == 3 && ija == 3)
|
||||
// probe.expectNoMsg(3 seconds) //long delay, longer than standard but not yet longer than any of the tasks
|
||||
// remover ! RemoverActor.HurryAll() //all hurried
|
||||
// //
|
||||
// val replies2 = probe.receiveN(15, 5 seconds)
|
||||
// var fja : Int = 0
|
||||
// var cta : Int = 0
|
||||
// var sja : Int = 0
|
||||
// var dta : Int = 0
|
||||
// var dtr : Int = 0
|
||||
// replies2.collect {
|
||||
// case RemoverActorTest.InclusionTestAlert() => ita += 1
|
||||
// case RemoverActorTest.InitialJobAlert() => ija += 1
|
||||
// case RemoverActorTest.FirstJobAlert() => fja += 1
|
||||
// case RemoverActorTest.ClearanceTestAlert() => cta += 1
|
||||
// case RemoverActorTest.SecondJobAlert() => sja += 1
|
||||
// case RemoverActorTest.DeletionTaskAlert() => dta += 1
|
||||
// case RemoverActorTest.DeletionTaskRunAlert() => dtr += 1
|
||||
// case msg => assert(false, s"$msg")
|
||||
// }
|
||||
// assert(fja == 3 && cta == 3 && sja == 3 && dta == 3 && dtr == 3)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//class ClearSelectionRemoverActorTest extends ActorTest {
|
||||
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
|
||||
// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
|
||||
//
|
||||
// "RemoverActor" should {
|
||||
// "be able to clear certain tasks" in {
|
||||
// expectNoMsg(500 milliseconds)
|
||||
// val probe = TestProbe()
|
||||
// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
|
||||
// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
|
||||
// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(5 seconds))
|
||||
//
|
||||
// val replies = probe.receiveN(4, 5 seconds)
|
||||
// var ita : Int = 0
|
||||
// var ija : Int = 0
|
||||
// replies.collect {
|
||||
// case RemoverActorTest.InclusionTestAlert() => ita += 1
|
||||
// case RemoverActorTest.InitialJobAlert() => ija += 1
|
||||
// case msg => assert(false, s"$msg")
|
||||
// }
|
||||
// assert(ita == 2 && ija == 2)
|
||||
// probe.expectNoMsg(4 seconds) //long delay, longer than standard but not yet 5 seconds
|
||||
// remover ! RemoverActor.ClearSpecific(List(RemoverActorTest.TestObject), Zone.Nowhere) //cleared
|
||||
// //
|
||||
// val reply3 = probe.receiveOne(2 seconds)
|
||||
// assert(reply3.isInstanceOf[RemoverActorTest.FirstJobAlert])
|
||||
// val reply4 = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply4.isInstanceOf[RemoverActorTest.ClearanceTestAlert])
|
||||
// val reply5 = probe.receiveOne(300 milliseconds)
|
||||
// assert(reply5.isInstanceOf[RemoverActorTest.SecondJobAlert])
|
||||
// val reply6 = probe.receiveOne(500 milliseconds)
|
||||
// assert(reply6.isInstanceOf[RemoverActorTest.DeletionTaskAlert])
|
||||
// val reply7 = probe.receiveOne(500 milliseconds)
|
||||
// assert(reply7.isInstanceOf[RemoverActorTest.DeletionTaskRunAlert])
|
||||
// //wait
|
||||
// probe.expectNoMsg(2 seconds) //nothing more to do
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//class ClearAllRemoverActorTest extends ActorTest {
|
||||
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
|
||||
// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
|
||||
//
|
||||
// "RemoverActor" should {
|
||||
// "be able to clear all tasks, with no more work on them" in {
|
||||
// expectNoMsg(500 milliseconds)
|
||||
// val probe = TestProbe()
|
||||
// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
|
||||
// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
|
||||
// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(5 seconds))
|
||||
//
|
||||
// val replies = probe.receiveN(4, 5 seconds)
|
||||
// var ita : Int = 0
|
||||
// var ija : Int = 0
|
||||
// replies.collect {
|
||||
// case RemoverActorTest.InclusionTestAlert() => ita += 1
|
||||
// case RemoverActorTest.InitialJobAlert() => ija += 1
|
||||
// case msg => assert(false, s"$msg")
|
||||
// }
|
||||
// assert(ita == 2 && ija == 2)
|
||||
// probe.expectNoMsg(4 seconds) //long delay, longer than standard but not yet 5 seconds
|
||||
// remover ! RemoverActor.ClearAll() //cleared
|
||||
// //wait
|
||||
// probe.expectNoMsg(3 seconds) //nothing more to do
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//class EarlyDeathRemoverActorTest extends ActorTest {
|
||||
// ServiceManager.boot ! ServiceManager.Register(RandomPool(2).props(Props[TaskResolver]), "taskResolver")
|
||||
// final val TestObject2 = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(2) } }
|
||||
//
|
||||
// "RemoverActor" should {
|
||||
// "be able to hurry certain tasks" in {
|
||||
// expectNoMsg(500 milliseconds)
|
||||
// val probe = TestProbe()
|
||||
// val remover = system.actorOf(Props(classOf[RemoverActorTest.TestRemover], probe), "test-remover")
|
||||
// remover ! RemoverActor.AddTask(RemoverActorTest.TestObject, Zone.Nowhere, Some(5 seconds))
|
||||
// remover ! RemoverActor.AddTask(TestObject2, Zone.Nowhere, Some(5 seconds))
|
||||
//
|
||||
// val replies = probe.receiveN(4, 5 seconds)
|
||||
// var ita : Int = 0
|
||||
// var ija : Int = 0
|
||||
// replies.collect {
|
||||
// case RemoverActorTest.InclusionTestAlert() => ita += 1
|
||||
// case RemoverActorTest.InitialJobAlert() => ija += 1
|
||||
// case msg => assert(false, s"$msg")
|
||||
// }
|
||||
// assert(ita == 2 && ija == 2)
|
||||
// probe.expectNoMsg(2 seconds)
|
||||
// remover ! akka.actor.PoisonPill
|
||||
// //
|
||||
// val replies2 = probe.receiveN(8, 5 seconds)
|
||||
// var fja : Int = 0
|
||||
// var cta : Int = 0
|
||||
// var sja : Int = 0
|
||||
// var dta : Int = 0
|
||||
// var dtr : Int = 0
|
||||
// replies2.collect {
|
||||
// case RemoverActorTest.FirstJobAlert() => fja += 1
|
||||
// case RemoverActorTest.ClearanceTestAlert() => cta += 1
|
||||
// case RemoverActorTest.SecondJobAlert() => sja += 1
|
||||
// case RemoverActorTest.DeletionTaskAlert() => dta += 1
|
||||
// case RemoverActorTest.DeletionTaskRunAlert() => dtr += 1
|
||||
// case msg => assert(false, s"$msg")
|
||||
// }
|
||||
// assert(fja == 2 && cta == 0 && sja == 2 && dta == 2 && dtr == 2) //no clearance tests
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//object RemoverActorTest {
|
||||
// final val TestObject = new Equipment() { def Definition = new EquipmentDefinition(0) { GUID = PlanetSideGUID(1) } }
|
||||
//
|
||||
// final case class InclusionTestAlert()
|
||||
//
|
||||
// final case class InitialJobAlert()
|
||||
//
|
||||
// final case class FirstJobAlert()
|
||||
//
|
||||
// final case class SecondJobAlert()
|
||||
//
|
||||
// final case class ClearanceTestAlert()
|
||||
//
|
||||
// final case class DeletionTaskAlert()
|
||||
//
|
||||
// final case class DeletionTaskRunAlert()
|
||||
//
|
||||
// class TestRemover(probe : TestProbe) extends RemoverActor {
|
||||
// import net.psforever.objects.guid.{Task, TaskResolver}
|
||||
// val FirstStandardDuration = 1 seconds
|
||||
//
|
||||
// val SecondStandardDuration = 100 milliseconds
|
||||
//
|
||||
// def InclusionTest(entry : RemoverActor.Entry) : Boolean = {
|
||||
// probe.ref ! InclusionTestAlert()
|
||||
// entry.obj.isInstanceOf[Equipment]
|
||||
// }
|
||||
//
|
||||
// def InitialJob(entry : RemoverActor.Entry) : Unit = {
|
||||
// probe.ref ! InitialJobAlert()
|
||||
// }
|
||||
//
|
||||
// def FirstJob(entry : RemoverActor.Entry) : Unit = {
|
||||
// probe.ref ! FirstJobAlert()
|
||||
// }
|
||||
//
|
||||
// override def SecondJob(entry : RemoverActor.Entry) : Unit = {
|
||||
// probe.ref ! SecondJobAlert()
|
||||
// super.SecondJob(entry)
|
||||
// }
|
||||
//
|
||||
// def ClearanceTest(entry : RemoverActor.Entry) : Boolean = {
|
||||
// probe.ref ! ClearanceTestAlert()
|
||||
// true
|
||||
// }
|
||||
//
|
||||
// def DeletionTask(entry : RemoverActor.Entry) : TaskResolver.GiveTask = {
|
||||
// probe.ref ! DeletionTaskAlert()
|
||||
// TaskResolver.GiveTask(new Task() {
|
||||
// private val localProbe = probe
|
||||
//
|
||||
// override def isComplete = Task.Resolution.Success
|
||||
//
|
||||
// def Execute(resolver : ActorRef) : Unit = {
|
||||
// localProbe.ref ! DeletionTaskRunAlert()
|
||||
// resolver ! scala.util.Success(this)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
|
@ -68,15 +68,15 @@ class VehicleService5Test extends ActorTest {
|
|||
}
|
||||
}
|
||||
|
||||
class AwarenessTest extends ActorTest {
|
||||
class OwnershipTest extends ActorTest {
|
||||
ServiceManager.boot(system)
|
||||
|
||||
"VehicleService" should {
|
||||
"pass Awareness" in {
|
||||
val service = system.actorOf(Props[VehicleService], "v-service")
|
||||
service ! Service.Join("test")
|
||||
service ! VehicleServiceMessage("test", VehicleAction.Awareness(PlanetSideGUID(10), PlanetSideGUID(11)))
|
||||
expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.Awareness(PlanetSideGUID(11))))
|
||||
service ! VehicleServiceMessage("test", VehicleAction.Ownership(PlanetSideGUID(10), PlanetSideGUID(11)))
|
||||
expectMsg(VehicleServiceResponse("/test/Vehicle", PlanetSideGUID(10), VehicleResponse.Ownership(PlanetSideGUID(11))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue