mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-03-03 12:30:24 +00:00
doors open and async close now via LocalEnvironment and DoorCloseActor
This commit is contained in:
parent
1c41972d69
commit
74b718c536
7 changed files with 138 additions and 94 deletions
|
|
@ -33,6 +33,7 @@ class Door(ddef : DoorDefinition) extends PlanetSideServerObject {
|
|||
Door.OpenEvent()
|
||||
}
|
||||
else if(openState) {
|
||||
openState = false
|
||||
Door.CloseEvent()
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ class DoorControl(door : Door) extends Actor {
|
|||
def receive : Receive = {
|
||||
case Door.Use(player, msg) =>
|
||||
sender ! Door.DoorMessage(player, msg, door.Use(player, msg))
|
||||
|
||||
case _ =>
|
||||
sender ! Door.NoEvent()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.zones
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class DoorCloseActor() extends Actor {
|
||||
import DoorCloseActor._
|
||||
private var doorCloserTrigger : Cancellable = DefaultCloser
|
||||
private var openDoors : List[DoorEntry] = Nil
|
||||
//private[this] val log = org.log4s.getLogger
|
||||
|
||||
def receive : Receive = {
|
||||
case DoorIsOpen(door, zone, time) =>
|
||||
openDoors = openDoors :+ DoorEntry(door, zone, time)
|
||||
if(openDoors.size == 1) {
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
doorCloserTrigger = context.system.scheduler.scheduleOnce(timeout, self, DoorCloseActor.TryCloseDoors())
|
||||
}
|
||||
|
||||
case TryCloseDoors() =>
|
||||
doorCloserTrigger.cancel
|
||||
val now : Long = System.nanoTime
|
||||
//TODO we can just walk across the list of doors and extract only the first few entries
|
||||
val (doorsToClose, doorsLeftOpen) = recursivePartitionDoors(openDoors.iterator, now)
|
||||
openDoors = doorsLeftOpen
|
||||
doorsToClose.foreach(entry => {
|
||||
entry.door.Open = false //permissible
|
||||
context.parent ! DoorCloseActor.CloseTheDoor(entry.door.GUID, entry.zone.Id)
|
||||
})
|
||||
|
||||
if(doorsLeftOpen.nonEmpty) {
|
||||
val short_timeout : FiniteDuration = math.max(1, timeout_time - (now - doorsLeftOpen.head.opened_at_time)) nanoseconds
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
doorCloserTrigger = context.system.scheduler.scheduleOnce(short_timeout, self, DoorCloseActor.TryCloseDoors())
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param iter na
|
||||
* @param now na
|
||||
* @param list na
|
||||
* @see `List.partition`
|
||||
* @return a `Tuple` of two `Lists`:
|
||||
* the entries for all `Door`s that are closing,
|
||||
* and the entries for all doors that are staying open
|
||||
*/
|
||||
@tailrec private def recursivePartitionDoors(iter : Iterator[DoorEntry], now : Long, list : List[DoorEntry] = Nil) : (List[DoorEntry], List[DoorEntry]) = {
|
||||
if(!iter.hasNext) {
|
||||
(list, iter.toList)
|
||||
}
|
||||
else {
|
||||
val entry = iter.next()
|
||||
if(now - entry.opened_at_time >= timeout_time) {
|
||||
recursivePartitionDoors(iter, now, list :+ entry)
|
||||
}
|
||||
else {
|
||||
(list, entry +: iter.toList)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object DoorCloseActor {
|
||||
private final val timeout_time : Long = 5000000000L //nanoseconds
|
||||
private final val timeout : FiniteDuration = timeout_time nanoseconds
|
||||
|
||||
private final val DefaultCloser : Cancellable = new Cancellable() {
|
||||
override def cancel : Boolean = true
|
||||
override def isCancelled : Boolean = true
|
||||
}
|
||||
|
||||
final case class DoorIsOpen(door : Door, zone : Zone, opened_at_time : Long = System.nanoTime())
|
||||
|
||||
final case class CloseTheDoor(door_guid : PlanetSideGUID, zone_id : String)
|
||||
|
||||
private final case class DoorEntry(door : Door, zone : Zone, opened_at_time : Long)
|
||||
|
||||
private final case class TryCloseDoors()
|
||||
}
|
||||
|
|
@ -48,10 +48,6 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
private val equipmentOnGround : ListBuffer[Equipment] = ListBuffer[Equipment]()
|
||||
/** Used by the `Zone` to coordinate `Equipment` dropping and collection requests. */
|
||||
private var ground : ActorRef = ActorRef.noSender
|
||||
/** */
|
||||
private var doors : ActorRef = ActorRef.noSender
|
||||
/** */
|
||||
private var events : ActorRef = ActorRef.noSender
|
||||
|
||||
private var bases : List[Base] = List()
|
||||
|
||||
|
|
@ -72,7 +68,6 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
implicit val guid : NumberPoolHub = this.guid //passed into builderObject.Build implicitly
|
||||
accessor = context.actorOf(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystem.AllocateNumberPoolActors(guid)), s"$Id-uns")
|
||||
ground = context.actorOf(Props(classOf[ZoneGroundActor], equipmentOnGround), s"$Id-ground")
|
||||
doors = context.actorOf(Props(classOf[ZoneDoorActor], this), s"$Id-doors")
|
||||
|
||||
Map.LocalObjects.foreach({ builderObject =>
|
||||
builderObject.Build
|
||||
|
|
@ -179,17 +174,6 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
*/
|
||||
def Ground : ActorRef = ground
|
||||
|
||||
def Doors : ActorRef = doors
|
||||
|
||||
def Events : ActorRef = events
|
||||
|
||||
def Events_=(zoneActor : ActorRef) : ActorRef = {
|
||||
if(events == ActorRef.noSender) {
|
||||
events = zoneActor
|
||||
}
|
||||
Events
|
||||
}
|
||||
|
||||
def MakeBases(num : Int) : List[Base] = {
|
||||
bases = (0 to num).map(id => new Base(id)).toList
|
||||
bases
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.zones
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class ZoneDoorActor(implicit val zone : Zone) extends Actor {
|
||||
import ZoneDoorActor._
|
||||
private var doorCloserTrigger : Cancellable = DefaultCloser
|
||||
private var openDoors : List[DoorEntry] = Nil
|
||||
|
||||
def receive : Receive = {
|
||||
case DoorIsOpen(guid, time) =>
|
||||
openDoors = openDoors :+ DoorEntry(guid, time)
|
||||
if(doorCloserTrigger.isCancelled) {
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
doorCloserTrigger = context.system.scheduler.scheduleOnce(timeout, self, ZoneDoorActor.CloseTheDoor())
|
||||
}
|
||||
|
||||
case CloseTheDoor() =>
|
||||
doorCloserTrigger.cancel
|
||||
val now : Long = System.nanoTime
|
||||
recursiveCloseDoors(openDoors.iterator, now) match {
|
||||
case entry :: rest =>
|
||||
openDoors = rest
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
doorCloserTrigger = context.system.scheduler.scheduleOnce((now - entry.opened_at_time + timeout_time)*1000 milliseconds, self, ZoneDoorActor.CloseTheDoor())
|
||||
case Nil =>
|
||||
openDoors = Nil
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
@tailrec private def recursiveCloseDoors(iter : Iterator[DoorEntry], now : Long) : List[DoorEntry] = {
|
||||
if(!iter.hasNext) {
|
||||
Nil
|
||||
}
|
||||
else {
|
||||
val entry = iter.next
|
||||
if(now - entry.opened_at_time < timeout_time) {
|
||||
entry +: iter.toList
|
||||
}
|
||||
else {
|
||||
//TODO close this door entry
|
||||
recursiveCloseDoors(iter, now)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ZoneDoorActor {
|
||||
private final val timeout_time = 5000
|
||||
private final val timeout : FiniteDuration = timeout_time milliseconds
|
||||
|
||||
private final val DefaultCloser : Cancellable = new Cancellable() {
|
||||
override def cancel : Boolean = true
|
||||
override def isCancelled : Boolean = true
|
||||
}
|
||||
|
||||
final case class DoorIsOpen(door_guid : PlanetSideGUID, opened_at_time : Long = System.nanoTime())
|
||||
|
||||
private final case class DoorEntry(door_guid : PlanetSideGUID, opened_at_time : Long)
|
||||
|
||||
private final case class CloseTheDoor()
|
||||
}
|
||||
|
|
@ -1,17 +1,21 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
import akka.actor.Actor
|
||||
import akka.actor.{Actor, Props}
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
import net.psforever.objects.zones.{DoorCloseActor, Zone}
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
||||
object LocalAction {
|
||||
trait Action
|
||||
|
||||
final case class Door(player_guid : PlanetSideGUID) extends Action
|
||||
final case class DoorOpens(player_guid : PlanetSideGUID, continent : Zone, door : Door) extends Action
|
||||
final case class DoorCloses(player_guid : PlanetSideGUID, door_guid : PlanetSideGUID) extends Action
|
||||
}
|
||||
|
||||
object LocalServiceResponse {
|
||||
trait Response
|
||||
|
||||
final case class Door(player_guid : PlanetSideGUID) extends Response
|
||||
final case class DoorOpens(door_guid : PlanetSideGUID) extends Response
|
||||
final case class DoorCloses(door_guid : PlanetSideGUID) extends Response
|
||||
}
|
||||
|
||||
final case class LocalServiceMessage(forChannel : String, actionMessage : LocalAction.Action)
|
||||
|
|
@ -24,6 +28,7 @@ final case class LocalServiceResponse(toChannel : String, avatar_guid : PlanetSi
|
|||
|
||||
class LocalService extends Actor {
|
||||
//import LocalService._
|
||||
private val doorCloser = context.actorOf(Props[DoorCloseActor], "local-door-closer")
|
||||
private [this] val log = org.log4s.getLogger
|
||||
|
||||
override def preStart = {
|
||||
|
|
@ -45,13 +50,25 @@ class LocalService extends Actor {
|
|||
|
||||
case LocalServiceMessage(forChannel, action) =>
|
||||
action match {
|
||||
case LocalAction.Door(player_guid) =>
|
||||
case LocalAction.DoorOpens(player_guid, zone, door) =>
|
||||
doorCloser ! DoorCloseActor.DoorIsOpen(door, zone)
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/LocalEnvironment" + forChannel, player_guid, LocalServiceResponse.Door(player_guid))
|
||||
LocalServiceResponse(s"/$forChannel/LocalEnvironment", player_guid, LocalServiceResponse.DoorOpens(door.GUID))
|
||||
)
|
||||
|
||||
case LocalAction.DoorCloses(player_guid, door_guid) =>
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/LocalEnvironment", player_guid, LocalServiceResponse.DoorCloses(door_guid))
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
//response from DoorCloseActor
|
||||
case DoorCloseActor.CloseTheDoor(door_guid, zone_id) =>
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$zone_id/LocalEnvironment", PlanetSideGUID(0), LocalServiceResponse.DoorCloses(door_guid))
|
||||
)
|
||||
|
||||
case msg =>
|
||||
log.info(s"Unhandled message $msg from $sender")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
clientKeepAlive.cancel()
|
||||
|
||||
avatarService ! Service.Leave()
|
||||
localService ! Service.Leave()
|
||||
LivePlayerList.Remove(sessionId) match {
|
||||
case Some(tplayer) =>
|
||||
if(tplayer.HasGUID) {
|
||||
|
|
@ -70,7 +71,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
if(pipe.hasNext) {
|
||||
rightRef = pipe.next
|
||||
rightRef !> HelloFriend(sessionId, pipe)
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
rightRef = sender()
|
||||
}
|
||||
context.become(Started)
|
||||
|
|
@ -221,13 +223,33 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
case _ => ;
|
||||
}
|
||||
|
||||
case Door.DoorMessage(_, msg, order) =>
|
||||
case LocalServiceResponse(_, guid, reply) =>
|
||||
reply match {
|
||||
case LocalServiceResponse.DoorOpens(door_guid) =>
|
||||
if(player.GUID != guid) {
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(door_guid, 16)))
|
||||
}
|
||||
|
||||
case LocalServiceResponse.DoorCloses(door_guid) => //door closes for everyone
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(door_guid, 17)))
|
||||
}
|
||||
|
||||
case Door.DoorMessage(tplayer, msg, order) =>
|
||||
val door_guid = msg.object_guid
|
||||
order match {
|
||||
case Door.OpenEvent() =>
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(msg.object_guid, 16)))
|
||||
continent.GUID(door_guid) match {
|
||||
case Some(door : Door) =>
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(door_guid, 16)))
|
||||
localService ! LocalServiceMessage (continent.Id, LocalAction.DoorOpens (tplayer.GUID, continent, door) )
|
||||
|
||||
case _ =>
|
||||
log.warn(s"door $door_guid wanted to be opened but could not be found")
|
||||
}
|
||||
|
||||
case Door.CloseEvent() =>
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(msg.object_guid, 17)))
|
||||
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(door_guid, 17)))
|
||||
localService ! LocalServiceMessage(continent.Id, LocalAction.DoorCloses(tplayer.GUID, door_guid))
|
||||
|
||||
case Door.NoEvent() => ;
|
||||
}
|
||||
|
|
@ -769,6 +791,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
|||
})
|
||||
|
||||
avatarService ! Service.Join(player.Continent)
|
||||
localService ! Service.Join(player.Continent)
|
||||
self ! SetCurrentAvatar(player)
|
||||
|
||||
case msg @ PlayerStateMessageUpstream(avatar_guid, pos, vel, yaw, pitch, yaw_upper, seq_time, unk3, is_crouching, is_jumping, unk4, is_cloaking, unk5, unk6) =>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue