mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-13 15:34:42 +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()
|
Door.OpenEvent()
|
||||||
}
|
}
|
||||||
else if(openState) {
|
else if(openState) {
|
||||||
|
openState = false
|
||||||
Door.CloseEvent()
|
Door.CloseEvent()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ class DoorControl(door : Door) extends Actor {
|
||||||
def receive : Receive = {
|
def receive : Receive = {
|
||||||
case Door.Use(player, msg) =>
|
case Door.Use(player, msg) =>
|
||||||
sender ! Door.DoorMessage(player, msg, door.Use(player, msg))
|
sender ! Door.DoorMessage(player, msg, door.Use(player, msg))
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
sender ! Door.NoEvent()
|
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]()
|
private val equipmentOnGround : ListBuffer[Equipment] = ListBuffer[Equipment]()
|
||||||
/** Used by the `Zone` to coordinate `Equipment` dropping and collection requests. */
|
/** Used by the `Zone` to coordinate `Equipment` dropping and collection requests. */
|
||||||
private var ground : ActorRef = ActorRef.noSender
|
private var ground : ActorRef = ActorRef.noSender
|
||||||
/** */
|
|
||||||
private var doors : ActorRef = ActorRef.noSender
|
|
||||||
/** */
|
|
||||||
private var events : ActorRef = ActorRef.noSender
|
|
||||||
|
|
||||||
private var bases : List[Base] = List()
|
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
|
implicit val guid : NumberPoolHub = this.guid //passed into builderObject.Build implicitly
|
||||||
accessor = context.actorOf(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystem.AllocateNumberPoolActors(guid)), s"$Id-uns")
|
accessor = context.actorOf(Props(classOf[UniqueNumberSystem], guid, UniqueNumberSystem.AllocateNumberPoolActors(guid)), s"$Id-uns")
|
||||||
ground = context.actorOf(Props(classOf[ZoneGroundActor], equipmentOnGround), s"$Id-ground")
|
ground = context.actorOf(Props(classOf[ZoneGroundActor], equipmentOnGround), s"$Id-ground")
|
||||||
doors = context.actorOf(Props(classOf[ZoneDoorActor], this), s"$Id-doors")
|
|
||||||
|
|
||||||
Map.LocalObjects.foreach({ builderObject =>
|
Map.LocalObjects.foreach({ builderObject =>
|
||||||
builderObject.Build
|
builderObject.Build
|
||||||
|
|
@ -179,17 +174,6 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
||||||
*/
|
*/
|
||||||
def Ground : ActorRef = ground
|
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] = {
|
def MakeBases(num : Int) : List[Base] = {
|
||||||
bases = (0 to num).map(id => new Base(id)).toList
|
bases = (0 to num).map(id => new Base(id)).toList
|
||||||
bases
|
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
|
// 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
|
import net.psforever.packet.game.PlanetSideGUID
|
||||||
|
|
||||||
object LocalAction {
|
object LocalAction {
|
||||||
trait Action
|
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 {
|
object LocalServiceResponse {
|
||||||
trait Response
|
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)
|
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 {
|
class LocalService extends Actor {
|
||||||
//import LocalService._
|
//import LocalService._
|
||||||
|
private val doorCloser = context.actorOf(Props[DoorCloseActor], "local-door-closer")
|
||||||
private [this] val log = org.log4s.getLogger
|
private [this] val log = org.log4s.getLogger
|
||||||
|
|
||||||
override def preStart = {
|
override def preStart = {
|
||||||
|
|
@ -45,13 +50,25 @@ class LocalService extends Actor {
|
||||||
|
|
||||||
case LocalServiceMessage(forChannel, action) =>
|
case LocalServiceMessage(forChannel, action) =>
|
||||||
action match {
|
action match {
|
||||||
case LocalAction.Door(player_guid) =>
|
case LocalAction.DoorOpens(player_guid, zone, door) =>
|
||||||
|
doorCloser ! DoorCloseActor.DoorIsOpen(door, zone)
|
||||||
LocalEvents.publish(
|
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 _ => ;
|
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 =>
|
case msg =>
|
||||||
log.info(s"Unhandled message $msg from $sender")
|
log.info(s"Unhandled message $msg from $sender")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
clientKeepAlive.cancel()
|
clientKeepAlive.cancel()
|
||||||
|
|
||||||
avatarService ! Service.Leave()
|
avatarService ! Service.Leave()
|
||||||
|
localService ! Service.Leave()
|
||||||
LivePlayerList.Remove(sessionId) match {
|
LivePlayerList.Remove(sessionId) match {
|
||||||
case Some(tplayer) =>
|
case Some(tplayer) =>
|
||||||
if(tplayer.HasGUID) {
|
if(tplayer.HasGUID) {
|
||||||
|
|
@ -70,7 +71,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
if(pipe.hasNext) {
|
if(pipe.hasNext) {
|
||||||
rightRef = pipe.next
|
rightRef = pipe.next
|
||||||
rightRef !> HelloFriend(sessionId, pipe)
|
rightRef !> HelloFriend(sessionId, pipe)
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
rightRef = sender()
|
rightRef = sender()
|
||||||
}
|
}
|
||||||
context.become(Started)
|
context.become(Started)
|
||||||
|
|
@ -221,13 +223,33 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
case _ => ;
|
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 {
|
order match {
|
||||||
case Door.OpenEvent() =>
|
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() =>
|
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() => ;
|
case Door.NoEvent() => ;
|
||||||
}
|
}
|
||||||
|
|
@ -769,6 +791,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
|
||||||
})
|
})
|
||||||
|
|
||||||
avatarService ! Service.Join(player.Continent)
|
avatarService ! Service.Join(player.Continent)
|
||||||
|
localService ! Service.Join(player.Continent)
|
||||||
self ! SetCurrentAvatar(player)
|
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) =>
|
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