mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +00:00
comments and documentation, mainly; adjusted the list splitting functionality in the LocalService support Actors
This commit is contained in:
parent
0a4bac8ab5
commit
d5f40a3d5f
|
|
@ -1245,7 +1245,7 @@ object GlobalDefinitions {
|
|||
cert_terminal = new CertTerminalDefinition
|
||||
|
||||
val
|
||||
external_lock = new IFFLockDefinition
|
||||
lock_external = new IFFLockDefinition
|
||||
val
|
||||
door = new DoorDefinition
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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."<br>
|
||||
* <br>
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`.<br>
|
||||
* <br>
|
||||
* 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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.<br>
|
||||
* <br>
|
||||
* `Start` initially displays the hacking progress bar.<br>
|
||||
* `Ongoing` is a neutral state that keeps the progress bar displayed while its value updates. (unconfirmed?)<br>
|
||||
* `Finished` disposes of the hacking progress bar. It does not, by itself, mean the hack was successful.<br>
|
||||
* `Hacked` modifies the target of the hack.<br>
|
||||
* `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.<br>
|
||||
* <br>
|
||||
* 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]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue