diff --git a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
index 13073dad..6d961e8f 100644
--- a/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
+++ b/common/src/main/scala/net/psforever/objects/GlobalDefinitions.scala
@@ -1245,7 +1245,7 @@ object GlobalDefinitions {
cert_terminal = new CertTerminalDefinition
val
- external_lock = new IFFLockDefinition
+ lock_external = new IFFLockDefinition
val
door = new DoorDefinition
}
diff --git a/common/src/main/scala/net/psforever/objects/Player.scala b/common/src/main/scala/net/psforever/objects/Player.scala
index 9d59f733..a91cc0ff 100644
--- a/common/src/main/scala/net/psforever/objects/Player.scala
+++ b/common/src/main/scala/net/psforever/objects/Player.scala
@@ -64,8 +64,6 @@ class Player(private val name : String,
/** Last medkituse. */
var lastMedkit : Long = 0
var death_by : Int = 0
- var doors : Array[Int] = Array.ofDim(120)
- var doorsTime : Array[Long] = Array.ofDim(120)
var lastSeenStreamMessage : Array[Long] = Array.fill[Long](65535)(0L)
var lastShotSeq_time : Int = -1
/** The player is shooting. */
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/CommonMessages.scala b/common/src/main/scala/net/psforever/objects/serverobject/CommonMessages.scala
index 8f4de61c..c73b7a23 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/CommonMessages.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/CommonMessages.scala
@@ -2,9 +2,8 @@
package net.psforever.objects.serverobject
import net.psforever.objects.Player
-import net.psforever.types.PlanetSideEmpire
-//temporary location for these temporary messages
+//temporary location for these messages
object CommonMessages {
final case class Hack(player : Player)
final case class ClearHack()
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala b/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala
index 3a0e71ec..f4497544 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/PlanetSideServerObject.scala
@@ -4,11 +4,25 @@ package net.psforever.objects.serverobject
import akka.actor.ActorRef
import net.psforever.objects.PlanetSideGameObject
+/**
+ * An object layered on top of the standard game object class that maintains an internal `ActorRef`.
+ * A measure of synchronization can be managed using this `Actor`.
+ */
abstract class PlanetSideServerObject extends PlanetSideGameObject {
private var actor = ActorRef.noSender
+ /**
+ * Retrieve a reference to the internal `Actor`.
+ * @return the internal `ActorRef`
+ */
def Actor : ActorRef = actor
+ /**
+ * Assign an `Actor` to act for this server object.
+ * This reference is only set once, that is, as long as the internal `ActorRef` directs to `Actor.noSender` (`null`).
+ * @param control the `Actor` whose functionality will govern this server object
+ * @return the current internal `ActorRef`
+ */
def Actor_=(control : ActorRef) : ActorRef = {
if(actor == ActorRef.noSender) {
actor = control
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/builders/IFFLockObjectBuilder.scala b/common/src/main/scala/net/psforever/objects/serverobject/builders/IFFLockObjectBuilder.scala
index e954eea4..26834d93 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/builders/IFFLockObjectBuilder.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/builders/IFFLockObjectBuilder.scala
@@ -5,8 +5,8 @@ import akka.actor.Props
import net.psforever.objects.serverobject.locks.{IFFLock, IFFLockControl, IFFLockDefinition}
/**
- * Wrapper `Class` designed to instantiate a `Door` server object.
- * @param idef a `IFFLockDefinition` object, indicating the specific functionality of the resulting `Door`
+ * Wrapper `Class` designed to instantiate a door lock server object that is sensitive to user faction affiliation.
+ * @param idef a `IFFLockDefinition` object, indicating the specific functionality
* @param id the globally unique identifier to which this `IFFLock` will be registered
*/
class IFFLockObjectBuilder(private val idef : IFFLockDefinition, private val id : Int) extends ServerObjectBuilder[IFFLock] {
@@ -14,7 +14,7 @@ class IFFLockObjectBuilder(private val idef : IFFLockDefinition, private val id
import net.psforever.objects.guid.NumberPoolHub
def Build(implicit context : ActorContext, guid : NumberPoolHub) : IFFLock = {
- val obj = IFFLock()
+ val obj = IFFLock(idef)
guid.register(obj, id) //non-Actor GUID registration
obj.Actor = context.actorOf(Props(classOf[IFFLockControl], obj), s"${idef.Name}_${obj.GUID.guid}")
obj
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/builders/ServerObjectBuilder.scala b/common/src/main/scala/net/psforever/objects/serverobject/builders/ServerObjectBuilder.scala
index 5b412814..e1bb0ce7 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/builders/ServerObjectBuilder.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/builders/ServerObjectBuilder.scala
@@ -7,8 +7,10 @@ import net.psforever.objects.guid.NumberPoolHub
/**
* Wrapper `Trait` designed to be extended to implement custom object instantiation logic at the `ZoneMap` level.
+ * @tparam A any object that extends from PlanetSideGameObject
* @see `Zone.Init`
*/
+//TODO can we changed PlanetSideGameObject -> PlanetSideServerObject?
trait ServerObjectBuilder[A <: PlanetSideGameObject] {
/**
* Instantiate and configure the given server object
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/doors/Base.scala b/common/src/main/scala/net/psforever/objects/serverobject/doors/Base.scala
index 17c590a7..90e6c490 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/doors/Base.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/doors/Base.scala
@@ -3,6 +3,10 @@ package net.psforever.objects.serverobject.doors
import net.psforever.types.PlanetSideEmpire
+/**
+ * A temporary class to represent "facilities" and "structures."
+ * @param id the map id of the base
+ */
class Base(private val id : Int) {
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala b/common/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala
index 5971fbaa..ae63affd 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/doors/Door.scala
@@ -6,10 +6,10 @@ import net.psforever.objects.Player
import net.psforever.packet.game.UseItemMessage
/**
- * na
+ * A structure-owned server object that is a "door" that can open and can close.
* @param ddef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
*/
-class Door(ddef : DoorDefinition) extends PlanetSideServerObject {
+class Door(private val ddef : DoorDefinition) extends PlanetSideServerObject {
private var openState : Boolean = false
private var lockState : Boolean = false
@@ -45,26 +45,46 @@ class Door(ddef : DoorDefinition) extends PlanetSideServerObject {
}
object Door {
+ /**
+ * Entry message into this `Door` that carries the request.
+ * @param player the player who sent this request message
+ * @param msg the original packet carrying the request
+ */
final case class Use(player : Player, msg : UseItemMessage)
+ /**
+ * A basic `Trait` connecting all of the actionable `Door` response messages.
+ */
sealed trait Exchange
+ /**
+ * Message that carries the result of the processed request message back to the original user (`player`).
+ * @param player the player who sent this request message
+ * @param msg the original packet carrying the request
+ * @param response the result of the processed request
+ */
final case class DoorMessage(player : Player, msg : UseItemMessage, response : Exchange)
+ /**
+ * This door will open.
+ */
final case class OpenEvent() extends Exchange
+ /**
+ * This door will close.
+ */
final case class CloseEvent() extends Exchange
+ /**
+ * This door will do nothing.
+ */
final case class NoEvent() extends Exchange
+ /**
+ * Overloaded constructor.
+ * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
+ */
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
- }
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/doors/DoorControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/doors/DoorControl.scala
index ac3832f0..8f116331 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/doors/DoorControl.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/doors/DoorControl.scala
@@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.doors
-import akka.actor.{Actor, Cancellable}
+import akka.actor.Actor
/**
* An `Actor` that handles messages being dispatched to a specific `Door`.
@@ -16,10 +16,3 @@ class DoorControl(door : Door) extends Actor {
sender ! Door.NoEvent()
}
}
-
-object DoorControl {
- final val DefaultCloser : Cancellable = new Cancellable() {
- override def cancel : Boolean = true
- override def isCancelled : Boolean = true
- }
-}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/doors/DoorDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/doors/DoorDefinition.scala
index 959aa6d2..6a22670c 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/doors/DoorDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/doors/DoorDefinition.scala
@@ -4,7 +4,7 @@ package net.psforever.objects.serverobject.doors
import net.psforever.objects.definition.ObjectDefinition
/**
- * The definition for any `door`.
+ * The definition for any `Door`.
* Object Id 242 is a generic door.
*/
class DoorDefinition extends ObjectDefinition(242) {
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala
index e7dad76c..e08587e8 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala
@@ -1,39 +1,66 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.locks
-import net.psforever.objects.{GlobalDefinitions, Player}
+import net.psforever.objects.Player
import net.psforever.objects.serverobject.PlanetSideServerObject
-import net.psforever.types.{PlanetSideEmpire, Vector3}
+import net.psforever.packet.game.PlanetSideGUID
+import net.psforever.types.Vector3
-class IFFLock extends PlanetSideServerObject {
- private var hackedBy : Option[(Player, Vector3)] = None
+/**
+ * A structure-owned server object that is a "door lock."
+ *
+ * The "door lock" exerts an "identify friend or foe" field that detects the faction affiliation of a target player.
+ * It also indirectly inherits faction affiliation from the structure to which it is connected
+ * or it can be "hacked" whereupon the person exploiting it leaves their "faction" as the aforementioned affiliated faction.
+ * The `IFFLock` is ideally associated with a server map object - a `Door` - to which it acts as a gatekeeper.
+ * @param idef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
+ */
+class IFFLock(private val idef : IFFLockDefinition) extends PlanetSideServerObject {
+ /**
+ * An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received.
+ */
+ private var hackedBy : Option[(Player, PlanetSideGUID, Vector3)] = None
- def HackedBy : Option[(Player, Vector3)] = hackedBy
+ def HackedBy : Option[(Player, PlanetSideGUID, Vector3)] = hackedBy
- def HackedBy_=(agent : Player) : Option[(Player, Vector3)] = HackedBy_=(Some(agent))
+ def HackedBy_=(agent : Player) : Option[(Player, PlanetSideGUID, Vector3)] = HackedBy_=(Some(agent))
- def HackedBy_=(agent : Option[Player]) : Option[(Player, Vector3)] = {
+ /**
+ * Set the hack state of this object by recording important information about the player that caused it.
+ * Set the hack state if there is no current hack state.
+ * Override the hack state with a new hack state if the new user has different faction affiliation.
+ * @param agent a `Player`, or no player
+ * @return the player hack entry
+ */
+ def HackedBy_=(agent : Option[Player]) : Option[(Player, PlanetSideGUID, Vector3)] = {
hackedBy match {
case None =>
+ //set the hack state if there is no current hack state
if(agent.isDefined) {
- hackedBy = Some(agent.get, agent.get.Position)
+ hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
}
case Some(_) =>
+ //clear the hack state
if(agent.isEmpty) {
hackedBy = None
}
+ //override the hack state with a new hack state if the new user has different faction affiliation
else if(agent.get.Faction != hackedBy.get._1.Faction) {
- hackedBy = Some(agent.get, agent.get.Position) //overwrite
+ hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
}
}
HackedBy
}
- def Definition : IFFLockDefinition = GlobalDefinitions.external_lock
+ def Definition : IFFLockDefinition = idef
}
object IFFLock {
- def apply() : IFFLock = {
- new IFFLock
+ /**
+ * Overloaded constructor.
+ * @param idef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
+ */
+ def apply(idef : IFFLockDefinition) : IFFLock = {
+ new IFFLock(idef)
}
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockControl.scala
index e96d1bd3..bf9d1b81 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockControl.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockControl.scala
@@ -1,22 +1,22 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.locks
-import akka.actor.{Actor, Cancellable}
+import akka.actor.Actor
import net.psforever.objects.serverobject.CommonMessages
+/**
+ * An `Actor` that handles messages being dispatched to a specific `IFFLock`.
+ * @param lock the `IFFLock` object being governed
+ * @see `CommonMessages`
+ */
class IFFLockControl(lock : IFFLock) extends Actor {
def receive : Receive = {
case CommonMessages.Hack(player) =>
lock.HackedBy = player
+
case CommonMessages.ClearHack() =>
lock.HackedBy = None
- case _ => ;
+
+ case _ => ; //no default message
}
}
-
-object IFFLockControl {
- final val DefaultCloser : Cancellable = new Cancellable() {
- override def cancel : Boolean = true
- override def isCancelled : Boolean = true
- }
-}
\ No newline at end of file
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockDefinition.scala b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockDefinition.scala
index dcad4139..d8c180d8 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockDefinition.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockDefinition.scala
@@ -3,6 +3,10 @@ package net.psforever.objects.serverobject.locks
import net.psforever.objects.definition.ObjectDefinition
-class IFFLockDefinition extends ObjectDefinition(0) {
+/**
+ * The definition for any `IFFLock`.
+ * Object Id 451 is a generic external lock.
+ */
+class IFFLockDefinition extends ObjectDefinition(451) {
Name = "iff_lock"
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TemporaryTerminalMessages.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TemporaryTerminalMessages.scala
deleted file mode 100644
index 6d264df3..00000000
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TemporaryTerminalMessages.scala
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2017 PSForever
-package net.psforever.objects.serverobject.terminals
-
-import net.psforever.objects.Player
-import net.psforever.types.PlanetSideEmpire
-
-//temporary location for these temporary messages
-object TemporaryTerminalMessages {
- //TODO send original packets along with these messages
- final case class UseItem(player : Player)
- final case class Convert(faction : PlanetSideEmpire.Value)
- final case class Hack(player : Player)
- final case class ClearHack()
- final case class Damaged(dm : Int)
- final case class Repaired(rep : Int)
-}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala
index bccd245c..57532c54 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala
@@ -5,48 +5,54 @@ import net.psforever.objects.Player
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.inventory.InventoryItem
import net.psforever.objects.serverobject.PlanetSideServerObject
-import net.psforever.packet.game.ItemTransactionMessage
-import net.psforever.types.{ExoSuitType, PlanetSideEmpire, TransactionType, Vector3}
+import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
+import net.psforever.types.{ExoSuitType, TransactionType, Vector3}
/**
- * na
+ * A structure-owned server object that is a "terminal" that can be accessed for amenities and services.
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
*/
class Terminal(tdef : TerminalDefinition) extends PlanetSideServerObject {
- //the following fields and related methods are neither finalized no integrated; GOTO Request
- private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
- private var hackedBy : Option[(Player, Vector3)] = None
- private var health : Int = 100 //TODO not real health value
+ /**
+ * An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received.
+ */
+ private var hackedBy : Option[(Player, PlanetSideGUID, Vector3)] = None
- def Faction : PlanetSideEmpire.Value = faction
+ def HackedBy : Option[(Player, PlanetSideGUID, Vector3)] = hackedBy
- def HackedBy : Option[(Player, Vector3)] = hackedBy
+ def HackedBy_=(agent : Player) : Option[(Player, PlanetSideGUID, Vector3)] = HackedBy_=(Some(agent))
- def HackedBy_=(agent : Player) : Option[(Player, Vector3)] = HackedBy_=(Some(agent))
-
- def HackedBy_=(agent : Option[Player]) : Option[(Player, Vector3)] = {
+ /**
+ * Set the hack state of this object by recording important information about the player that caused it.
+ * Set the hack state if there is no current hack state.
+ * Override the hack state with a new hack state if the new user has different faction affiliation.
+ * @param agent a `Player`, or no player
+ * @return the player hack entry
+ */
+ def HackedBy_=(agent : Option[Player]) : Option[(Player, PlanetSideGUID, Vector3)] = {
hackedBy match {
case None =>
+ //set the hack state if there is no current hack state
if(agent.isDefined) {
- hackedBy = Some(agent.get, agent.get.Position)
+ hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
}
case Some(_) =>
+ //clear the hack state
if(agent.isEmpty) {
hackedBy = None
}
+ //override the hack state with a new hack state if the new user has different faction affiliation
else if(agent.get.Faction != hackedBy.get._1.Faction) {
- hackedBy = Some(agent.get, agent.get.Position) //overwrite
+ hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
}
}
HackedBy
}
- def Health : Int = health
+ //the following fields and related methods are neither finalized nor integrated; GOTO Request
+ private var health : Int = 100 //TODO not real health value
- def Convert(toFaction : PlanetSideEmpire.Value) : Unit = {
- hackedBy = None
- faction = toFaction
- }
+ def Health : Int = health
def Damaged(dam : Int) : Unit = {
health = Math.max(0, Health - dam)
@@ -146,14 +152,11 @@ object Terminal {
*/
final case class InfantryLoadout(exosuit : ExoSuitType.Value, subtype : Int = 0, holsters : List[InventoryItem], inventory : List[InventoryItem]) extends Exchange
+ /**
+ * Overloaded constructor.
+ * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
+ */
def apply(tdef : TerminalDefinition) : Terminal = {
new Terminal(tdef)
}
-
- import net.psforever.packet.game.PlanetSideGUID
- def apply(guid : PlanetSideGUID, tdef : TerminalDefinition) : Terminal = {
- val obj = new Terminal(tdef)
- obj.GUID = guid
- obj
- }
}
diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala
index 8774245e..c74ada79 100644
--- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala
+++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala
@@ -4,9 +4,7 @@ package net.psforever.objects.serverobject.terminals
import akka.actor.Actor
/**
- * An `Actor` that handles messages being dispatched to a specific `Terminal`.
- *
- * For now, the only important message being managed is `Terminal.Request`.
+ * An `Actor` that handles messages being dispatched to a specific `Terminal`.
* @param term the `Terminal` object being governed
*/
class TerminalControl(term : Terminal) extends Actor {
@@ -14,12 +12,6 @@ class TerminalControl(term : Terminal) extends Actor {
case Terminal.Request(player, msg) =>
sender ! Terminal.TerminalMessage(player, msg, term.Request(player, msg))
- case TemporaryTerminalMessages.Hack(player) =>
- term.HackedBy = player
-
- case TemporaryTerminalMessages.ClearHack() =>
- term.HackedBy = None
-
case _ =>
sender ! Terminal.NoDeal()
}
diff --git a/common/src/main/scala/net/psforever/objects/zones/DoorCloseActor.scala b/common/src/main/scala/net/psforever/objects/zones/DoorCloseActor.scala
index b1604b75..4b2d9e9f 100644
--- a/common/src/main/scala/net/psforever/objects/zones/DoorCloseActor.scala
+++ b/common/src/main/scala/net/psforever/objects/zones/DoorCloseActor.scala
@@ -8,33 +8,38 @@ import net.psforever.packet.game.PlanetSideGUID
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 {
- import DoorCloseActor._
- private var doorCloserTrigger : Cancellable = DefaultCloser
- private var openDoors : List[DoorEntry] = Nil
+ /** The periodic `Executor` that checks for doors to be closed */
+ private var doorCloserTrigger : Cancellable = DoorCloseActor.DefaultCloser
+ /** A `List` of currently open doors */
+ private var openDoors : List[DoorCloseActor.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) {
+ 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(timeout, self, DoorCloseActor.TryCloseDoors())
+ doorCloserTrigger = context.system.scheduler.scheduleOnce(DoorCloseActor.timeout, self, DoorCloseActor.TryCloseDoors())
}
- case TryCloseDoors() =>
+ case DoorCloseActor.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)
+ val (doorsToClose, doorsLeftOpen) = PartitionEntries(openDoors, now)
openDoors = doorsLeftOpen
doorsToClose.foreach(entry => {
- entry.door.Open = false //permissible
- context.parent ! DoorCloseActor.CloseTheDoor(entry.door.GUID, entry.zone.Id)
+ entry.door.Open = false //permissible break from synchronization
+ context.parent ! DoorCloseActor.CloseTheDoor(entry.door.GUID, entry.zone.Id) //call up to the main event system
})
if(doorsLeftOpen.nonEmpty) {
- val short_timeout : FiniteDuration = math.max(1, timeout_time - (now - doorsLeftOpen.head.opened_at_time)) nanoseconds
+ val short_timeout : FiniteDuration = math.max(1, DoorCloseActor.timeout_time - (now - doorsLeftOpen.head.time)) nanoseconds
import scala.concurrent.ExecutionContext.Implicits.global
doorCloserTrigger = context.system.scheduler.scheduleOnce(short_timeout, self, DoorCloseActor.TryCloseDoors())
}
@@ -43,33 +48,53 @@ class DoorCloseActor() extends Actor {
}
/**
- * na
- * @param iter na
- * @param now na
- * @param list na
+ * 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`:
- * the entries for all `Door`s that are closing,
- * and the entries for all doors that are staying open
+ * @return a `Tuple` of two `Lists`, whose qualifications are explained above
*/
- @tailrec private def recursivePartitionDoors(iter : Iterator[DoorEntry], now : Long, list : List[DoorEntry] = Nil) : (List[DoorEntry], List[DoorEntry]) = {
+ 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) {
- (list, iter.toList)
+ index
}
else {
val entry = iter.next()
- if(now - entry.opened_at_time >= timeout_time) {
- recursivePartitionDoors(iter, now, list :+ entry)
+ if(now - entry.time >= DoorCloseActor.timeout_time) {
+ recursivePartitionEntries(iter, now, index + 1)
}
else {
- (list, entry +: iter.toList)
+ index
}
}
}
}
object DoorCloseActor {
- private final val timeout_time : Long = 5000000000L //nanoseconds
+ /** 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
private final val DefaultCloser : Cancellable = new Cancellable() {
@@ -77,11 +102,33 @@ object DoorCloseActor {
override def isCancelled : Boolean = true
}
- final case class DoorIsOpen(door : Door, zone : Zone, opened_at_time : Long = System.nanoTime())
-
+ /**
+ * 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)
-
- private final case class DoorEntry(door : Door, zone : Zone, opened_at_time : Long)
-
+ /**
+ * 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)
}
diff --git a/common/src/main/scala/net/psforever/objects/zones/HackClearActor.scala b/common/src/main/scala/net/psforever/objects/zones/HackClearActor.scala
index b595051e..2ec3cb05 100644
--- a/common/src/main/scala/net/psforever/objects/zones/HackClearActor.scala
+++ b/common/src/main/scala/net/psforever/objects/zones/HackClearActor.scala
@@ -8,33 +8,39 @@ 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 {
- import HackClearActor._
- private var clearTrigger : Cancellable = DefaultClearer
- private var hackedObjects : List[HackEntry] = Nil
+ /** The periodic `Executor` that checks for server objects to be unhacked */
+ private var clearTrigger : Cancellable = HackClearActor.DefaultClearer
+ /** 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 ObjectIsHacked(door, zone, unk1, unk2, time) =>
- hackedObjects = hackedObjects :+ HackEntry(door, zone, unk1, unk2, time)
- if(hackedObjects.size == 1) {
+ 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(timeout, self, HackClearActor.TryClearHacks())
+ clearTrigger = context.system.scheduler.scheduleOnce(HackClearActor.timeout, self, HackClearActor.TryClearHacks())
}
- case 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) = recursivePartitionHacks(hackedObjects.iterator, now)
+ 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)
+ 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, timeout_time - (now - stillHackedObjects.head.hacked_at_time)) nanoseconds
+ 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())
}
@@ -43,33 +49,53 @@ class HackClearActor() extends Actor {
}
/**
- * na
- * @param iter na
- * @param now na
- * @param list na
+ * 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`:
- * the entries for all objects that are no longer hacked,
- * and the entries for all objects that are still hacked
+ * @return a `Tuple` of two `Lists`, whose qualifications are explained above
*/
- @tailrec private def recursivePartitionHacks(iter : Iterator[HackEntry], now : Long, list : List[HackEntry] = Nil) : (List[HackEntry], List[HackEntry]) = {
+ 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) {
- (list, iter.toList)
+ index
}
else {
val entry = iter.next()
- if(now - entry.hacked_at_time >= timeout_time) {
- recursivePartitionHacks(iter, now, list :+ entry)
+ if(now - entry.time >= HackClearActor.timeout_time) {
+ recursivePartitionEntries(iter, now, index + 1)
}
else {
- (list, entry +: iter.toList)
+ index
}
}
}
}
object HackClearActor {
- private final val timeout_time : Long = 60000000000L //nanoseconds (1 minute)
+ /** 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
private final val DefaultClearer : Cancellable = new Cancellable() {
@@ -77,11 +103,33 @@ object HackClearActor {
override def isCancelled : Boolean = true
}
- final case class ObjectIsHacked(target : PlanetSideServerObject, zone : Zone, unk1 : Long, unk2 : Long, hacked_at_time : Long = System.nanoTime())
-
+ /**
+ * 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)
-
- private final case class HackEntry(target : PlanetSideServerObject, zone : Zone, unk1 : Long, unk2 : Long, hacked_at_time : 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)
}
diff --git a/common/src/main/scala/net/psforever/packet/game/HackMessage.scala b/common/src/main/scala/net/psforever/packet/game/HackMessage.scala
index ae3c0451..2225033e 100644
--- a/common/src/main/scala/net/psforever/packet/game/HackMessage.scala
+++ b/common/src/main/scala/net/psforever/packet/game/HackMessage.scala
@@ -6,21 +6,65 @@ import scodec.Codec
import scodec.codecs._
/**
- *
- * @param unk1 na
- * @param unk2 na
- * @param unk3 na
- * @param unk4 na
- * @param unk5 na
- * @param unk6 na
- * @param unk7 na
- */
+ * An `Enumeration` of the various states and activities of the hacking process.
+ * These values are closely tied to the condition of the hacking progress bar and/or the condition of the hacked object.
+ *
+ * `Start` initially displays the hacking progress bar.
+ * `Ongoing` is a neutral state that keeps the progress bar displayed while its value updates. (unconfirmed?)
+ * `Finished` disposes of the hacking progress bar. It does not, by itself, mean the hack was successful.
+ * `Hacked` modifies the target of the hack.
+ * `HackCleared` modifies the target of the hack, opposite of `Hacked`.
+ */
+object HackState extends Enumeration {
+ type Type = Value
+
+ val
+ Unknown0,
+ Start,
+ Unknown2,
+ Ongoing,
+ Finished,
+ Unknown5,
+ Hacked,
+ HackCleared
+ = Value
+
+ implicit val codec = PacketHelpers.createEnumerationCodec(this, uint8L)
+}
+
+/**
+ * Dispatched by the server to control the process of hacking.
+ *
+ * Part of the hacking process is regulated by the server while another part of it is automatically reset by the client.
+ * The visibility, update, and closing of the hacking progress bar must be handled manually, for each tick.
+ * When hacking is complete, using the appropriate `HackState` will cue the target to be affected by the hack.
+ * Terminals and door IFF panels will temporarily expose their functionality;
+ * the faction association of vehicles will be converted permanently;
+ * a protracted process of a base conversion will be enacted; etc..
+ * This transfer of faction association occurs to align the target with the faction of the hacking player (as indicated).
+ * The client will select the faction without needing to be explicitly told
+ * and will select the appropriate action to enact upon the target.
+ * Upon the hack's completion, the target on the client will automatically revert back to its original state, if possible.
+ * (It will still be necessary to alert this change from the server's perspective.)
+ * @param unk1 na;
+ * hack type?
+ * @param target_guid the target of the hack
+ * @param player_guid the player
+ * @param progress the amount of progress visible;
+ * visible range is 0 - 100
+ * @param unk5 na;
+ * often a large number;
+ * doesn't seem to be `char_id`?
+ * @param hack_state hack state
+ * @param unk7 na;
+ * usually, 8?
+ */
final case class HackMessage(unk1 : Int,
- unk2 : Int,
- unk3 : Int,
- unk4 : Int,
+ target_guid : PlanetSideGUID,
+ player_guid : PlanetSideGUID,
+ progress : Int,
unk5 : Long,
- unk6 : Int,
+ hack_state : HackState.Value,
unk7 : Long)
extends PlanetSideGamePacket {
type Packet = HackMessage
@@ -30,12 +74,12 @@ final case class HackMessage(unk1 : Int,
object HackMessage extends Marshallable[HackMessage] {
implicit val codec : Codec[HackMessage] = (
- ("unk1" | uint2L) ::
- ("unk2" | uint16L) ::
- ("unk3" | uint16L) ::
- ("unk4" | uint8L) ::
- ("unk5" | uint32L) ::
- ("unk6" | uint8L) ::
- ("unk7" | uint32L)
- ).as[HackMessage]
+ ("unk1" | uint2L) ::
+ ("object_guid" | PlanetSideGUID.codec) ::
+ ("player_guid" | PlanetSideGUID.codec) ::
+ ("progress" | uint8L) ::
+ ("unk5" | uint32L) ::
+ ("hack_state" | HackState.codec) ::
+ ("unk7" | uint32L)
+ ).as[HackMessage]
}
diff --git a/common/src/test/scala/game/HackMessageTest.scala b/common/src/test/scala/game/HackMessageTest.scala
index 8473e2c5..469950d1 100644
--- a/common/src/test/scala/game/HackMessageTest.scala
+++ b/common/src/test/scala/game/HackMessageTest.scala
@@ -12,13 +12,13 @@ class HackMessageTest extends Specification {
"decode" in {
PacketCoding.DecodePacket(string).require match {
- case HackMessage(unk1, unk2, unk3, unk4, unk5, unk6, unk7) =>
+ case HackMessage(unk1, target_guid, player_guid, progress, unk5, hack_state, unk7) =>
unk1 mustEqual 0
- unk2 mustEqual 1024
- unk3 mustEqual 3607
- unk4 mustEqual 0
+ target_guid mustEqual PlanetSideGUID(1024)
+ player_guid mustEqual PlanetSideGUID(3607)
+ progress mustEqual 0
unk5 mustEqual 3212836864L
- unk6 mustEqual 1
+ hack_state mustEqual HackState.Start
unk7 mustEqual 8L
case _ =>
ko
@@ -26,7 +26,7 @@ class HackMessageTest extends Specification {
}
"encode" in {
- val msg = HackMessage(0,1024,3607,0,3212836864L,1,8L)
+ val msg = HackMessage(0, PlanetSideGUID(1024), PlanetSideGUID(3607), 0, 3212836864L, HackState.Start, 8L)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
}
diff --git a/pslogin/src/main/scala/PsLogin.scala b/pslogin/src/main/scala/PsLogin.scala
index de00fb1b..94551628 100644
--- a/pslogin/src/main/scala/PsLogin.scala
+++ b/pslogin/src/main/scala/PsLogin.scala
@@ -229,8 +229,8 @@ object PsLogin {
LocalObject(DoorObjectBuilder(door, 332))
LocalObject(DoorObjectBuilder(door, 372))
LocalObject(DoorObjectBuilder(door, 373))
- LocalObject(IFFLockObjectBuilder(external_lock, 556))
- LocalObject(IFFLockObjectBuilder(external_lock, 558))
+ LocalObject(IFFLockObjectBuilder(lock_external, 556))
+ LocalObject(IFFLockObjectBuilder(lock_external, 558))
LocalObject(TerminalObjectBuilder(cert_terminal, 186))
LocalObject(TerminalObjectBuilder(cert_terminal, 187))
LocalObject(TerminalObjectBuilder(cert_terminal, 188))
diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala
index 50e8a59a..2ed2191f 100644
--- a/pslogin/src/main/scala/WorldSessionActor.scala
+++ b/pslogin/src/main/scala/WorldSessionActor.scala
@@ -230,11 +230,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
case LocalServiceResponse.HackClear(target_guid, unk1, unk2) =>
log.info(s"Clear hack of $target_guid")
- sendResponse(PacketCoding.CreateGamePacket(0, HackMessage(0, target_guid.guid, guid.guid, 0, unk1, 7, unk2)))
+ sendResponse(PacketCoding.CreateGamePacket(0, HackMessage(0, target_guid, guid, 0, unk1, HackState.HackCleared, unk2)))
case LocalServiceResponse.HackObject(target_guid, unk1, unk2) =>
if(player.GUID != guid) {
- sendResponse(PacketCoding.CreateGamePacket(0, HackMessage(0, target_guid.guid, guid.guid, 100, unk1, 6, unk2)))
+ sendResponse(PacketCoding.CreateGamePacket(0, HackMessage(0, target_guid, guid, 100, unk1, HackState.Hacked, unk2)))
}
}
@@ -586,23 +586,23 @@ class WorldSessionActor extends Actor with MDCContextAware {
progressBarUpdate.cancel
if(progressBarValue.isDefined) {
val progressBarVal : Float = progressBarValue.get + delta
- val vis = if(progressBarVal == 0L) {
- 1
+ val vis = if(progressBarVal == 0L) { //hack state for progress bar visibility
+ HackState.Start
}
- else if(progressBarVal >= 100L) {
- 4
+ else if(progressBarVal > 100L) {
+ HackState.Finished
}
else {
- 3
+ HackState.Ongoing
}
- sendResponse(PacketCoding.CreateGamePacket(0, HackMessage(1, target.GUID.guid, player.GUID.guid, progressBarVal.toInt, 0L, vis, 8L)))
- if(progressBarVal > 100) {
+ sendResponse(PacketCoding.CreateGamePacket(0, HackMessage(1, target.GUID, player.GUID, progressBarVal.toInt, 0L, vis, 8L)))
+ if(progressBarVal > 100) { //done
progressBarValue = None
log.info(s"Hacked a $target")
- sendResponse(PacketCoding.CreateGamePacket(0, HackMessage(0, target.GUID.guid, player.GUID.guid, 100, 1114636288L, 6, 8L)))
+ sendResponse(PacketCoding.CreateGamePacket(0, HackMessage(0, target.GUID, player.GUID, 100, 1114636288L, HackState.Hacked, 8L)))
completeAction()
}
- else {
+ else { //continue next tick
tickAction.getOrElse(() => Unit)()
progressBarValue = Some(progressBarVal)
import scala.concurrent.duration._
@@ -1028,10 +1028,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
// TODO: Not all incoming UseItemMessage's respond with another UseItemMessage (i.e. doors only send out GenericObjectStateMsg)
continent.GUID(object_guid) match {
case Some(door : Door) =>
- continent.Map.DoorToLock.get(object_guid.guid) match { //check for IFFLock
+ continent.Map.DoorToLock.get(object_guid.guid) match { //check for IFF Lock
case Some(lock_guid) =>
val lock_hacked = continent.GUID(lock_guid).get.asInstanceOf[IFFLock].HackedBy match {
- case Some((tplayer, _)) =>
+ case Some((tplayer, _, _)) =>
tplayer.Faction == player.Faction
case None =>
false
@@ -1042,7 +1042,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
door.Actor ! Door.Use(player, msg)
}
case None =>
- if(lock_hacked) { //is locks hacked?
+ if(lock_hacked) { //is lock hacked? this may be a weird case
door.Actor ! Door.Use(player, msg)
}
}
@@ -1054,13 +1054,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
player.Slot(player.DrawnSlot).Equipment match {
case Some(tool : SimpleItem) =>
if(tool.Definition == GlobalDefinitions.remote_electronics_kit) {
+ //TODO get player hack level (for now, presume 15s in intervals of 4/s)
progressBarValue = Some(-2.66f)
- self ! WorldSessionActor.ItemHacking(player, panel, tool.GUID, 2.66f, HackTemporary(panel))
+ self ! WorldSessionActor.ItemHacking(player, panel, tool.GUID, 2.66f, FinishHackingDoor(panel, 1114636288L))
+ log.info("Hacking a door~")
}
case _ => ;
}
- log.info("Hacking a door~")
- //TODO get player hack level (for now, presume 15s in internals of 4/s)
case Some(obj : PlanetSideGameObject) =>
if(itemType != 121) {
@@ -1071,10 +1071,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
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 => ;
}
@@ -1625,9 +1622,18 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
- private def HackTemporary(target : PlanetSideServerObject)() : Unit = {
+ /**
+ * The process of hacking the `Door` `IFFLock` is completed.
+ * Pass the message onto the lock and onto the local events system.
+ * @param target the `IFFLock` belonging to the door that is being hacked
+ * @param unk na;
+ * used by `HackingMessage` as `unk5`
+ * @see `HackMessage`
+ */
+ //TODO add params here depending on which params in HackMessage are important
+ private def FinishHackingDoor(target : IFFLock, unk : Long)() : Unit = {
target.Actor ! CommonMessages.Hack(player)
- localService ! LocalServiceMessage(player.Continent, LocalAction.HackTemporarily(player.GUID, continent, target, 1114636288L))
+ localService ! LocalServiceMessage(continent.Id, LocalAction.HackTemporarily(player.GUID, continent, target, unk))
}
def failWithError(error : String) = {
@@ -1660,6 +1666,18 @@ object WorldSessionActor {
private final case class PlayerFailedToLoad(tplayer : Player)
private final case class ListAccountCharacters()
private final case class SetCurrentAvatar(tplayer : Player)
+
+ /**
+ * A message that indicates the user is using a remote electronics kit to hack some server object.
+ * Each time this message is sent for a given hack attempt counts as a single "tick" of progress.
+ * The process of "making progress" with a hack involves sending this message repeatedly until the progress is 100 or more.
+ * @param tplayer the player
+ * @param target the object being hacked
+ * @param tool_guid the REK
+ * @param delta how much the progress bar value changes each tick
+ * @param completeAction a custom action performed once the hack is completed
+ * @param tickAction an optional action is is performed for each tick of progress
+ */
private final case class ItemHacking(tplayer : Player,
target : PlanetSideServerObject,
tool_guid : PlanetSideGUID,