Splitting single vehicle spawn control process into several subtasks. Vehicle event bus attached to zone objects, especially for interacting with VehicleSpawnControl. Importing SouNourS copy of ServerVehicleOverrideMessage packet and tests.

This commit is contained in:
FateJH 2018-04-03 23:14:44 -04:00
parent dbc2ea8084
commit fde49773cd
22 changed files with 993 additions and 339 deletions

View file

@ -203,12 +203,29 @@ object PsLogin {
)
*/
val continentList = createContinents()
val serviceManager = ServiceManager.boot
serviceManager ! ServiceManager.Register(RandomPool(50).props(Props[TaskResolver]), "taskResolver")
serviceManager ! ServiceManager.Register(Props[AvatarService], "avatar")
serviceManager ! ServiceManager.Register(Props[LocalService], "local")
serviceManager ! ServiceManager.Register(Props[VehicleService], "vehicle")
serviceManager ! ServiceManager.Register(Props(classOf[InterstellarCluster], createContinents()), "galaxy")
serviceManager ! ServiceManager.Register(Props(classOf[InterstellarCluster], continentList), "galaxy")
//attach event bus entry point to each zone
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.{Failure, Success}
implicit val timeout = Timeout(200 milliseconds)
val requestVehicleEventBus : Future[ServiceManager.LookupResult] =
(ServiceManager.serviceManager ask ServiceManager.Lookup("vehicle")).mapTo[ServiceManager.LookupResult]
requestVehicleEventBus.onComplete {
case Success(ServiceManager.LookupResult(_, bus)) =>
continentList.foreach { _.VehicleEvents = bus }
case Failure(_) => ;
//TODO how to fail
}
/** Create two actors for handling the login and world server endpoints */
loginRouter = Props(new SessionRouter("Login", loginTemplate))

View file

@ -409,6 +409,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(ChildObjectStateMessage(object_guid, pitch, yaw))
}
case VehicleResponse.ConcealPlayer(player_guid) =>
sendResponse(GenericObjectActionMessage(player_guid, 36))
case VehicleResponse.DismountVehicle(unk1, unk2) =>
if(tplayer_guid != guid) {
sendResponse(DismountVehicleMsg(guid, unk1, unk2))
@ -456,6 +459,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(ObjectAttachMessage(vehicle_guid, guid, seat))
}
case VehicleResponse.RevealPlayer(player_guid) =>
//TODO any action will cause the player to appear after the effects of ConcealPlayer
case VehicleResponse.SeatPermissions(vehicle_guid, seat_group, permission) =>
if(tplayer_guid != guid) {
sendResponse(PlanetsideAttributeMessage(vehicle_guid, seat_group, permission))
@ -1000,46 +1006,30 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(ItemTransactionResultMessage(msg.terminal_guid, msg.transaction_type, false))
}
case VehicleSpawnPad.ConcealPlayer =>
sendResponse(GenericObjectActionMessage(player.GUID, 36))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ConcealPlayer(player.GUID))
case VehicleSpawnPad.LoadVehicle(vehicle, _/*pad*/) =>
val player_guid = player.GUID
val definition = vehicle.Definition
val objedtId = definition.ObjectId
case VehicleSpawnPad.StartPlayerSeatedInVehicle(vehicle) =>
val vehicle_guid = vehicle.GUID
val vdata = definition.Packet.ConstructorData(vehicle).get
sendResponse(ObjectCreateMessage(objedtId, vehicle_guid, vdata))
continent.Transport ! Zone.SpawnVehicle(vehicle)
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.LoadVehicle(player_guid, vehicle, objedtId, vehicle_guid, vdata))
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 1L)) //mount points off?
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 21, player_guid.guid)) //fte and ownership?
//sendResponse(ObjectAttachMessage(vehicle_guid, player_guid, 0))
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) //cancel queue timeout delay
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, continent, 21L) //temporary drive away from pad delay
vehicle.Actor ! Mountable.TryMount(player, 0)
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 21, player.GUID.guid)) //fte and ownership?
case VehicleSpawnPad.PlayerSeatedInVehicle(vehicle) =>
vehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(vehicle, continent, 21L) //sitting in the vehicle clears the drive away delay
val vehicle_guid = vehicle.GUID
if(player.VehicleSeated.nonEmpty) {
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid)
}
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 0L)) //mount points on?
//sendResponse(PlanetsideAttributeMessage(vehicle_guid, 0, vehicle.Definition.MaxHealth)))
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 68, 0L)) //???
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 113, 0L)) //???
ReloadVehicleAccessPermissions(vehicle)
case VehicleSpawnPad.SpawnPadBlockedWarning(vehicle, warning_count) =>
if(warning_count > 2) {
sendResponse(TriggerSoundMessage(TriggeredSound.Unknown14, vehicle.Position, 20, 1f))
sendResponse(
ChatMsg(ChatMessageType.CMT_TELL, true, "", "\\#FYour vehicle is blocking the spawn pad, and will be deconstructed if not moved.", None)
)
}
case VehicleSpawnPad.ServerVehicleOverrideStart(speed) =>
sendResponse(ServerVehicleOverrideMsg.On(speed))
case VehicleSpawnPad.SpawnPadUnblocked(vehicle_guid) =>
//vehicle has moved away from spawn pad after initial spawn
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) //cancel temporary drive away from pad delay
case VehicleSpawnPad.ServerVehicleOverrideEnd(speed) =>
sendResponse(ServerVehicleOverrideMsg.Off(speed))
case VehicleSpawnPad.PeriodicReminder(msg) =>
sendResponse(ChatMsg(ChatMessageType.CMT_OPEN, true, "", msg, None))
case ListAccountCharacters =>
import net.psforever.objects.definition.converter.CharacterSelectConverter
@ -2753,14 +2743,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
new Task() {
private val localVehicle = obj
private val localPad = pad.Actor
private val localAnnounce = vehicleService
private val localSession : String = sessionId.toString
private val localPlayer = player
private val localVehicleService = vehicleService
private val localZone = continent
override def isComplete : Task.Resolution.Value = {
if(localVehicle.Actor != ActorRef.noSender) {
if(localVehicle.HasGUID) {
Task.Resolution.Success
}
else {
@ -2769,9 +2758,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
def Execute(resolver : ActorRef) : Unit = {
localAnnounce ! VehicleServiceMessage.GiveActorControl(obj, localSession)
localPad ! VehicleSpawnPad.VehicleOrder(localPlayer, localVehicle)
localVehicleService ! VehicleServiceMessage.DelayedVehicleDeconstruction(localVehicle, localZone, 60L)
resolver ! scala.util.Success(this)
}
}, List(RegisterVehicle(obj)))

View file

@ -11,12 +11,14 @@ object VehicleResponse {
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 DismountVehicle(unk1 : Int, unk2 : Boolean) extends Response
final case class InventoryState(obj : PlanetSideGameObject, parent_guid : PlanetSideGUID, start : Int, con_data : ConstructorData) extends Response
final case class KickPassenger(unk1 : Int, unk2 : 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 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

View file

@ -2,11 +2,12 @@
package services.vehicle
import akka.actor.{Actor, ActorRef, Props}
import services.vehicle.support.{DeconstructionActor, DelayedDeconstructionActor, VehicleContextActor}
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.zones.Zone
import services.vehicle.support.{DeconstructionActor, DelayedDeconstructionActor}
import services.{GenericEventBus, Service}
class VehicleService extends Actor {
private val vehicleContext : ActorRef = context.actorOf(Props[VehicleContextActor], "vehicle-context-root")
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
@ -91,14 +92,6 @@ class VehicleService extends Actor {
case _ => ;
}
//message to VehicleContext
case VehicleServiceMessage.GiveActorControl(vehicle, actorName) =>
vehicleContext ! VehicleServiceMessage.GiveActorControl(vehicle, actorName)
//message to VehicleContext
case VehicleServiceMessage.RevokeActorControl(vehicle) =>
vehicleContext ! VehicleServiceMessage.RevokeActorControl(vehicle)
//message to DeconstructionActor
case VehicleServiceMessage.RequestDeleteVehicle(vehicle, continent) =>
vehicleDecon ! DeconstructionActor.RequestDeleteVehicle(vehicle, continent)
@ -117,6 +110,35 @@ class VehicleService extends Actor {
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))
)
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)
case msg =>
log.info(s"Unhandled message $msg from $sender")
}

View file

@ -81,10 +81,13 @@ class DeconstructionActor extends Actor {
vehiclesToScrap.foreach(entry => {
val vehicle = entry.vehicle
val zone = entry.zone
<<<<<<< 18a068c10272376450c412425118c86612af7397
vehicle.Position = Vector3.Zero //somewhere it will not disturb anything
entry.zone.Transport ! Zone.DespawnVehicle(vehicle)
=======
vehicle.Position = Vector3.Zero
>>>>>>> Splitting single vehicle spawn control process into several subtasks with their own control Actor objects. Importing SouNourS copy of ServerVehicleOverrideMessage packet and tests.
entry.zone.Transport ! Zone.Vehicle.Despawn(vehicle)
context.parent ! DeconstructionActor.DeleteVehicle(vehicle.GUID, zone.Id) //call up to the main event system
context.parent ! VehicleServiceMessage.RevokeActorControl(vehicle) //call up to a sibling manager
taskResolver ! DeconstructionTask(vehicle, zone)
})

View file

@ -1,33 +0,0 @@
// Copyright (c) 2017 PSForever
package services.vehicle.support
import akka.actor.{Actor, ActorRef, Props}
import net.psforever.objects.vehicles.VehicleControl
import services.vehicle.VehicleServiceMessage
/**
* Provide a context for a `Vehicle` `Actor` - the `VehicleControl`.<br>
* <br>
* A vehicle can be passed between different zones and, therefore, does not belong to the zone.
* A vehicle cna be given to different players and can persist and change though players have gone.
* Therefore, also does not belong to `WorldSessionActor`.
* A vehicle must anchored to something that exists outside of the `InterstellarCluster` and its agents.<br>
* <br>
* The only purpose of this `Actor` is to allow vehicles to borrow a context for the purpose of `Actor` creation.
* It is also be allowed to be responsible for cleaning up that context.
* (In reality, it can be cleaned up anywhere a `PoisonPill` can be sent.)<br>
* <br>
* This `Actor` is intended to sit on top of the event system that handles broadcast messaging.
*/
class VehicleContextActor() extends Actor {
def receive : Receive = {
case VehicleServiceMessage.GiveActorControl(vehicle, actorName) =>
vehicle.Actor = context.actorOf(Props(classOf[VehicleControl], vehicle), s"${vehicle.Definition.Name}_$actorName.${System.nanoTime()}")
case VehicleServiceMessage.RevokeActorControl(vehicle) =>
vehicle.Actor ! akka.actor.PoisonPill
vehicle.Actor = ActorRef.noSender
case _ => ;
}
}