started doors

This commit is contained in:
FateJH 2017-09-25 21:54:59 -04:00
parent c85d7a65a2
commit fa633aa79d
10 changed files with 233 additions and 16 deletions

View file

@ -0,0 +1,77 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.doors
import akka.actor.{ActorContext, ActorRef, Props}
import net.psforever.objects.{PlanetSideGameObject, Player}
import net.psforever.packet.game.UseItemMessage
import net.psforever.types.PlanetSideEmpire
/**
* na
* @param ddef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
*/
class Door(ddef : DoorDefinition) extends PlanetSideGameObject {
/** Internal reference to the `Actor` for this `Door`, sets up by this `Door`. */
private var actor = ActorRef.noSender
def Actor(implicit context : ActorContext) : ActorRef = {
if(actor == ActorRef.noSender) {
actor = context.actorOf(Props(classOf[DoorControl], this), s"${ddef.Name}_${GUID.guid}")
}
actor
}
private var openState : Boolean = false
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
private var hackedBy : Option[PlanetSideEmpire.Value] = None
def Open : Boolean = openState
def Faction : PlanetSideEmpire.Value = faction
def Convert(toFaction : PlanetSideEmpire.Value) : Unit = {
hackedBy = None
faction = toFaction
}
def Request(player : Player, msg : UseItemMessage) : Door.Exchange = {
if(!openState) {
if(faction == PlanetSideEmpire.NEUTRAL || player.Faction == faction) {
Door.OpenEvent()
}
else {
Door.NoEvent()
}
}
else {
Door.NoEvent()
}
}
def Definition : DoorDefinition = ddef
}
object Door {
final case class Request(player : Player, msg : UseItemMessage)
sealed trait Exchange
final case class DoorMessage(player : Player, msg : UseItemMessage, response : Exchange)
final case class OpenEvent() extends Exchange
final case class CloseEvent() extends Exchange
final case class NoEvent() extends Exchange
def apply(tdef : DoorDefinition) : Door = {
new Door(tdef)
}
import net.psforever.packet.game.PlanetSideGUID
def apply(guid : PlanetSideGUID, ddef : DoorDefinition) : Door = {
val obj = new Door(ddef)
obj.GUID = guid
obj
}
}

View file

@ -0,0 +1,40 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.doors
import akka.actor.{Actor, ActorRef, Cancellable}
import net.psforever.packet.game.PlanetSideGUID
import scala.collection.mutable.ListBuffer
class DoorCloseControl(implicit val environment : ActorRef) extends Actor {
import DoorCloseControl._
private var doorCloser : Cancellable = DefaultCloser
private var openDoors : List[DoorEntry] = Nil
def receive : Receive = {
case DoorIsOpen(guid, time) =>
if(openDoors.isEmpty) {
//doorCloser = context.system.scheduler.scheduleOnce(timeout, environment, Door.DoorMessage())
}
else {
openDoors = openDoors :+ DoorEntry(guid, time)
}
case _ => ;
}
}
object DoorCloseControl {
private final val timeout : Long = 5000L
private final val DefaultCloser : Cancellable = new Cancellable() {
override def cancel : Boolean = true
override def isCancelled : Boolean = true
}
private final case class DoorEntry(door_guid : PlanetSideGUID, opened_at_time : Long)
final case class DoorIsOpen(door_guid : PlanetSideGUID, opened_at_time : Long = System.nanoTime())
final case class CloseTheDoor(door_guid : PlanetSideGUID)
}

View file

@ -0,0 +1,27 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.doors
import akka.actor.{Actor, Cancellable}
/**
* An `Actor` that handles messages being dispatched to a specific `Door`.
* @param door the `Door` object being governed
*/
class DoorControl(door : Door) extends Actor {
private var doorCloser : Cancellable = DoorControl.DefaultCloser
def receive : Receive = {
case Door.Request(player, msg) =>
sender ! Door.DoorMessage(player, msg, door.Request(player, msg))
//doorCloser = context.system.scheduler.scheduleOnce(5000L, sender, Door.DoorMessage())
case _ =>
sender ! Door.NoEvent()
}
}
object DoorControl {
final val DefaultCloser : Cancellable = new Cancellable() {
override def cancel : Boolean = true
override def isCancelled : Boolean = true
}
}

View file

@ -0,0 +1,13 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.doors
import net.psforever.objects.definition.ObjectDefinition
/**
* The definition for any `door`.
* @param objectId the object's identifier number
*/
abstract class DoorDefinition(objectId : Int) extends ObjectDefinition(objectId) {
Name = "door"
}

View file

@ -0,0 +1,33 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.zones
import net.psforever.objects.doors.{Door, DoorDefinition}
/**
* Wrapper `Class` designed to instantiate a `Door` server object.
* @param ddef a `DoorDefinition` object, indicating the specific functionality of the resulting `Door`
* @param id the globally unique identifier to which this `Door` will be registered
*/
class DoorObjectBuilder(private val ddef : DoorDefinition, private val id : Int) extends ServerObjectBuilder[Door] {
import akka.actor.ActorContext
import net.psforever.objects.guid.NumberPoolHub
def Build(implicit context : ActorContext, guid : NumberPoolHub) : Door = {
val obj = Door(ddef)
guid.register(obj, id) //non-Actor GUID registration
obj.Actor //it's necessary to register beforehand because the Actor name utilizes the GUID
obj
}
}
object DoorObjectBuilder {
/**
* Overloaded constructor for a `DoorObjectBuilder`.
* @param ddef a `DoorDefinition` object
* @param id a globally unique identifier
* @return a `DoorObjectBuilder` object
*/
def apply(ddef : DoorDefinition, id : Int) : DoorObjectBuilder = {
new DoorObjectBuilder(ddef, id)
}
}

View file

@ -9,7 +9,7 @@ import net.psforever.objects.guid.NumberPoolHub
* Wrapper `Trait` designed to be extended to implement custom object instantiation logic at the `ZoneMap` level.
* @see `Zone.Init`
*/
trait ServerObjectBuilder {
trait ServerObjectBuilder[A <: PlanetSideGameObject] {
/**
* Instantiate and configure the given server object
* (at a later time compared to the construction of the builder class).<br>
@ -23,5 +23,5 @@ trait ServerObjectBuilder {
* defaults to `null`
* @return the object that was created and integrated into the `Zone`
*/
def Build(implicit context : ActorContext = null, guid : NumberPoolHub = null) : PlanetSideGameObject
def Build(implicit context : ActorContext = null, guid : NumberPoolHub = null) : A
}

View file

@ -8,7 +8,7 @@ import net.psforever.objects.terminals.{Terminal, TerminalDefinition}
* @param tdef a `TerminalDefinition` object, indicating the specific functionality of the resulting `Terminal`
* @param id the globally unique identifier to which this `Terminal` will be registered
*/
class TerminalObjectBuilder(private val tdef : TerminalDefinition, private val id : Int) extends ServerObjectBuilder {
class TerminalObjectBuilder(private val tdef : TerminalDefinition, private val id : Int) extends ServerObjectBuilder[Terminal] {
import akka.actor.ActorContext
import net.psforever.objects.guid.NumberPoolHub

View file

@ -21,7 +21,7 @@ package net.psforever.objects.zones
* `LoadMapMessage`
*/
class ZoneMap(private val name : String) {
private var localObjects : List[ServerObjectBuilder] = List()
private var localObjects : List[ServerObjectBuilder[_]] = List()
def Name : String = name
@ -29,7 +29,7 @@ class ZoneMap(private val name : String) {
* Append the builder for a server object to the list of builders known to this `ZoneMap`.
* @param obj the builder for a server object
*/
def LocalObject(obj : ServerObjectBuilder) : Unit = {
def LocalObject(obj : ServerObjectBuilder[_]) : Unit = {
localObjects = localObjects :+ obj
}
@ -37,7 +37,7 @@ class ZoneMap(private val name : String) {
* The list of all server object builder wrappers that have been assigned to this `ZoneMap`.
* @return the `List` of all `ServerObjectBuilders` known to this `ZoneMap`
*/
def LocalObjects : List[ServerObjectBuilder] = {
def LocalObjects : List[ServerObjectBuilder[_]] = {
localObjects
}
}

View file

@ -12,7 +12,7 @@ import ch.qos.logback.core.status._
import ch.qos.logback.core.util.StatusPrinter
import com.typesafe.config.ConfigFactory
import net.psforever.crypto.CryptoInterface
import net.psforever.objects.zones.{InterstellarCluster, TerminalObjectBuilder, Zone, ZoneMap}
import net.psforever.objects.zones._
import net.psforever.objects.guid.TaskResolver
import org.slf4j
import org.fusesource.jansi.Ansi._
@ -222,6 +222,12 @@ object PsLogin {
def createContinents() : List[Zone] = {
val map13 = new ZoneMap("map13") {
import net.psforever.objects.GlobalDefinitions._
val ddef = new net.psforever.objects.doors.DoorDefinition(242) {} //generic door
LocalObject(DoorObjectBuilder(ddef, 330))
LocalObject(DoorObjectBuilder(ddef, 332))
LocalObject(DoorObjectBuilder(ddef, 372))
LocalObject(DoorObjectBuilder(ddef, 373))
LocalObject(TerminalObjectBuilder(cert_terminal, 186))
LocalObject(TerminalObjectBuilder(cert_terminal, 187))
LocalObject(TerminalObjectBuilder(cert_terminal, 188))

View file

@ -11,6 +11,7 @@ import org.log4s.MDC
import MDCContextAware.Implicits._
import ServiceManager.Lookup
import net.psforever.objects._
import net.psforever.objects.doors.Door
import net.psforever.objects.zones.{InterstellarCluster, Zone}
import net.psforever.objects.entity.IdentifiableEntity
import net.psforever.objects.equipment._
@ -206,6 +207,17 @@ class WorldSessionActor extends Actor with MDCContextAware {
case _ => ;
}
case Door.DoorMessage(_, msg, order) =>
order match {
case Door.OpenEvent() =>
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(msg.object_guid, 16)))
case Door.CloseEvent() =>
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(msg.object_guid, 17)))
case Door.NoEvent() => ;
}
case Terminal.TerminalMessage(tplayer, msg, order) =>
order match {
case Terminal.BuyExosuit(exosuit, subtype) =>
@ -943,15 +955,24 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.info("UseItem: " + msg)
// TODO: Not all fields in the response are identical to source in real packet logs (but seems to be ok)
// TODO: Not all incoming UseItemMessage's respond with another UseItemMessage (i.e. doors only send out GenericObjectStateMsg)
if (itemType != 121) sendResponse(PacketCoding.CreateGamePacket(0, UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)))
if (itemType == 121 && !unk3){ // TODO : medkit use ?!
sendResponse(PacketCoding.CreateGamePacket(0, UseItemMessage(avatar_guid, unk1, object_guid, 0, unk3, unk4, unk5, unk6, unk7, unk8, itemType)))
sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(avatar_guid, 0, 100))) // avatar with 100 hp
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(PlanetSideGUID(unk1), 2)))
}
if (unk1 == 0 && !unk3 && unk7 == 25) {
// TODO: This should only actually be sent to doors upon opening; may break non-door items upon use
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(object_guid, 16)))
continent.GUID(object_guid) match {
case Some(door : Door) =>
log.info("Door action!")
door.Actor ! Door.Request(player, msg)
case Some(obj : PlanetSideGameObject) =>
if(itemType != 121) {
sendResponse(PacketCoding.CreateGamePacket(0, UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)))
}
else if(itemType == 121 && !unk3) { // TODO : medkit use ?!
sendResponse(PacketCoding.CreateGamePacket(0, UseItemMessage(avatar_guid, unk1, object_guid, 0, unk3, unk4, unk5, unk6, unk7, unk8, itemType)))
sendResponse(PacketCoding.CreateGamePacket(0, PlanetsideAttributeMessage(avatar_guid, 0, 100))) // avatar with 100 hp
sendResponse(PacketCoding.CreateGamePacket(0, ObjectDeleteMessage(PlanetSideGUID(unk1), 2)))
}
// if(unk1 == 0 && !unk3 && unk7 == 25) {
// // TODO: This should only actually be sent to doors upon opening; may break non-door items upon use
// sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(object_guid, 16)))
// }
case None => ;
}
case msg @ UnuseItemMessage(player_guid, item) =>