mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-03-06 21:50:22 +00:00
Timed capture consoles & towers / hacking improvements (#228)
* Add capture terminal definitions * Logging / documentation * Functionality for timed base hacks * Disable IFF locks while base is hacked * Hacking speed based on player's hacking certification level (and hack effect duration data - currently not functional) * Hack effect duration functionality * Sync hack states with clients joining the zone * Whitespace / comments * Allow IFF locks to be resecured by the owning faction if it has been hacked * Fix bases with no NTU silo failing to capture * Reset CC properly on hack that expires with no NTU in silo * Capture towers instantly and improve handling of hacked objects queue * Fix handling of neutral IFF Locks and remove unnecessary casting * Move HackCaptureActor to correct location * Re-enable Anguta door locks for air pad now hacking/resecuring IFF locks work Add capture terminals (timed CC only for now) for a few bases & a tower Add a few missed locks/doors Add a resource silo to Girru * Fix merge issues & missing documentation
This commit is contained in:
parent
8b5073dcbc
commit
abdebf09ba
23 changed files with 649 additions and 168 deletions
|
|
@ -864,6 +864,10 @@ object GlobalDefinitions {
|
|||
|
||||
val resource_silo = new ResourceSiloDefinition
|
||||
|
||||
val capture_terminal = new CaptureTerminalDefinition(158) // Base CC
|
||||
|
||||
val secondary_capture = new CaptureTerminalDefinition(751) // Tower CC
|
||||
|
||||
val manned_turret = new MannedTurretDefinition(480) {
|
||||
Name = "manned_turret"
|
||||
MaxHealth = 3600
|
||||
|
|
|
|||
|
|
@ -7,13 +7,8 @@ import net.psforever.types.Vector3
|
|||
trait Hackable {
|
||||
/** 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
|
||||
|
||||
private var hackSound : TriggeredSound.Value = TriggeredSound.HackDoor
|
||||
|
||||
def HackedBy : Option[(Player, PlanetSideGUID, Vector3)] = hackedBy
|
||||
|
||||
def HackedBy_=(agent : Player) : Option[(Player, PlanetSideGUID, Vector3)] = HackedBy_=(Some(agent))
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
|
@ -41,9 +36,27 @@ trait Hackable {
|
|||
HackedBy
|
||||
}
|
||||
|
||||
/** The sound made when the object is hacked */
|
||||
private var hackSound : TriggeredSound.Value = TriggeredSound.HackDoor
|
||||
def HackSound : TriggeredSound.Value = hackSound
|
||||
def HackSound_=(sound : TriggeredSound.Value) : TriggeredSound.Value = {
|
||||
hackSound = sound
|
||||
hackSound
|
||||
}
|
||||
|
||||
/** The duration in seconds a hack lasts for, based on the hacker's certification level */
|
||||
private var hackEffectDuration = Array(0, 0, 0 , 0)
|
||||
def HackEffectDuration: Array[Int] = hackEffectDuration
|
||||
def HackEffectDuration_=(arr: Array[Int]) : Array[Int] = {
|
||||
hackEffectDuration = arr
|
||||
arr
|
||||
}
|
||||
|
||||
/** How long it takes to hack the object in seconds, based on the hacker's certification level */
|
||||
private var hackDuration = Array(0, 0, 0, 0)
|
||||
def HackDuration: Array[Int] = hackDuration
|
||||
def HackDuration_=(arr: Array[Int]) : Array[Int] = {
|
||||
hackDuration = arr
|
||||
arr
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import net.psforever.packet.game.TriggeredSound
|
|||
class IFFLock(private val idef : IFFLockDefinition) extends Amenity with Hackable {
|
||||
def Definition : IFFLockDefinition = idef
|
||||
HackSound = TriggeredSound.HackDoor
|
||||
HackEffectDuration = Array(60, 180, 300, 360)
|
||||
HackDuration = Array(5, 3, 1, 1)
|
||||
}
|
||||
|
||||
object IFFLock {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import net.psforever.packet.game.TriggeredSound
|
|||
class Locker extends Amenity with Hackable {
|
||||
def Definition : LockerDefinition = GlobalDefinitions.mb_locker
|
||||
HackSound = TriggeredSound.HackTerminal
|
||||
HackEffectDuration = Array(0, 30, 60, 90)
|
||||
HackDuration = Array(0, 10, 5, 3)
|
||||
}
|
||||
|
||||
object Locker {
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ class ResourceSiloControl(resourceSilo : ResourceSilo) extends Actor with Factio
|
|||
if(resourceSilo.CapacitorDisplay != ntuBarLevel) {
|
||||
log.trace(s"Silo ${resourceSilo.GUID} NTU bar level has changed from ${resourceSilo.CapacitorDisplay} to ${ntuBarLevel}")
|
||||
resourceSilo.CapacitorDisplay = ntuBarLevel
|
||||
resourceSilo.Owner.Actor ! Building.SendMapUpdateToAllClients()
|
||||
resourceSilo.Owner.Actor ! Building.SendMapUpdate(all_clients = true)
|
||||
avatarService ! AvatarServiceMessage(resourceSilo.Owner.asInstanceOf[Building].Zone.Id, AvatarAction.PlanetsideAttribute(resourceSilo.GUID, 45, resourceSilo.CapacitorDisplay))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,6 @@ object Building {
|
|||
val obj = new Building(id, zone, buildingType)
|
||||
obj.Position = location
|
||||
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$id-$buildingType-building")
|
||||
obj.Actor ! "startup"
|
||||
obj
|
||||
}
|
||||
|
||||
|
|
@ -76,9 +75,8 @@ object Building {
|
|||
import akka.actor.Props
|
||||
val obj = new Building(id, zone, buildingType)
|
||||
obj.Actor = context.actorOf(Props(classOf[BuildingControl], obj), s"$id-$buildingType-building")
|
||||
obj.Actor ! "startup"
|
||||
obj
|
||||
}
|
||||
|
||||
final case class SendMapUpdateToAllClients()
|
||||
final case class SendMapUpdate(all_clients: Boolean)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,74 +1,114 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.structures
|
||||
|
||||
import akka.actor.{Actor, ActorRef}
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import net.psforever.objects.GlobalDefinitions
|
||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
||||
import net.psforever.objects.serverobject.terminals.CaptureTerminal
|
||||
import net.psforever.packet.game.{BuildingInfoUpdateMessage, PlanetSideGeneratorState}
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
import services.ServiceManager
|
||||
import services.ServiceManager.Lookup
|
||||
import services.galaxy.{GalaxyAction, GalaxyServiceMessage}
|
||||
import services.galaxy.{GalaxyAction, GalaxyResponse, GalaxyServiceMessage, GalaxyServiceResponse}
|
||||
import services.local.support.HackCaptureActor
|
||||
|
||||
import scala.util.Success
|
||||
import scala.concurrent.duration._
|
||||
import akka.pattern.ask
|
||||
|
||||
import scala.concurrent.{Await, Future}
|
||||
|
||||
class BuildingControl(building : Building) extends Actor with FactionAffinityBehavior.Check {
|
||||
def FactionObject : FactionAffinity = building
|
||||
var galaxyService : ActorRef = Actor.noSender
|
||||
var localService : ActorRef = Actor.noSender
|
||||
private[this] val log = org.log4s.getLogger
|
||||
|
||||
def receive : Receive = {
|
||||
case "startup" =>
|
||||
ServiceManager.serviceManager ! Lookup("galaxy") //ask for a resolver to deal with the GUID system
|
||||
|
||||
case ServiceManager.LookupResult("galaxy", endpoint) =>
|
||||
galaxyService = endpoint
|
||||
log.trace("BuildingControl: Building " + building.ModelId + " Got galaxy service " + endpoint)
|
||||
|
||||
// todo: This is just a temporary solution to drain NTU over time. When base object destruction is properly implemented NTU should be deducted when base objects repair themselves
|
||||
context.become(Processing)
|
||||
|
||||
case _ => log.warn("Message received before startup called");
|
||||
override def preStart = {
|
||||
log.info(s"Starting BuildingControl for ${building.GUID} / ${building.ModelId}")
|
||||
ServiceManager.serviceManager ! Lookup("galaxy")
|
||||
ServiceManager.serviceManager ! Lookup("local")
|
||||
}
|
||||
|
||||
def Processing : Receive = checkBehavior.orElse {
|
||||
def receive : Receive = checkBehavior.orElse {
|
||||
case ServiceManager.LookupResult("galaxy", endpoint) =>
|
||||
galaxyService = endpoint
|
||||
log.info("BuildingControl: Building " + building.ModelId + " Got galaxy service " + endpoint)
|
||||
case ServiceManager.LookupResult("local", endpoint) =>
|
||||
localService = endpoint
|
||||
log.info("BuildingControl: Building " + building.ModelId + " Got local service " + endpoint)
|
||||
case FactionAffinity.ConvertFactionAffinity(faction) =>
|
||||
val originalAffinity = building.Faction
|
||||
if(originalAffinity != (building.Faction = faction)) {
|
||||
building.Amenities.foreach(_.Actor forward FactionAffinity.ConfirmFactionAffinity())
|
||||
}
|
||||
sender ! FactionAffinity.AssertFactionAffinity(building, faction)
|
||||
case Building.SendMapUpdateToAllClients() =>
|
||||
log.info(s"Sending BuildingInfoUpdateMessage update to all clients. Zone: ${building.Zone.Number} - Building: ${building.ModelId}")
|
||||
case Building.SendMapUpdate(all_clients: Boolean) =>
|
||||
log.info(s"Sending BuildingInfoUpdateMessage update. Zone: ${building.Zone.Number} - Building: ${building.ModelId}")
|
||||
var ntuLevel = 0
|
||||
var is_hacked = false
|
||||
var hack_time_remaining_ms = 0L;
|
||||
var hacked_by_faction = PlanetSideEmpire.NEUTRAL
|
||||
|
||||
// Get Ntu level from silo if it exists
|
||||
building.Amenities.filter(x => (x.Definition == GlobalDefinitions.resource_silo)).headOption.asInstanceOf[Option[ResourceSilo]] match {
|
||||
case Some(obj: ResourceSilo) =>
|
||||
ntuLevel = obj.CapacitorDisplay.toInt
|
||||
case _ => ;
|
||||
}
|
||||
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(
|
||||
BuildingInfoUpdateMessage(
|
||||
|
||||
// Get hack status & time from control console if it exists
|
||||
building.Amenities.filter(x => x.Definition == GlobalDefinitions.capture_terminal).headOption.asInstanceOf[Option[CaptureTerminal with Hackable]] match {
|
||||
case Some(obj: CaptureTerminal with Hackable) =>
|
||||
if(!obj.HackedBy.isEmpty) {
|
||||
is_hacked = true
|
||||
hacked_by_faction = obj.HackedBy.get._1.Faction
|
||||
}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
val future = ask(localService, HackCaptureActor.GetHackTimeRemainingNanos(obj.GUID))(1 second)
|
||||
|
||||
//todo: this is blocking. Not so bad when we're only retrieving one piece of data but as more functionality is added we'll need to change this to be async but wait for all replies before sending BIUM to clients
|
||||
val time = Await.result(future, 1 second).asInstanceOf[Long]
|
||||
hack_time_remaining_ms = TimeUnit.MILLISECONDS.convert(time, TimeUnit.NANOSECONDS)
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
val msg = BuildingInfoUpdateMessage(
|
||||
continent_id = building.Zone.Number, //Zone
|
||||
building_id = building.Id, //Facility
|
||||
ntu_level = ntuLevel,
|
||||
is_hacked = false, //Hacked
|
||||
PlanetSideEmpire.NEUTRAL, //Base hacked by
|
||||
hack_time_remaining = 0, //Time remaining for hack (ms)
|
||||
empire_own = building.Faction, //Base owned by
|
||||
is_hacked,
|
||||
hacked_by_faction,
|
||||
hack_time_remaining_ms,
|
||||
empire_own = building.Faction,
|
||||
unk1 = 0, //!! Field != 0 will cause malformed packet. See class def.
|
||||
unk1x = None,
|
||||
generator_state = PlanetSideGeneratorState.Normal, //Generator state
|
||||
spawn_tubes_normal = true, //Respawn tubes operating state
|
||||
force_dome_active = false, //Force dome state
|
||||
lattice_benefit = 0, //Lattice benefits
|
||||
generator_state = PlanetSideGeneratorState.Normal,
|
||||
spawn_tubes_normal = true,
|
||||
force_dome_active = false,
|
||||
lattice_benefit = 0,
|
||||
cavern_benefit = 0, //!! Field > 0 will cause malformed packet. See class def.
|
||||
unk4 = Nil,
|
||||
unk5 = 0,
|
||||
unk6 = false,
|
||||
unk7 = 8, //!! Field != 8 will cause malformed packet. See class def.
|
||||
unk7x = None,
|
||||
boost_spawn_pain = false, //Boosted spawn room pain field
|
||||
boost_generator_pain = false //Boosted generator room pain field
|
||||
)))
|
||||
boost_spawn_pain = false,
|
||||
boost_generator_pain = false
|
||||
)
|
||||
|
||||
if(all_clients) {
|
||||
galaxyService ! GalaxyServiceMessage(GalaxyAction.MapUpdate(msg))
|
||||
} else {
|
||||
// Fake a GalaxyServiceResponse response back to just the sender
|
||||
sender ! GalaxyServiceResponse("", GalaxyResponse.MapUpdate(msg))
|
||||
}
|
||||
|
||||
case default =>
|
||||
log.warn(s"BuildingControl: Unknown message ${default} received from ${sender().path}")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.structures.Amenity
|
||||
import net.psforever.packet.game.TriggeredSound
|
||||
|
||||
class CaptureTerminal(private val idef : CaptureTerminalDefinition) extends Amenity with Hackable {
|
||||
def Definition : CaptureTerminalDefinition = idef
|
||||
HackDuration = Array(60, 40, 20, 15)
|
||||
HackSound = TriggeredSound.HackTerminal
|
||||
}
|
||||
|
||||
object CaptureTerminal {
|
||||
/**
|
||||
* Overloaded constructor.
|
||||
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||
*/
|
||||
def apply(tdef : CaptureTerminalDefinition) : CaptureTerminal = {
|
||||
new CaptureTerminal(tdef)
|
||||
}
|
||||
|
||||
import akka.actor.ActorContext
|
||||
def Constructor(tdef: CaptureTerminalDefinition)(id : Int, context : ActorContext) : CaptureTerminal = {
|
||||
import akka.actor.Props
|
||||
val obj = CaptureTerminal(tdef)
|
||||
obj.Actor = context.actorOf(Props(classOf[CaptureTerminalControl], obj), s"${tdef.Name}_$id")
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||
|
||||
|
||||
class CaptureTerminalControl(terminal : CaptureTerminal) extends Actor with FactionAffinityBehavior.Check {
|
||||
def FactionObject : FactionAffinity = terminal
|
||||
|
||||
def receive : Receive = checkBehavior.orElse {
|
||||
case CommonMessages.Hack(player) =>
|
||||
terminal.HackedBy = player
|
||||
sender ! true
|
||||
case CommonMessages.ClearHack() =>
|
||||
terminal.HackedBy = None
|
||||
|
||||
case _ => ; //no default message
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.definition.ObjectDefinition
|
||||
|
||||
class CaptureTerminalDefinition(objectId : Int) extends ObjectDefinition(objectId) {
|
||||
Name = if(objectId == 158) {
|
||||
"capture_terminal"
|
||||
} else if (objectId == 751) {
|
||||
"secondary_capture"
|
||||
} else {
|
||||
throw new IllegalArgumentException("Not a valid capture terminal object id")
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,8 @@ import net.psforever.types.TransactionType
|
|||
*/
|
||||
class Terminal(tdef : TerminalDefinition) extends Amenity with Hackable {
|
||||
HackSound = TriggeredSound.HackTerminal
|
||||
HackEffectDuration = Array(0, 30, 60, 90)
|
||||
HackDuration = Array(0, 10, 5, 3)
|
||||
|
||||
//the following fields and related methods are neither finalized nor integrated; GOTO Request
|
||||
private var health : Int = 100 //TODO not real health value
|
||||
|
|
|
|||
|
|
@ -61,7 +61,8 @@ class InterstellarCluster(zones : List[Zone]) extends Actor {
|
|||
sender ! Zone.Lattice.NoValidSpawnPoint(zone_number, None)
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
case _ =>
|
||||
log.warn(s"InterstellarCluster received unknown message");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ object HackState extends Enumeration {
|
|||
* 2 when performing (phalanx) upgrades;
|
||||
* 3 for building objects during login phase;
|
||||
* hack type?
|
||||
* possibly player hacking level 0-3?
|
||||
* @param target_guid the target of the hack
|
||||
* @param player_guid the player
|
||||
* @param progress the amount of progress visible;
|
||||
|
|
@ -65,6 +66,7 @@ object HackState extends Enumeration {
|
|||
* doesn't seem to be `char_id`?
|
||||
* @param hack_state hack state
|
||||
* @param unk7 na;
|
||||
* 5 - boost pain field at matrixing terminal?
|
||||
* usually, 8?
|
||||
*/
|
||||
final case class HackMessage(unk1 : Int,
|
||||
|
|
|
|||
|
|
@ -51,6 +51,15 @@ import scodec.codecs._
|
|||
* `17 - BEP. Value seems to be the same as BattleExperienceMessage`<br>
|
||||
* `18 - CEP.`<br>
|
||||
* `19 - Anchors. Value is 0 to disengage, 1 to engage.`<br>
|
||||
* `20 - Control console hacking. "The FactionName has hacked into BaseName` - also sets timer on CC and yellow base warning lights on<br>
|
||||
* <ul>
|
||||
* <li>65535 segments per faction in deciseconds (seconds * 10)</li>
|
||||
* <li>0-65535 = Neutral 0 seconds to 1h 49m 14s</li>
|
||||
* <li>65536 - 131071 - TR</li>
|
||||
* <li>131072 - 196607 - NC</li>
|
||||
* <li>196608 - 262143 - VS</li>
|
||||
* <li>17039360 - CC Resecured</li>
|
||||
* </ul>
|
||||
* `24 - Learn certifications with value :`<br>
|
||||
* 01 : Medium Assault<br>
|
||||
* 02 : Heavy Assault<br>
|
||||
|
|
@ -127,6 +136,8 @@ import scodec.codecs._
|
|||
* `116 - Apply colour to REK beam and REK icon above players (0 = yellow, 1 = red, 2 = purple, 3 = blue)`<br>
|
||||
* Client to Server : <br>
|
||||
* `106 - Custom Head`<br>
|
||||
* `224 - Player/vehicle joins black ops`<br>
|
||||
* `228 - Player/vehicle leaves black ops`<br>
|
||||
* <br>
|
||||
* `Vehicles:`<br>
|
||||
* `10 - Driver seat permissions (0 = Locked, 1 = Group, 3 = Empire)`<br>
|
||||
|
|
|
|||
|
|
@ -10,22 +10,26 @@ import scodec.codecs._
|
|||
* (Where the child object was before it was moved is not specified or important.)
|
||||
* @see `Definition.ObjectId`<br>
|
||||
* `TurretUpgrade`
|
||||
* @param avatar_guid the player
|
||||
* @param item_used_guid the item used;
|
||||
* e.g., a REK to hack, or a medkit to heal
|
||||
* @param object_guid the object affected;
|
||||
* e.g., door when opened, terminal when accessed, avatar when medkit used
|
||||
* @param unk2 na;
|
||||
* when upgrading phalanx turrets, 1 for `AVCombo` and 2 for `FlakCombo`
|
||||
* @param unk3 using tools, e.g., a REK or nano-dispenser
|
||||
* @param unk4 na
|
||||
* @param unk5 na
|
||||
* @param unk6 na
|
||||
* @param unk7 na;
|
||||
* 25 when door 223 when terminal
|
||||
* @param unk8 na;
|
||||
* 0 when door 1 when use rek (252 then equipment term)
|
||||
* @param object_id he object id for `object_guid`'s object
|
||||
* @param avatar_guid the player
|
||||
* @param item_used_guid the item used;
|
||||
* e.g. a REK to hack or a medkit to heal.
|
||||
* @param object_guid the object affected;
|
||||
* e.g., door when opened, terminal when accessed, avatar when medkit used
|
||||
* @param unk2 na;
|
||||
* when upgrading phalanx turrets, 1 for `AVCombo` and 2 for `FlakCombo`
|
||||
* @param unk3 using tools, e.g., a REK or nano-dispenser
|
||||
* @param unk4 seems to be related to T-REK viruses.
|
||||
* 0 - unlock all doors
|
||||
* 1 - disable linked benefits
|
||||
* 2 - double ntu drain
|
||||
* 3 - disable enemy radar
|
||||
* 4 - access equipment terminals
|
||||
* @param unk6 na
|
||||
* @param unk7 na;
|
||||
* 25 when door 223 when terminal
|
||||
* @param unk8 na;
|
||||
* 0 when door 1 when use rek (252 then equipment term)
|
||||
* @param object_id the object id `object_guid`'s object
|
||||
*/
|
||||
final case class UseItemMessage(avatar_guid : PlanetSideGUID,
|
||||
item_used_guid : PlanetSideGUID,
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@ package services.local
|
|||
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.terminals.CaptureTerminal
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.{PlanetSideGUID, TriggeredSound}
|
||||
import net.psforever.types.Vector3
|
||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||
|
||||
object LocalAction {
|
||||
trait Action
|
||||
|
|
@ -13,7 +15,10 @@ object LocalAction {
|
|||
final case class DoorOpens(player_guid : PlanetSideGUID, continent : Zone, door : Door) extends Action
|
||||
final case class DoorCloses(player_guid : PlanetSideGUID, door_guid : PlanetSideGUID) extends Action
|
||||
final case class HackClear(player_guid : PlanetSideGUID, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
|
||||
final case class HackTemporarily(player_guid : PlanetSideGUID, continent : Zone, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
|
||||
final case class HackTemporarily(player_guid : PlanetSideGUID, continent : Zone, target : PlanetSideServerObject, unk1 : Long, duration: Int, unk2 : Long = 8L) extends Action
|
||||
final case class ClearTemporaryHack(player_guid: PlanetSideGUID, target: PlanetSideServerObject with Hackable) extends Action
|
||||
final case class HackCaptureTerminal(player_guid : PlanetSideGUID, continent : Zone, target : CaptureTerminal, unk1 : Long, unk2 : Long = 8L, isResecured : Boolean) extends Action
|
||||
final case class ProximityTerminalEffect(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, effectState : Boolean) extends Action
|
||||
final case class TriggerSound(player_guid : PlanetSideGUID, sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Action
|
||||
final case class SetEmpire(object_guid: PlanetSideGUID, empire: PlanetSideEmpire.Value) extends Action
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
package services.local
|
||||
|
||||
import net.psforever.packet.game.{PlanetSideGUID, TriggeredSound}
|
||||
import net.psforever.types.Vector3
|
||||
import net.psforever.types.{PlanetSideEmpire, Vector3}
|
||||
|
||||
object LocalResponse {
|
||||
trait Response
|
||||
|
|
@ -11,6 +11,8 @@ object LocalResponse {
|
|||
final case class DoorCloses(door_guid : PlanetSideGUID) extends Response
|
||||
final case class HackClear(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
|
||||
final case class HackObject(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
|
||||
final case class HackCaptureTerminal(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long, isResecured: Boolean) extends Response
|
||||
final case class ProximityTerminalEffect(object_guid : PlanetSideGUID, effectState : Boolean) extends Response
|
||||
final case class TriggerSound(sound : TriggeredSound.Value, pos : Vector3, unk : Int, volume : Float) extends Response
|
||||
final case class SetEmpire(object_guid: PlanetSideGUID, empire: PlanetSideEmpire.Value) extends Response
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,44 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.local
|
||||
|
||||
import akka.actor.{Actor, Props}
|
||||
import services.{GenericEventBus, Service}
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
import net.psforever.objects.zones.{InterstellarCluster, Zone}
|
||||
import net.psforever.objects.zones.InterstellarCluster.GetWorld
|
||||
import services.local.support.{DoorCloseActor, HackCaptureActor, HackClearActor}
|
||||
import services.{GenericEventBus, Service, ServiceManager}
|
||||
import services.local.support.{DoorCloseActor, HackClearActor}
|
||||
|
||||
import scala.util.Success
|
||||
import scala.concurrent.duration._
|
||||
import akka.pattern.ask
|
||||
import net.psforever.objects.GlobalDefinitions
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
||||
import net.psforever.objects.serverobject.structures.{Amenity, Building}
|
||||
import net.psforever.objects.serverobject.terminals.CaptureTerminal
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import services.ServiceManager.Lookup
|
||||
|
||||
class LocalService extends Actor {
|
||||
private val doorCloser = context.actorOf(Props[DoorCloseActor], "local-door-closer")
|
||||
private val hackClearer = context.actorOf(Props[HackClearActor], "local-hack-clearer")
|
||||
private val hackCapturer = context.actorOf(Props[HackCaptureActor], "local-hack-capturer")
|
||||
private [this] val log = org.log4s.getLogger
|
||||
var cluster : ActorRef = Actor.noSender
|
||||
|
||||
override def preStart = {
|
||||
log.info("Starting...")
|
||||
ServiceManager.serviceManager ! Lookup("cluster")
|
||||
}
|
||||
|
||||
val LocalEvents = new GenericEventBus[LocalServiceResponse]
|
||||
|
||||
def receive = {
|
||||
case ServiceManager.LookupResult("cluster", endpoint) =>
|
||||
cluster = endpoint
|
||||
log.trace("LocalService got cluster service " + endpoint)
|
||||
|
||||
case Service.Join(channel) =>
|
||||
val path = s"/$channel/Local"
|
||||
val who = sender()
|
||||
|
|
@ -50,11 +72,31 @@ class LocalService extends Actor {
|
|||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackClear(target.GUID, unk1, unk2))
|
||||
)
|
||||
case LocalAction.HackTemporarily(player_guid, zone, target, unk1, unk2) =>
|
||||
hackClearer ! HackClearActor.ObjectIsHacked(target, zone, unk1, unk2)
|
||||
case LocalAction.HackTemporarily(player_guid, zone, target, unk1, duration, unk2) =>
|
||||
hackClearer ! HackClearActor.ObjectIsHacked(target, zone, unk1, unk2, duration)
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackObject(target.GUID, unk1, unk2))
|
||||
)
|
||||
case LocalAction.ClearTemporaryHack(player_guid, target) =>
|
||||
hackClearer ! HackClearActor.ObjectIsResecured(target)
|
||||
case LocalAction.HackCaptureTerminal(player_guid, zone, target, unk1, unk2, isResecured) =>
|
||||
|
||||
if(isResecured){
|
||||
hackCapturer ! HackCaptureActor.ClearHack(target, zone)
|
||||
} else {
|
||||
target.Definition match {
|
||||
case GlobalDefinitions.capture_terminal =>
|
||||
// Base CC
|
||||
hackCapturer ! HackCaptureActor.ObjectIsHacked(target, zone, unk1, unk2, duration = 15 minutes)
|
||||
case GlobalDefinitions.secondary_capture =>
|
||||
// Tower CC
|
||||
hackCapturer ! HackCaptureActor.ObjectIsHacked(target, zone, unk1, unk2, duration = 1 nanosecond)
|
||||
}
|
||||
}
|
||||
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackCaptureTerminal(target.GUID, unk1, unk2, isResecured))
|
||||
)
|
||||
case LocalAction.ProximityTerminalEffect(player_guid, object_guid, effectState) =>
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.ProximityTerminalEffect(object_guid, effectState))
|
||||
|
|
@ -63,6 +105,10 @@ class LocalService extends Actor {
|
|||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.TriggerSound(sound, pos, unk, volume))
|
||||
)
|
||||
case LocalAction.SetEmpire(object_guid, empire) =>
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", PlanetSideGUID(-1), LocalResponse.SetEmpire(object_guid, empire))
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
|
|
@ -74,10 +120,48 @@ class LocalService extends Actor {
|
|||
|
||||
//response from HackClearActor
|
||||
case HackClearActor.ClearTheHack(target_guid, zone_id, unk1, unk2) =>
|
||||
log.warn(s"Clearing hack for ${target_guid}")
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$zone_id/Local", Service.defaultPlayerGUID, LocalResponse.HackClear(target_guid, unk1, unk2))
|
||||
)
|
||||
|
||||
case HackCaptureActor.HackTimeoutReached(capture_terminal_guid, zone_id, unk1, unk2, hackedByFaction) =>
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
ask(cluster, InterstellarCluster.GetWorld(zone_id))(1 seconds).onComplete {
|
||||
case Success(InterstellarCluster.GiveWorld(zoneId, zone)) =>
|
||||
val terminal = zone.asInstanceOf[Zone].GUID(capture_terminal_guid).get.asInstanceOf[CaptureTerminal]
|
||||
val building = terminal.Owner.asInstanceOf[Building]
|
||||
|
||||
// todo: Move this to a function for Building
|
||||
var ntuLevel = 0
|
||||
building.Amenities.filter(x => (x.Definition == GlobalDefinitions.resource_silo)).headOption.asInstanceOf[Option[ResourceSilo]] match {
|
||||
case Some(obj: ResourceSilo) =>
|
||||
ntuLevel = obj.CapacitorDisplay.toInt
|
||||
case _ =>
|
||||
// Base has no NTU silo - likely a tower / cavern CC
|
||||
ntuLevel = 1
|
||||
}
|
||||
|
||||
if(ntuLevel > 0) {
|
||||
log.info(s"Setting base ${building.ModelId} as owned by ${hackedByFaction}")
|
||||
|
||||
building.Faction = hackedByFaction
|
||||
self ! LocalServiceMessage(zone.Id, LocalAction.SetEmpire(PlanetSideGUID(building.ModelId), hackedByFaction))
|
||||
} else {
|
||||
log.info("Base hack completed, but base was out of NTU.")
|
||||
}
|
||||
|
||||
// Reset CC back to normal operation
|
||||
self ! LocalServiceMessage(zone.Id, LocalAction.HackCaptureTerminal(PlanetSideGUID(-1), zone, terminal, 0, 8L, isResecured = true))
|
||||
//todo: this appears to be the way to reset the base warning lights after the hack finishes but it doesn't seem to work. The attribute above is a workaround
|
||||
self ! HackClearActor.ClearTheHack(PlanetSideGUID(building.ModelId), zone.Id, 3212836864L, 8L)
|
||||
case Success(_) =>
|
||||
log.warn("Got success from InterstellarCluster.GetWorld but didn't know how to handle it")
|
||||
|
||||
case scala.util.Failure(_) => log.warn(s"LocalService Failed to get zone when hack timeout was reached")
|
||||
}
|
||||
case HackCaptureActor.GetHackTimeRemainingNanos(capture_console_guid) =>
|
||||
hackCapturer forward HackCaptureActor.GetHackTimeRemainingNanos(capture_console_guid)
|
||||
case msg =>
|
||||
log.info(s"Unhandled message $msg from $sender")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,112 @@
|
|||
package services.local.support
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.objects.DefaultCancellable
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.structures.Building
|
||||
import net.psforever.objects.serverobject.terminals.CaptureTerminal
|
||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
import scala.concurrent.duration.{FiniteDuration, _}
|
||||
|
||||
class HackCaptureActor extends Actor {
|
||||
private [this] val log = org.log4s.getLogger
|
||||
|
||||
private var clearTrigger : Cancellable = DefaultCancellable.obj
|
||||
|
||||
/** A `List` of currently hacked server objects */
|
||||
private var hackedObjects : List[HackCaptureActor.HackEntry] = Nil
|
||||
|
||||
def receive : Receive = {
|
||||
case HackCaptureActor.ObjectIsHacked(target, zone, unk1, unk2, duration, time) =>
|
||||
log.trace(s"${target.GUID} is hacked.")
|
||||
|
||||
hackedObjects.filter(x => x.target == target).headOption match {
|
||||
case Some(x) =>
|
||||
log.trace(s"${target.GUID} was already hacked - removing it from the hacked objects list before re-adding it.")
|
||||
hackedObjects = hackedObjects.filterNot(x => x.target == target)
|
||||
log.warn(s"len: ${hackedObjects.length}")
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
hackedObjects = hackedObjects :+ HackCaptureActor.HackEntry(target, zone, unk1, unk2, duration, time)
|
||||
|
||||
// Restart the timer, in case this is the first object in the hacked objects list or the object was removed and re-added
|
||||
RestartTimer()
|
||||
|
||||
target.Owner.Actor ! Building.SendMapUpdate(all_clients = true)
|
||||
|
||||
case HackCaptureActor.ProcessCompleteHacks() =>
|
||||
log.trace("Processing complete hacks")
|
||||
clearTrigger.cancel
|
||||
val now : Long = System.nanoTime
|
||||
val stillHacked = hackedObjects.filter(x => now - x.hack_timestamp <= x.duration.toNanos)
|
||||
val unhackObjects = hackedObjects.filter(x => now - x.hack_timestamp >= x.duration.toNanos)
|
||||
hackedObjects = stillHacked
|
||||
unhackObjects.foreach(entry => {
|
||||
log.trace(s"Capture terminal hack timeout reached for terminal ${entry.target.GUID}")
|
||||
|
||||
val hackedByFaction = entry.target.HackedBy.get._1.Faction
|
||||
entry.target.Actor ! CommonMessages.ClearHack()
|
||||
|
||||
context.parent ! HackCaptureActor.HackTimeoutReached(entry.target.GUID, entry.zone.Id, entry.unk1, entry.unk2, hackedByFaction) //call up to the main event system
|
||||
})
|
||||
|
||||
// If there's hacked objects left in the list restart the timer with the shortest hack time left
|
||||
RestartTimer()
|
||||
|
||||
case HackCaptureActor.ClearHack(target, zone) =>
|
||||
hackedObjects = hackedObjects.filterNot(x => x.target == target)
|
||||
target.Owner.Actor ! Building.SendMapUpdate(all_clients = true)
|
||||
|
||||
// Restart the timer in case the object we just removed was the next one scheduled
|
||||
RestartTimer()
|
||||
|
||||
case HackCaptureActor.GetHackTimeRemainingNanos(capture_console_guid) =>
|
||||
hackedObjects.filter(x => x.target.GUID == capture_console_guid).headOption match {
|
||||
case Some(obj: HackCaptureActor.HackEntry) =>
|
||||
val time_left: Long = obj.duration.toNanos - (System.nanoTime - obj.hack_timestamp)
|
||||
sender ! time_left
|
||||
case _ =>
|
||||
log.warn(s"Couldn't find capture terminal guid ${capture_console_guid} in hackedObjects list")
|
||||
sender ! 0L
|
||||
}
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
private def RestartTimer(): Unit = {
|
||||
if(hackedObjects.length != 0) {
|
||||
val now = System.nanoTime()
|
||||
def minTimeLeft(entry1: HackCaptureActor.HackEntry, entry2: HackCaptureActor.HackEntry): HackCaptureActor.HackEntry = {
|
||||
val entry1TimeLeft = entry1.duration.toNanos - (now - entry1.hack_timestamp)
|
||||
val entry2TimeLeft = entry2.duration.toNanos - (now - entry2.hack_timestamp)
|
||||
if(entry1TimeLeft < entry2TimeLeft) entry1 else entry2
|
||||
}
|
||||
|
||||
val hackEntry = hackedObjects.reduceLeft(minTimeLeft)
|
||||
val short_timeout : FiniteDuration = math.max(1, hackEntry.duration.toNanos - (System.nanoTime - hackEntry.hack_timestamp)) nanoseconds
|
||||
|
||||
log.trace(s"Still items left in hacked objects list. Checking again in ${short_timeout}")
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
clearTrigger = context.system.scheduler.scheduleOnce(short_timeout, self, HackCaptureActor.ProcessCompleteHacks())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object HackCaptureActor {
|
||||
final case class ObjectIsHacked(target : CaptureTerminal, zone : Zone, unk1 : Long, unk2 : Long, duration: FiniteDuration, time : Long = System.nanoTime())
|
||||
|
||||
final case class HackTimeoutReached(capture_terminal_guid : PlanetSideGUID, zone_id : String, unk1 : Long, unk2 : Long, hackedByFaction : PlanetSideEmpire.Value)
|
||||
|
||||
final case class ClearHack(target : CaptureTerminal, zone : Zone)
|
||||
|
||||
final case class GetHackTimeRemainingNanos(capture_console_guid: PlanetSideGUID)
|
||||
|
||||
|
||||
private final case class ProcessCompleteHacks()
|
||||
|
||||
private final case class HackEntry(target : PlanetSideServerObject with Hackable, zone : Zone, unk1 : Long, unk2 : Long, duration: FiniteDuration, hack_timestamp : Long)
|
||||
}
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package services.local.support
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.objects.DefaultCancellable
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
|
@ -20,15 +23,15 @@ class HackClearActor() extends Actor {
|
|||
private var clearTrigger : Cancellable = DefaultCancellable.obj
|
||||
/** A `List` of currently hacked server objects */
|
||||
private var hackedObjects : List[HackClearActor.HackEntry] = Nil
|
||||
//private[this] val log = org.log4s.getLogger
|
||||
private[this] val log = org.log4s.getLogger
|
||||
|
||||
def receive : Receive = {
|
||||
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(HackClearActor.timeout, self, HackClearActor.TryClearHacks())
|
||||
}
|
||||
case HackClearActor.ObjectIsHacked(target, zone, unk1, unk2, duration, time) =>
|
||||
val durationNanos = TimeUnit.NANOSECONDS.convert(duration, TimeUnit.SECONDS)
|
||||
hackedObjects = hackedObjects :+ HackClearActor.HackEntry(target, zone, unk1, unk2, time, durationNanos)
|
||||
|
||||
// Restart the timer, in case this is the first object in the hacked objects list
|
||||
RestartTimer()
|
||||
|
||||
case HackClearActor.TryClearHacks() =>
|
||||
clearTrigger.cancel
|
||||
|
|
@ -41,15 +44,36 @@ class HackClearActor() extends Actor {
|
|||
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, HackClearActor.timeout_time - (now - stillHackedObjects.head.time)) nanoseconds
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
clearTrigger = context.system.scheduler.scheduleOnce(short_timeout, self, HackClearActor.TryClearHacks())
|
||||
RestartTimer()
|
||||
|
||||
case HackClearActor.ObjectIsResecured(target) =>
|
||||
val obj = hackedObjects.filter(x => x.target == target).headOption
|
||||
obj match {
|
||||
case Some(entry: HackClearActor.HackEntry) =>
|
||||
hackedObjects = hackedObjects.filterNot(x => x.target == target)
|
||||
entry.target.Actor ! CommonMessages.ClearHack()
|
||||
context.parent ! HackClearActor.ClearTheHack(entry.target.GUID, entry.zone.Id, entry.unk1, entry.unk2) //call up to the main event system
|
||||
|
||||
// Restart the timer in case the object we just removed was the next one scheduled
|
||||
RestartTimer()
|
||||
case None => ;
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
private def RestartTimer(): Unit = {
|
||||
if(hackedObjects.length != 0) {
|
||||
val now = System.nanoTime()
|
||||
val (unhackObjects, stillHackedObjects) = PartitionEntries(hackedObjects, now)
|
||||
val short_timeout : FiniteDuration = math.max(1, stillHackedObjects.head.duration - (now - stillHackedObjects.head.time)) nanoseconds
|
||||
|
||||
log.warn(s"Still items left in hacked objects list. Checking again in ${short_timeout}")
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
clearTrigger = context.system.scheduler.scheduleOnce(short_timeout, self, HackClearActor.TryClearHacks())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over entries in a `List` until an entry that does not exceed the time limit is discovered.
|
||||
* Separate the original `List` into two:
|
||||
|
|
@ -84,7 +108,7 @@ class HackClearActor() extends Actor {
|
|||
}
|
||||
else {
|
||||
val entry = iter.next()
|
||||
if(now - entry.time >= HackClearActor.timeout_time) {
|
||||
if(now - entry.time >= entry.duration) {
|
||||
recursivePartitionEntries(iter, now, index + 1)
|
||||
}
|
||||
else {
|
||||
|
|
@ -95,26 +119,31 @@ class HackClearActor() extends Actor {
|
|||
}
|
||||
|
||||
object HackClearActor {
|
||||
/** 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
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param duration how long the object is to stay hacked for in seconds
|
||||
* @see `HackEntry`
|
||||
*/
|
||||
final case class ObjectIsHacked(target : PlanetSideServerObject, zone : Zone, unk1 : Long, unk2 : Long, time : Long = System.nanoTime())
|
||||
final case class ObjectIsHacked(target : PlanetSideServerObject, zone : Zone, unk1 : Long, unk2 : Long, duration: Int, time : Long = System.nanoTime())
|
||||
|
||||
/**
|
||||
* Message used to request that a hack is cleared from the hacked objects list and the unhacked status returned to all clients
|
||||
*
|
||||
*/
|
||||
final case class ObjectIsResecured(target: PlanetSideServerObject with Hackable)
|
||||
|
||||
/**
|
||||
* 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 obj 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)
|
||||
final case class ClearTheHack(obj : PlanetSideGUID, zone_id : String, unk1 : Long, unk2 : Long)
|
||||
|
||||
|
||||
/**
|
||||
* Internal message used to signal a test of the queued door information.
|
||||
*/
|
||||
|
|
@ -122,11 +151,12 @@ object HackClearActor {
|
|||
|
||||
/**
|
||||
* 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.
|
||||
* The `zone` is maintained separately to ensure that any message resulting in an attempt to close doors is targeted.
|
||||
* @param target the server object
|
||||
* @param zone the zone in which the object resides
|
||||
* @param time when the object was hacked
|
||||
* @param duration The hack duration in nanoseconds
|
||||
* @see `ObjectIsHacked`
|
||||
*/
|
||||
private final case class HackEntry(target : PlanetSideServerObject, zone : Zone, unk1 : Long, unk2 : Long, time : Long)
|
||||
private final case class HackEntry(target : PlanetSideServerObject, zone : Zone, unk1 : Long, unk2 : Long, time : Long, duration: Long)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ class ResourceSiloControlUpdate1Test extends ActorTest {
|
|||
assert(reply1.asInstanceOf[AvatarServiceMessage]
|
||||
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_value == 3)
|
||||
|
||||
assert(reply2.isInstanceOf[Building.SendMapUpdateToAllClients])
|
||||
assert(reply2.isInstanceOf[Building.SendMapUpdate])
|
||||
|
||||
val reply3 = probe1.receiveOne(500 milliseconds)
|
||||
assert(reply3.isInstanceOf[AvatarServiceMessage])
|
||||
|
|
@ -249,7 +249,7 @@ class ResourceSiloControlUpdate2Test extends ActorTest {
|
|||
assert(reply1.asInstanceOf[AvatarServiceMessage]
|
||||
.actionMessage.asInstanceOf[AvatarAction.PlanetsideAttribute].attribute_value == 2)
|
||||
|
||||
assert(reply2.isInstanceOf[Building.SendMapUpdateToAllClients])
|
||||
assert(reply2.isInstanceOf[Building.SendMapUpdate])
|
||||
|
||||
val reply3 = probe1.receiveOne(500 milliseconds)
|
||||
assert(obj.LowNtuWarningOn == false)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue