mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-19 18:14:44 +00:00
Merge pull request #676 from Mazo/capture-terminal-refactor
Capture terminal logic refactor
This commit is contained in:
commit
ab354e49df
|
|
@ -6,8 +6,8 @@ import akka.actor.typed.scaladsl.adapter._
|
|||
import akka.actor.{Actor, ActorRef, Cancellable, MDCContextAware}
|
||||
import akka.pattern.ask
|
||||
import akka.util.Timeout
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
import net.psforever.actors.net.MiddlewareActor
|
||||
import net.psforever.services.ServiceManager.Lookup
|
||||
import net.psforever.objects.locker.LockerContainer
|
||||
|
|
@ -37,7 +37,6 @@ import net.psforever.objects.serverobject.deploy.Deployment
|
|||
import net.psforever.objects.serverobject.doors.Door
|
||||
import net.psforever.objects.serverobject.generator.Generator
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
|
||||
import net.psforever.objects.serverobject.locks.IFFLock
|
||||
import net.psforever.objects.serverobject.mblocker.Locker
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
|
|
@ -45,6 +44,8 @@ import net.psforever.objects.serverobject.pad.VehicleSpawnPad
|
|||
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
||||
import net.psforever.objects.serverobject.structures.{Amenity, Building, StructureType, WarpGate}
|
||||
import net.psforever.objects.serverobject.terminals._
|
||||
import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminals}
|
||||
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.objects.serverobject.turret.{FacilityTurret, WeaponTurret}
|
||||
import net.psforever.objects.serverobject.zipline.ZipLinePath
|
||||
|
|
@ -57,6 +58,7 @@ import net.psforever.objects.vital.interaction.DamageInteraction
|
|||
import net.psforever.objects.vital.projectile.ProjectileReason
|
||||
import net.psforever.objects.zones.{Zone, ZoneHotSpotProjector, Zoning}
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
|
||||
import net.psforever.packet.game.{HotSpotInfo => PacketHotSpotInfo, _}
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.services.ServiceManager.LookupResult
|
||||
|
|
@ -64,7 +66,7 @@ import net.psforever.services.account.{AccountPersistenceService, PlayerToken, R
|
|||
import net.psforever.services.avatar.{AvatarAction, AvatarResponse, AvatarServiceMessage, AvatarServiceResponse}
|
||||
import net.psforever.services.chat.ChatService
|
||||
import net.psforever.services.galaxy.{GalaxyAction, GalaxyResponse, GalaxyServiceMessage, GalaxyServiceResponse}
|
||||
import net.psforever.services.local.support.RouterTelepadActivation
|
||||
import net.psforever.services.local.support.{HackCaptureActor, RouterTelepadActivation}
|
||||
import net.psforever.services.local.{LocalAction, LocalResponse, LocalServiceMessage, LocalServiceResponse}
|
||||
import net.psforever.services.properties.PropertyOverrideManager
|
||||
import net.psforever.services.support.SupportActor
|
||||
|
|
@ -2296,7 +2298,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
DeconstructDeployable(obj, guid, pos, obj.Orientation, 2)
|
||||
}
|
||||
|
||||
case LocalResponse.HackClear(target_guid, unk1, unk2) =>
|
||||
case LocalResponse.SendHackMessageHackCleared(target_guid, unk1, unk2) =>
|
||||
log.trace(s"Clearing hack for ${target_guid}")
|
||||
// Reset hack state for all players
|
||||
sendResponse(HackMessage(0, target_guid, guid, 0, unk1, HackState.HackCleared, unk2))
|
||||
|
|
@ -2304,8 +2306,8 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
case LocalResponse.HackObject(target_guid, unk1, unk2) =>
|
||||
HackObject(target_guid, unk1, unk2)
|
||||
|
||||
case LocalResponse.HackCaptureTerminal(target_guid, unk1, unk2, isResecured) =>
|
||||
HackCaptureTerminal(target_guid, unk1, unk2, isResecured)
|
||||
case LocalResponse.SendPlanetsideAttributeMessage(target_guid, attribute_number, attribute_value) =>
|
||||
SendPlanetsideAttributeMessage(target_guid, attribute_number, attribute_value)
|
||||
|
||||
case LocalResponse.ObjectDelete(object_guid, unk) =>
|
||||
if (tplayer_guid != guid) {
|
||||
|
|
@ -2657,6 +2659,7 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
case VehicleResponse.KickPassenger(seat_num, wasKickedByDriver, vehicle_guid) =>
|
||||
// seat_num seems to be correct if passenger is kicked manually by driver, but always seems to return 4 if user is kicked by seat permissions
|
||||
sendResponse(DismountVehicleMsg(guid, BailType.Kicked, wasKickedByDriver))
|
||||
player.VehicleSeated = None
|
||||
if (tplayer_guid == guid) {
|
||||
continent.GUID(vehicle_guid) match {
|
||||
case Some(obj: Vehicle) =>
|
||||
|
|
@ -6698,7 +6701,10 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
case obj: Hackable if obj.HackedBy.nonEmpty =>
|
||||
amenity.Definition match {
|
||||
case GlobalDefinitions.capture_terminal =>
|
||||
HackCaptureTerminal(amenity.GUID, 0L, 0L, false)
|
||||
SendPlanetsideAttributeMessage(
|
||||
amenity.GUID,
|
||||
PlanetsideAttributeEnum.ControlConsoleHackUpdate,
|
||||
HackCaptureActor.GetHackUpdateAttributeValue(amenity.asInstanceOf[CaptureTerminal], isResecured = false))
|
||||
case _ =>
|
||||
HackObject(amenity.GUID, 1114636288L, 8L) //generic hackable object
|
||||
}
|
||||
|
|
@ -6742,51 +6748,13 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
}
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param target_guid na
|
||||
* @param unk1 na
|
||||
* @param unk2 na
|
||||
* @param isResecured na
|
||||
* Send a PlanetsideAttributeMessage packet to the client
|
||||
* @param target_guid The target of the attribute
|
||||
* @param attribute_number The attribute number
|
||||
* @param attribute_value The attribute value
|
||||
*/
|
||||
def HackCaptureTerminal(target_guid: PlanetSideGUID, unk1: Long, unk2: Long, isResecured: Boolean): Unit = {
|
||||
var value = 0L
|
||||
if (isResecured) {
|
||||
value = 17039360L
|
||||
sendResponse(PlanetsideAttributeMessage(target_guid, 20, value))
|
||||
} else {
|
||||
continent.GUID(target_guid) match {
|
||||
case Some(capture_terminal: Amenity with Hackable) =>
|
||||
capture_terminal.HackedBy match {
|
||||
case Some(Hackable.HackInfo(_, _, hfaction, _, start, length)) =>
|
||||
val hack_time_remaining_ms =
|
||||
TimeUnit.MILLISECONDS.convert(math.max(0, start + length - System.nanoTime), TimeUnit.NANOSECONDS)
|
||||
val deciseconds_remaining = (hack_time_remaining_ms / 100)
|
||||
//See PlanetSideAttributeMessage #20 documentation for an explanation of how the timer is calculated
|
||||
val start_num = hfaction match {
|
||||
case PlanetSideEmpire.TR => 65536L
|
||||
case PlanetSideEmpire.NC => 131072L
|
||||
case PlanetSideEmpire.VS => 196608L
|
||||
}
|
||||
value = start_num + deciseconds_remaining
|
||||
sendResponse(PlanetsideAttributeMessage(target_guid, 20, value))
|
||||
GetMountableAndSeat(None, player, continent) match {
|
||||
case (Some(mountable: Amenity), Some(seat)) if mountable.Owner.GUID == capture_terminal.Owner.GUID =>
|
||||
mountable.Seats(seat).Occupant = None
|
||||
player.VehicleSeated = None
|
||||
continent.VehicleEvents ! VehicleServiceMessage(
|
||||
continent.id,
|
||||
VehicleAction.KickPassenger(player.GUID, seat, true, mountable.GUID)
|
||||
)
|
||||
case _ => ;
|
||||
}
|
||||
case _ => log.warn("HackCaptureTerminal: hack state monitor not defined")
|
||||
}
|
||||
case _ =>
|
||||
log.warn(
|
||||
s"HackCaptureTerminal: couldn't find capture terminal with GUID ${target_guid} in zone ${continent.id}"
|
||||
)
|
||||
}
|
||||
}
|
||||
def SendPlanetsideAttributeMessage(target_guid: PlanetSideGUID, attribute_number: PlanetsideAttributeEnum, attribute_value: Long): Unit = {
|
||||
sendResponse(PlanetsideAttributeMessage(target_guid, attribute_number, attribute_value))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package net.psforever.actors.zone
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor.typed.receptionist.Receptionist
|
||||
import akka.actor.typed.scaladsl.{ActorContext, Behaviors, StashBuffer}
|
||||
import akka.actor.typed.{ActorRef, Behavior, SupervisorStrategy}
|
||||
|
|
@ -8,11 +9,14 @@ import net.psforever.actors.commands.NtuCommand
|
|||
import net.psforever.objects.NtuContainer
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl}
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.structures.{Amenity, Building, StructureType, WarpGate}
|
||||
import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAware, CaptureTerminalAwareBehavior}
|
||||
import net.psforever.objects.serverobject.turret.{FacilityTurret, FacilityTurretControl}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.persistence
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGeneratorState}
|
||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, PlanetSideGeneratorState}
|
||||
import net.psforever.util.Database._
|
||||
import net.psforever.services.galaxy.{GalaxyAction, GalaxyServiceMessage}
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
|
|
@ -201,6 +205,25 @@ class BuildingActor(
|
|||
}
|
||||
Behaviors.same
|
||||
|
||||
case AmenityStateChange(terminal: CaptureTerminal, data) =>
|
||||
// Notify amenities that listen for CC hack state changes, e.g. wall turrets to dismount seated players
|
||||
building.Amenities.filter(x => x.isInstanceOf[CaptureTerminalAware]).foreach(amenity => {
|
||||
data match {
|
||||
case Some(isResecured: Boolean) => amenity.Actor ! CaptureTerminalAwareBehavior.TerminalStatusChanged(terminal, isResecured)
|
||||
case _ => log.warn("CaptureTerminal AmenityStateChange was received with no attached data.")
|
||||
}
|
||||
})
|
||||
|
||||
// When a CC is hacked (or resecured) all currently hacked amenities for the base should return to their default unhacked state
|
||||
building.HackableAmenities.foreach(amenity => {
|
||||
if (amenity.HackedBy.isDefined) {
|
||||
zone.LocalEvents ! LocalServiceMessage(amenity.Zone.id,LocalAction.ClearTemporaryHack(PlanetSideGUID(0), amenity))
|
||||
}
|
||||
})
|
||||
|
||||
// No map update needed - will be sent by `HackCaptureActor` when required
|
||||
Behaviors.same
|
||||
|
||||
case AmenityStateChange(_, _) =>
|
||||
//TODO when parameter object is finally immutable, perform analysis on it to determine specific actions
|
||||
//for now, just update the map
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import net.psforever.objects.inventory.InventoryTile
|
|||
import net.psforever.objects.serverobject.aura.Aura
|
||||
import net.psforever.objects.serverobject.doors.DoorDefinition
|
||||
import net.psforever.objects.serverobject.generator.GeneratorDefinition
|
||||
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMechDefinition
|
||||
import net.psforever.objects.serverobject.locks.IFFLockDefinition
|
||||
import net.psforever.objects.serverobject.mblocker.LockerDefinition
|
||||
import net.psforever.objects.serverobject.pad.VehicleSpawnPadDefinition
|
||||
|
|
@ -21,6 +20,8 @@ import net.psforever.objects.serverobject.terminals._
|
|||
import net.psforever.objects.serverobject.tube.SpawnTubeDefinition
|
||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition
|
||||
import net.psforever.objects.serverobject.structures.{AutoRepairStats, BuildingDefinition, WarpGateDefinition}
|
||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalDefinition
|
||||
import net.psforever.objects.serverobject.terminals.implant.{ImplantTerminalDefinition, ImplantTerminalMechDefinition}
|
||||
import net.psforever.objects.serverobject.turret.{FacilityTurretDefinition, TurretUpgrade}
|
||||
import net.psforever.objects.vehicles.{DestroyedVehicle, InternalTelepadDefinition, SeatArmorRestriction, UtilityType}
|
||||
import net.psforever.objects.vital.base.DamageType
|
||||
|
|
|
|||
|
|
@ -521,7 +521,7 @@ class Player(var avatar: Avatar)
|
|||
} else {
|
||||
""
|
||||
}
|
||||
s"${avatar.name}$guid ${Health}/${MaxHealth} ${Armor}/${MaxArmor}"
|
||||
s"${avatar.name}$guid ${avatar.faction} H: ${Health}/${MaxHealth} A: ${Armor}/${MaxArmor}"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class PainboxControl(painbox: Painbox) extends PoweredAmenityControl {
|
|||
if (painbox.Owner.Continent.matches("c[0-9]")) {
|
||||
//are we in a safe zone?
|
||||
// todo: handle non-radius painboxes in caverns properly
|
||||
log.warn(s"Skipping initialization of ${painbox.GUID} on ${painbox.Owner.Continent} - ${painbox.Position}")
|
||||
log.info(s"Skipping initialization of ${painbox.GUID} on ${painbox.Owner.Continent} - ${painbox.Position}")
|
||||
disabled = true
|
||||
} else {
|
||||
if (painbox.Definition.HasNearestDoorDependency) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
package net.psforever.objects.serverobject.structures
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.actors.zone.BuildingActor
|
||||
import net.psforever.objects.{GlobalDefinitions, NtuContainer, Player}
|
||||
|
|
@ -11,13 +10,13 @@ import net.psforever.objects.serverobject.generator.Generator
|
|||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.painbox.Painbox
|
||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
||||
import net.psforever.objects.serverobject.terminals.CaptureTerminal
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.BuildingInfoUpdateMessage
|
||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, PlanetSideGeneratorState, Vector3}
|
||||
import scalax.collection.{Graph, GraphEdge}
|
||||
import akka.actor.typed.scaladsl.adapter._
|
||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
|
||||
|
||||
class Building(
|
||||
private val name: String,
|
||||
|
|
@ -123,6 +122,10 @@ class Building(
|
|||
}
|
||||
}
|
||||
|
||||
def HackableAmenities: List[Amenity with Hackable] = {
|
||||
Amenities.filter(x => x.isInstanceOf[Hackable]).map(x => x.asInstanceOf[Amenity with Hackable])
|
||||
}
|
||||
|
||||
def CaptureTerminalIsHacked: Boolean = {
|
||||
CaptureTerminal match {
|
||||
case Some(obj: CaptureTerminal) =>
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright (c) 2020 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
object CaptureTerminals {
|
||||
private val log = org.log4s.getLogger("CaptureTerminals")
|
||||
|
||||
/**
|
||||
* The process of hacking an object is completed.
|
||||
* Pass the message onto the hackable object and onto the local events system.
|
||||
* @param target the `Hackable` object that has been hacked
|
||||
* @param unk na;
|
||||
* used by `HackMessage` as `unk5`
|
||||
* @see `HackMessage`
|
||||
*/
|
||||
//TODO add params here depending on which params in HackMessage are important
|
||||
def FinishHackingCaptureConsole(target: CaptureTerminal, user: Player, unk: Long)(): Unit = {
|
||||
import akka.pattern.ask
|
||||
import scala.concurrent.duration._
|
||||
log.info(s"Hacked a $target")
|
||||
// Wait for the target actor to set the HackedBy property, otherwise LocalAction.HackTemporarily will not complete properly
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
val tplayer = user
|
||||
ask(target.Actor, CommonMessages.Hack(tplayer, target))(1 second).mapTo[Boolean].onComplete {
|
||||
case Success(_) =>
|
||||
val zone = target.Zone
|
||||
val zoneId = zone.id
|
||||
val pguid = tplayer.GUID
|
||||
zone.LocalEvents ! LocalServiceMessage(
|
||||
zoneId,
|
||||
LocalAction.TriggerSound(pguid, target.HackSound, tplayer.Position, 30, 0.49803925f)
|
||||
)
|
||||
zone.LocalEvents ! LocalServiceMessage(
|
||||
zoneId,
|
||||
LocalAction.HackCaptureTerminal(pguid, zone, target, unk, 8L, tplayer.Faction == target.Faction)
|
||||
)
|
||||
case Failure(_) => log.warn(s"Hack message failed on target guid: ${target.GUID}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package net.psforever.objects.serverobject.terminals
|
||||
package net.psforever.objects.serverobject.terminals.capture
|
||||
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.structures.Amenity
|
||||
|
|
@ -9,6 +9,15 @@ class CaptureTerminal(private val idef: CaptureTerminalDefinition) extends Ameni
|
|||
def Definition: CaptureTerminalDefinition = idef
|
||||
HackDuration = Array(60, 40, 20, 15)
|
||||
HackSound = TriggeredSound.HackTerminal
|
||||
|
||||
override def toString: String = {
|
||||
val guid = if (HasGUID) {
|
||||
s" ${Continent}-${GUID.guid}"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
s"${this.getClass.getName}: $guid ($Faction)"
|
||||
}
|
||||
}
|
||||
|
||||
object CaptureTerminal {
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package net.psforever.objects.serverobject.terminals.capture
|
||||
|
||||
/**
|
||||
* A trait to mark an Amenity as being aware that a capture terminal exists on the parent object
|
||||
* and that it is interested in status updates about that capture terminal, for example mountables to kick players out once the CC is hacked
|
||||
* @see CaptureTerminalAwareBehavior
|
||||
*/
|
||||
trait CaptureTerminalAware {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package net.psforever.objects.serverobject.terminals.capture
|
||||
|
||||
import akka.actor.Actor.Receive
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.structures.Amenity
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
/**
|
||||
* The behaviours corresponding to an Amenity that is marked as being CaptureTerminalAware
|
||||
* @see CaptureTerminalAware
|
||||
*/
|
||||
trait CaptureTerminalAwareBehavior {
|
||||
def CaptureTerminalAwareObject : Amenity with CaptureTerminalAware
|
||||
|
||||
val captureTerminalAwareBehaviour: Receive = {
|
||||
case CaptureTerminalAwareBehavior.TerminalStatusChanged(terminal, isResecured) =>
|
||||
isResecured match {
|
||||
case true => ; // CC is resecured
|
||||
case false => // CC is hacked
|
||||
// Remove seated occupants for mountables
|
||||
if (CaptureTerminalAwareObject.isInstanceOf[Mountable]) {
|
||||
CaptureTerminalAwareObject.asInstanceOf[Mountable].Seats.filter(x => x._2.isOccupied).foreach(x => {
|
||||
val (seat_num, seat) = x
|
||||
CaptureTerminalAwareObject.Zone.VehicleEvents ! VehicleServiceMessage(
|
||||
CaptureTerminalAwareObject.Zone.id,
|
||||
VehicleAction.KickPassenger(seat.Occupant.get.GUID, seat_num, true, CaptureTerminalAwareObject.GUID))
|
||||
|
||||
seat.Occupant = None
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object CaptureTerminalAwareBehavior {
|
||||
final case class TerminalStatusChanged(terminal: CaptureTerminal, isResecured: Boolean)
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
package net.psforever.objects.serverobject.terminals.capture
|
||||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.objects.{GlobalDefinitions, SimpleItem}
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
|
||||
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
|
||||
import net.psforever.objects.{GlobalDefinitions, SimpleItem}
|
||||
|
||||
class CaptureTerminalControl(terminal: CaptureTerminal)
|
||||
extends Actor
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package net.psforever.objects.serverobject.terminals
|
||||
package net.psforever.objects.serverobject.terminals.capture
|
||||
|
||||
import net.psforever.objects.serverobject.structures.AmenityDefinition
|
||||
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package net.psforever.objects.serverobject.terminals.capture
|
||||
|
||||
import net.psforever.actors.zone.BuildingActor
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.packet.game.PlanetsideAttributeEnum
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
object CaptureTerminals {
|
||||
private val log = org.log4s.getLogger("CaptureTerminals")
|
||||
|
||||
/**
|
||||
* The process of hacking an object is completed.
|
||||
* Pass the message onto the hackable object and onto the local events system.
|
||||
* @param target the `Hackable` object that has been hacked
|
||||
* @param hackingPlayer The player that hacked the control console
|
||||
* @param unk na;
|
||||
* used by `HackMessage` as `unk5`
|
||||
* @see `HackMessage`
|
||||
*/
|
||||
//TODO add params here depending on which params in HackMessage are important
|
||||
def FinishHackingCaptureConsole(target: CaptureTerminal, hackingPlayer: Player, unk: Long)(): Unit = {
|
||||
import akka.pattern.ask
|
||||
|
||||
import scala.concurrent.duration._
|
||||
log.info(s"${hackingPlayer.toString} Hacked a ${target.toString}")
|
||||
// Wait for the target actor to set the HackedBy property
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
ask(target.Actor, CommonMessages.Hack(hackingPlayer, target))(1 second).mapTo[Boolean].onComplete {
|
||||
case Success(_) =>
|
||||
target.Zone.LocalEvents ! LocalServiceMessage(
|
||||
target.Zone.id,
|
||||
LocalAction.TriggerSound(hackingPlayer.GUID, target.HackSound, hackingPlayer.Position, 30, 0.49803925f)
|
||||
)
|
||||
|
||||
val isResecured = hackingPlayer.Faction == target.Faction
|
||||
|
||||
if (isResecured) {
|
||||
// Resecure the CC
|
||||
target.Zone.LocalEvents ! LocalServiceMessage(
|
||||
target.Zone.id,
|
||||
LocalAction.ResecureCaptureTerminal(
|
||||
target
|
||||
)
|
||||
)
|
||||
} else {
|
||||
// Start the CC hack timer
|
||||
target.Zone.LocalEvents ! LocalServiceMessage(
|
||||
target.Zone.id,
|
||||
LocalAction.StartCaptureTerminalHack(
|
||||
target
|
||||
)
|
||||
)
|
||||
}
|
||||
case Failure(_) => log.warn(s"Hack message failed on target guid: ${target.GUID}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
package net.psforever.objects.serverobject.terminals.implant
|
||||
|
||||
import net.psforever.objects.GlobalDefinitions
|
||||
import net.psforever.objects.definition.ImplantDefinition
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.implantmech
|
||||
package net.psforever.objects.serverobject.terminals.implant
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.serverobject.hackable.Hackable
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.structures.Amenity
|
||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAware
|
||||
import net.psforever.objects.vehicles.Seat
|
||||
import net.psforever.packet.game.TriggeredSound
|
||||
import net.psforever.types.Vector3
|
||||
|
|
@ -17,7 +18,8 @@ import net.psforever.types.Vector3
|
|||
class ImplantTerminalMech(private val idef: ImplantTerminalMechDefinition)
|
||||
extends Amenity
|
||||
with Mountable
|
||||
with Hackable {
|
||||
with Hackable
|
||||
with CaptureTerminalAware {
|
||||
private val seats: Map[Int, Seat] = Map(0 -> new Seat(idef.Seats(0)))
|
||||
|
||||
HackSound = TriggeredSound.HackTerminal
|
||||
|
|
@ -1,16 +1,17 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.implantmech
|
||||
package net.psforever.objects.serverobject.terminals.implant
|
||||
|
||||
import net.psforever.objects.{GlobalDefinitions, Player, SimpleItem}
|
||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
|
||||
import net.psforever.objects.serverobject.damage.Damageable.Target
|
||||
import net.psforever.objects.serverobject.damage.{Damageable, DamageableEntity, DamageableMountable}
|
||||
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
|
||||
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
|
||||
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableEntity}
|
||||
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
|
||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAwareBehavior
|
||||
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
|
||||
import net.psforever.objects.vital.interaction.DamageResult
|
||||
import net.psforever.objects.{GlobalDefinitions, Player, SimpleItem}
|
||||
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
|
||||
|
||||
/**
|
||||
|
|
@ -25,13 +26,15 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
|
|||
with HackableBehavior.GenericHackable
|
||||
with DamageableEntity
|
||||
with RepairableEntity
|
||||
with AmenityAutoRepair {
|
||||
with AmenityAutoRepair
|
||||
with CaptureTerminalAwareBehavior {
|
||||
def MountableObject = mech
|
||||
def HackableObject = mech
|
||||
def FactionObject = mech
|
||||
def DamageableObject = mech
|
||||
def RepairableObject = mech
|
||||
def AutoRepairObject = mech
|
||||
def CaptureTerminalAwareObject = mech
|
||||
|
||||
def commonBehavior: Receive =
|
||||
checkBehavior
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.implantmech
|
||||
package net.psforever.objects.serverobject.terminals.implant
|
||||
|
||||
import net.psforever.objects.definition.SeatDefinition
|
||||
import net.psforever.objects.serverobject.structures.AmenityDefinition
|
||||
|
|
@ -3,9 +3,14 @@ package net.psforever.objects.serverobject.turret
|
|||
|
||||
import net.psforever.objects.equipment.JammableUnit
|
||||
import net.psforever.objects.serverobject.structures.Amenity
|
||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAware
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
class FacilityTurret(tDef: FacilityTurretDefinition) extends Amenity with WeaponTurret with JammableUnit {
|
||||
class FacilityTurret(tDef: FacilityTurretDefinition)
|
||||
extends Amenity
|
||||
with WeaponTurret
|
||||
with JammableUnit
|
||||
with CaptureTerminalAware {
|
||||
WeaponTurret.LoadDefinition(this)
|
||||
|
||||
def MountPoints: Map[Int, Int] = Definition.MountPoints.toMap
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import net.psforever.objects.serverobject.damage.{Damageable, DamageableWeaponTu
|
|||
import net.psforever.objects.serverobject.hackable.GenericHackables
|
||||
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableWeaponTurret}
|
||||
import net.psforever.objects.serverobject.structures.PoweredAmenityControl
|
||||
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAwareBehavior
|
||||
import net.psforever.objects.vital.interaction.DamageResult
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
|
|
@ -35,13 +36,15 @@ class FacilityTurretControl(turret: FacilityTurret)
|
|||
with DamageableWeaponTurret
|
||||
with RepairableWeaponTurret
|
||||
with AmenityAutoRepair
|
||||
with JammableMountedWeapons {
|
||||
with JammableMountedWeapons
|
||||
with CaptureTerminalAwareBehavior {
|
||||
def FactionObject = turret
|
||||
def MountableObject = turret
|
||||
def JammableObject = turret
|
||||
def DamageableObject = turret
|
||||
def RepairableObject = turret
|
||||
def AutoRepairObject = turret
|
||||
def CaptureTerminalAwareObject = turret
|
||||
|
||||
// Used for timing ammo recharge for vanu turrets in caves
|
||||
var weaponAmmoRechargeTimer = Default.Cancellable
|
||||
|
|
@ -59,6 +62,7 @@ class FacilityTurretControl(turret: FacilityTurret)
|
|||
.orElse(takesDamage)
|
||||
.orElse(canBeRepairedByNanoDispenser)
|
||||
.orElse(autoRepairBehavior)
|
||||
.orElse(captureTerminalAwareBehaviour)
|
||||
|
||||
def poweredStateLogic: Receive =
|
||||
commonBehavior
|
||||
|
|
@ -113,7 +117,6 @@ class FacilityTurretControl(turret: FacilityTurret)
|
|||
weaponAmmoRechargeTimer.cancel()
|
||||
weaponAmmoRechargeTimer = Default.Cancellable
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import net.psforever.actors.zone.ZoneActor
|
|||
import net.psforever.objects.avatar.Avatar
|
||||
import net.psforever.objects.geometry.Geometry3D
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.objects.vehicles.UtilityType
|
||||
import net.psforever.objects.vital.etc.{EmpReason, ExplodingEntityReason}
|
||||
|
|
@ -257,7 +258,7 @@ class Zone(val id: String, val map: ZoneMap, zoneNumber: Int) {
|
|||
case (mechGuid, interfaceGuid) =>
|
||||
validateObject(
|
||||
mechGuid,
|
||||
(x: PlanetSideGameObject) => x.isInstanceOf[serverobject.implantmech.ImplantTerminalMech],
|
||||
(x: PlanetSideGameObject) => x.isInstanceOf[ImplantTerminalMech],
|
||||
"implant terminal mech"
|
||||
)
|
||||
validateObject(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2016 PSForever.net to present
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||
import net.psforever.types.PlanetSideGUID
|
||||
import scodec.Codec
|
||||
|
|
@ -71,11 +72,11 @@ import scodec.codecs._
|
|||
* Format is: Time left - 2 bytes, faction - 1 byte (1-4), isResecured - 1 byte (0-1)`<br>
|
||||
* <ul>
|
||||
* <li>65535 segments per faction in deciseconds (seconds * 10)</li>
|
||||
* <li>0-65535 = Neutral 0 seconds to 1h 49m 14s - 0x 00 00 00 00 to 0x FF FF 00 00</li>
|
||||
* <li>65536 - 131071 - TR - 0x 00 00 01 00</li>
|
||||
* <li>131072 - 196607 - NC - 0x 00 00 02 00</li>
|
||||
* <li>196608 - 262143 - VS - 0x 00 00 03 00</li>
|
||||
* <li>17039360 - CC Resecured - 0x 00 00 04 01</li>
|
||||
* <li>0-65535 = Neutral 0 seconds to 1h 49m 14s - 0x0000 to 0xFFFF</li>
|
||||
* <li>65536 (0x10000) - 131071 (0x1FFFF) - TR</li>
|
||||
* <li>131072 (0x20000) - 196607 (0x2FFFF) - NC</li>
|
||||
* <li>196608 (0x30000) - 262143 (0x3FFFF) - VS</li>
|
||||
* <li>17039360 (0x1040000) - CC Resecured</li>
|
||||
* </ul>
|
||||
* `24 - Learn certification:`<br>
|
||||
* <ul>
|
||||
|
|
@ -215,9 +216,23 @@ object PlanetsideAttributeMessage extends Marshallable[PlanetsideAttributeMessag
|
|||
PlanetsideAttributeMessage(guid, attribute_type, attribute_value.guid)
|
||||
}
|
||||
|
||||
def apply(guid: PlanetSideGUID, attribute_type: PlanetsideAttributeEnum, attribute_value: Int): PlanetsideAttributeMessage = {
|
||||
PlanetsideAttributeMessage(guid, attribute_type.id, attribute_value.toLong)
|
||||
}
|
||||
|
||||
def apply(guid: PlanetSideGUID, attribute_type: PlanetsideAttributeEnum, attribute_value: Long): PlanetsideAttributeMessage = {
|
||||
PlanetsideAttributeMessage(guid, attribute_type.id, attribute_value)
|
||||
}
|
||||
|
||||
implicit val codec: Codec[PlanetsideAttributeMessage] = (
|
||||
("guid" | PlanetSideGUID.codec) ::
|
||||
("attribute_type" | uint8L) ::
|
||||
("attribute_value" | uint32L)
|
||||
).as[PlanetsideAttributeMessage]
|
||||
}
|
||||
|
||||
object PlanetsideAttributeEnum extends Enumeration {
|
||||
type PlanetsideAttributeEnum = Value
|
||||
|
||||
val ControlConsoleHackUpdate = Value(20)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package net.psforever.persistence
|
||||
|
||||
import net.psforever.objects.definition.ImplantDefinition
|
||||
import net.psforever.objects.serverobject.terminals.ImplantTerminalDefinition
|
||||
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalDefinition
|
||||
|
||||
case class Implant(
|
||||
name: String,
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@
|
|||
package net.psforever.services.local
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Props}
|
||||
import akka.pattern.Patterns
|
||||
import akka.util.Timeout
|
||||
import net.psforever.actors.zone.{BuildingActor, ZoneActor}
|
||||
import net.psforever.objects.ce.Deployable
|
||||
import net.psforever.objects.serverobject.structures.{Amenity, Building}
|
||||
import net.psforever.objects.serverobject.terminals.{CaptureTerminal, Terminal}
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.objects._
|
||||
import net.psforever.packet.game.{TriggeredEffect, TriggeredEffectLocation}
|
||||
import net.psforever.packet.game.{PlanetsideAttributeEnum, TriggeredEffect, TriggeredEffectLocation}
|
||||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.types.{PlanetSideGUID, Vector3}
|
||||
import net.psforever.services.local.support._
|
||||
|
|
@ -20,6 +22,8 @@ import net.psforever.objects.serverobject.hackable.Hackable
|
|||
import net.psforever.objects.vehicles.{Utility, UtilityType}
|
||||
import net.psforever.services.support.SupportActor
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
class LocalService(zone: Zone) extends Actor {
|
||||
|
|
@ -89,7 +93,7 @@ class LocalService(zone: Zone) extends Actor {
|
|||
)
|
||||
case LocalAction.HackClear(player_guid, target, unk1, unk2) =>
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackClear(target.GUID, unk1, unk2))
|
||||
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.SendHackMessageHackCleared(target.GUID, unk1, unk2))
|
||||
)
|
||||
case LocalAction.HackTemporarily(player_guid, _, target, unk1, duration, unk2) =>
|
||||
hackClearer ! HackClearActor.ObjectIsHacked(target, zone, unk1, unk2, duration)
|
||||
|
|
@ -98,43 +102,18 @@ class LocalService(zone: Zone) extends Actor {
|
|||
)
|
||||
case LocalAction.ClearTemporaryHack(_, target) =>
|
||||
hackClearer ! HackClearActor.ObjectIsResecured(target)
|
||||
case LocalAction.HackCaptureTerminal(player_guid, _, target, unk1, unk2, isResecured) =>
|
||||
// When a CC is hacked (or resecured) all amenities for the base should be unhacked
|
||||
val building = target.Owner.asInstanceOf[Building]
|
||||
val hackableAmenities =
|
||||
building.Amenities.filter(x => x.isInstanceOf[Hackable]).map(x => x.asInstanceOf[Amenity with Hackable])
|
||||
hackableAmenities.foreach(amenity =>
|
||||
if (amenity.HackedBy.isDefined) { hackClearer ! HackClearActor.ObjectIsResecured(amenity) }
|
||||
)
|
||||
|
||||
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)
|
||||
case GlobalDefinitions.vanu_control_console =>
|
||||
hackCapturer ! HackCaptureActor.ObjectIsHacked(target, zone, unk1, unk2, duration = 10 minutes)
|
||||
}
|
||||
}
|
||||
|
||||
case LocalAction.ResecureCaptureTerminal(target) =>
|
||||
hackCapturer ! HackCaptureActor.ResecureCaptureTerminal(target, zone)
|
||||
case LocalAction.StartCaptureTerminalHack(target) =>
|
||||
hackCapturer ! HackCaptureActor.StartCaptureTerminalHack(target, zone, 0, 8L)
|
||||
case LocalAction.SendPlanetsideAttributeMessage(player_guid, target_guid, attribute_number, attribute_value) =>
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(
|
||||
s"/$forChannel/Local",
|
||||
player_guid,
|
||||
LocalResponse.HackCaptureTerminal(target.GUID, unk1, unk2, isResecured)
|
||||
LocalResponse.SendPlanetsideAttributeMessage(target_guid, attribute_number, attribute_value)
|
||||
)
|
||||
)
|
||||
|
||||
// If the owner of this capture terminal is on the lattice trigger a zone wide map update to update lattice benefits
|
||||
zone.Lattice find building match {
|
||||
case Some(_) => building.Zone.actor ! ZoneActor.ZoneMapUpdate()
|
||||
case None => ;
|
||||
}
|
||||
case LocalAction.RouterTelepadTransport(player_guid, passenger_guid, src_guid, dest_guid) =>
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(
|
||||
|
|
@ -214,13 +193,13 @@ class LocalService(zone: Zone) extends Actor {
|
|||
)
|
||||
|
||||
//response from HackClearActor
|
||||
case HackClearActor.ClearTheHack(target_guid, _, unk1, unk2) =>
|
||||
case HackClearActor.SendHackMessageHackCleared(target_guid, _, unk1, unk2) =>
|
||||
log.info(s"Clearing hack for $target_guid")
|
||||
LocalEvents.publish(
|
||||
LocalServiceResponse(
|
||||
s"/${zone.id}/Local",
|
||||
Service.defaultPlayerGUID,
|
||||
LocalResponse.HackClear(target_guid, unk1, unk2)
|
||||
LocalResponse.SendHackMessageHackCleared(target_guid, unk1, unk2)
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -242,29 +221,6 @@ class LocalService(zone: Zone) extends Actor {
|
|||
)
|
||||
)
|
||||
|
||||
case HackCaptureActor.HackTimeoutReached(capture_terminal_guid, _, _, _, hackedByFaction) =>
|
||||
val terminal = zone.GUID(capture_terminal_guid).get.asInstanceOf[CaptureTerminal]
|
||||
val building = terminal.Owner.asInstanceOf[Building]
|
||||
|
||||
if (building.NtuLevel > 0) {
|
||||
log.info(s"Setting base ${building.GUID} / MapId: ${building.MapId} as owned by $hackedByFaction")
|
||||
building.Actor ! BuildingActor.SetFaction(hackedByFaction)
|
||||
} else {
|
||||
log.info("Base hack completed, but base was out of NTU.")
|
||||
}
|
||||
|
||||
// FIXME shitty workaround so we don't get a "resecured by owner" message
|
||||
// SetFaction must be processed before we can keep going
|
||||
Thread.sleep(1000)
|
||||
|
||||
// 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(building.GUID, zone.id, 3212836864L, 8L)
|
||||
|
||||
//message to Engineer
|
||||
case LocalServiceMessage.Deployables(msg) =>
|
||||
engineer forward msg
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ import net.psforever.objects.ce.Deployable
|
|||
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.serverobject.terminals.capture.CaptureTerminal
|
||||
import net.psforever.objects.vehicles.Utility
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
|
||||
import net.psforever.packet.game.{DeployableInfo, DeploymentAction, TriggeredSound}
|
||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||
|
||||
|
|
@ -45,13 +46,13 @@ object LocalAction {
|
|||
) extends Action
|
||||
final case class ClearTemporaryHack(player_guid: PlanetSideGUID, target: PlanetSideServerObject with Hackable)
|
||||
extends Action
|
||||
final case class HackCaptureTerminal(
|
||||
final case class ResecureCaptureTerminal(target: CaptureTerminal) extends Action
|
||||
final case class StartCaptureTerminalHack(target: CaptureTerminal) extends Action
|
||||
final case class SendPlanetsideAttributeMessage(
|
||||
player_guid: PlanetSideGUID,
|
||||
continent: Zone,
|
||||
target: CaptureTerminal,
|
||||
unk1: Long,
|
||||
unk2: Long = 8L,
|
||||
isResecured: Boolean
|
||||
target: PlanetSideGUID,
|
||||
attribute_number: PlanetsideAttributeEnum,
|
||||
attribute_value: Long
|
||||
) extends Action
|
||||
final case class RouterTelepadTransport(
|
||||
player_guid: PlanetSideGUID,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle}
|
|||
import net.psforever.objects.ce.Deployable
|
||||
import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal}
|
||||
import net.psforever.objects.vehicles.Utility
|
||||
import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||
import net.psforever.services.GenericEventBusMsg
|
||||
|
|
@ -28,9 +29,9 @@ object LocalResponse {
|
|||
object_guid: PlanetSideGUID,
|
||||
pos: Vector3
|
||||
) extends Response
|
||||
final case class HackClear(target_guid: PlanetSideGUID, unk1: Long, unk2: Long) extends Response
|
||||
final case class SendHackMessageHackCleared(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)
|
||||
final case class SendPlanetsideAttributeMessage(target_guid: PlanetSideGUID, attribute_number: PlanetsideAttributeEnum, attribute_value: Long)
|
||||
extends Response
|
||||
final case class ObjectDelete(item_guid: PlanetSideGUID, unk: Int) extends Response
|
||||
final case class ProximityTerminalAction(terminal: Terminal with ProximityUnit, target: PlanetSideGameObject)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
package net.psforever.services.local.support
|
||||
|
||||
import akka.actor.{Actor, Cancellable}
|
||||
import net.psforever.actors.zone.ZoneActor
|
||||
import net.psforever.objects.Default
|
||||
import net.psforever.actors.zone.{BuildingActor, ZoneActor}
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
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.serverobject.terminals.capture.CaptureTerminal
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.objects.{Default, GlobalDefinitions}
|
||||
import net.psforever.packet.game.PlanetsideAttributeEnum
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
import scala.concurrent.duration.{FiniteDuration, _}
|
||||
|
||||
class HackCaptureActor extends Actor {
|
||||
|
|
@ -21,8 +24,21 @@ class HackCaptureActor extends Actor {
|
|||
private var hackedObjects: List[HackCaptureActor.HackEntry] = Nil
|
||||
|
||||
def receive: Receive = {
|
||||
case HackCaptureActor.ObjectIsHacked(target, zone, unk1, unk2, duration, time) =>
|
||||
case HackCaptureActor.StartCaptureTerminalHack(target, zone, unk1, unk2, startTime) =>
|
||||
log.trace(s"${target.GUID} is hacked.")
|
||||
|
||||
val duration = target.Definition match {
|
||||
case GlobalDefinitions.capture_terminal =>
|
||||
// Base CC
|
||||
15 minutes
|
||||
case GlobalDefinitions.secondary_capture =>
|
||||
// Tower CC
|
||||
1 nanosecond
|
||||
case GlobalDefinitions.vanu_control_console =>
|
||||
// Cavern CC
|
||||
10 minutes
|
||||
}
|
||||
|
||||
target.HackedBy match {
|
||||
case Some(hackInfo) =>
|
||||
target.HackedBy = hackInfo.Duration(duration.toNanos)
|
||||
|
|
@ -36,56 +52,81 @@ class HackCaptureActor extends Actor {
|
|||
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)
|
||||
hackedObjects = hackedObjects :+ HackCaptureActor.HackEntry(target, zone, unk1, unk2, duration, startTime)
|
||||
|
||||
// Restart the timer, in case this is the first object in the hacked objects list or the object was removed and re-added
|
||||
RestartTimer()
|
||||
|
||||
if (target.isInstanceOf[CaptureTerminal]) {
|
||||
target.Owner.asInstanceOf[Building].Zone.actor ! ZoneActor.ZoneMapUpdate()
|
||||
}
|
||||
NotifyHackStateChange(target, isResecured = false)
|
||||
|
||||
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)
|
||||
val finishedHacks = hackedObjects.filter(x => now - x.hack_timestamp >= x.duration.toNanos)
|
||||
hackedObjects = stillHacked
|
||||
unhackObjects.foreach(entry => {
|
||||
finishedHacks.foreach(entry => {
|
||||
log.trace(s"Capture terminal hack timeout reached for terminal ${entry.target.GUID}")
|
||||
|
||||
val hackedByFaction = entry.target.HackedBy.get.hackerFaction
|
||||
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
|
||||
HackCompleted(entry.target, hackedByFaction)
|
||||
})
|
||||
|
||||
// If there's hacked objects left in the list restart the timer with the shortest hack time left
|
||||
RestartTimer()
|
||||
|
||||
case HackCaptureActor.ClearHack(target, _) =>
|
||||
case HackCaptureActor.ResecureCaptureTerminal(target, _) =>
|
||||
hackedObjects = hackedObjects.filterNot(x => x.target == target)
|
||||
|
||||
if (target.isInstanceOf[CaptureTerminal]) {
|
||||
target.Owner.asInstanceOf[Building].Zone.actor ! ZoneActor.ZoneMapUpdate()
|
||||
}
|
||||
NotifyHackStateChange(target, isResecured = true)
|
||||
|
||||
// Restart the timer in case the object we just removed was the next one scheduled
|
||||
RestartTimer()
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
private def NotifyHackStateChange(target: CaptureTerminal, isResecured: Boolean): Unit = {
|
||||
val attribute_value = HackCaptureActor.GetHackUpdateAttributeValue(target, isResecured)
|
||||
|
||||
// Notify all clients that CC has been hacked
|
||||
target.Zone.LocalEvents ! LocalServiceMessage(
|
||||
target.Zone.id,
|
||||
LocalAction.SendPlanetsideAttributeMessage(
|
||||
PlanetSideGUID(-1),
|
||||
target.GUID,
|
||||
PlanetsideAttributeEnum.ControlConsoleHackUpdate,
|
||||
attribute_value
|
||||
)
|
||||
)
|
||||
|
||||
// Notify parent building that state has changed
|
||||
target.Owner.Actor ! BuildingActor.AmenityStateChange(target, Some(isResecured))
|
||||
|
||||
// Push map update to clients
|
||||
target.Owner.asInstanceOf[Building].Zone.actor ! ZoneActor.ZoneMapUpdate()
|
||||
}
|
||||
|
||||
private def HackCompleted(terminal: CaptureTerminal with Hackable, hackedByFaction: PlanetSideEmpire.Value): Unit = {
|
||||
val building = terminal.Owner.asInstanceOf[Building]
|
||||
if (building.NtuLevel > 0) {
|
||||
log.info(s"Setting base ${building.GUID} / MapId: ${building.MapId} as owned by $hackedByFaction")
|
||||
building.Actor! BuildingActor.SetFaction(hackedByFaction)
|
||||
} else {
|
||||
log.info("Base hack completed, but base was out of NTU.")
|
||||
}
|
||||
|
||||
NotifyHackStateChange(terminal, 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.
|
||||
context.parent ! HackClearActor.SendHackMessageHackCleared(building.GUID, terminal.Zone.id, 3212836864L, 8L) //call up to the `LocalService`
|
||||
}
|
||||
|
||||
private def RestartTimer(): Unit = {
|
||||
if (hackedObjects.nonEmpty) {
|
||||
val now = System.nanoTime()
|
||||
|
|
@ -110,33 +151,49 @@ class HackCaptureActor extends Actor {
|
|||
}
|
||||
|
||||
object HackCaptureActor {
|
||||
final case class ObjectIsHacked(
|
||||
target: CaptureTerminal,
|
||||
zone: Zone,
|
||||
unk1: Long,
|
||||
unk2: Long,
|
||||
duration: FiniteDuration,
|
||||
time: Long = System.nanoTime()
|
||||
final case class StartCaptureTerminalHack(
|
||||
target: CaptureTerminal,
|
||||
zone: Zone,
|
||||
unk1: Long,
|
||||
unk2: Long,
|
||||
startTime: 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 ResecureCaptureTerminal(target: CaptureTerminal, zone: Zone)
|
||||
|
||||
private final case class ProcessCompleteHacks()
|
||||
|
||||
private final case class HackEntry(
|
||||
target: PlanetSideServerObject with Hackable,
|
||||
target: CaptureTerminal with Hackable,
|
||||
zone: Zone,
|
||||
unk1: Long,
|
||||
unk2: Long,
|
||||
duration: FiniteDuration,
|
||||
hack_timestamp: Long
|
||||
)
|
||||
|
||||
def GetHackUpdateAttributeValue(target: CaptureTerminal, isResecured: Boolean): Long = {
|
||||
if (isResecured) {
|
||||
17039360L
|
||||
} else {
|
||||
target.HackedBy match {
|
||||
case Some(Hackable.HackInfo(_, _, hackingFaction, _, start, length)) =>
|
||||
// See PlanetSideAttributeMessage #20 documentation for an explanation of how the timer is calculated
|
||||
val hack_time_remaining_ms =
|
||||
TimeUnit.MILLISECONDS.convert(math.max(0, start + length - System.nanoTime), TimeUnit.NANOSECONDS)
|
||||
|
||||
val start_num = hackingFaction match {
|
||||
case PlanetSideEmpire.TR => 0x10000
|
||||
case PlanetSideEmpire.NC => 0x20000
|
||||
case PlanetSideEmpire.VS => 0x30000
|
||||
}
|
||||
|
||||
start_num + (hack_time_remaining_ms / 100) // Add time remaining as deciseconds
|
||||
|
||||
case _ =>
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class HackClearActor() extends Actor {
|
|||
hackedObjects = stillHackedObjects
|
||||
unhackObjects.foreach(entry => {
|
||||
entry.target.Actor ! CommonMessages.ClearHack()
|
||||
context.parent ! HackClearActor.ClearTheHack(
|
||||
context.parent ! HackClearActor.SendHackMessageHackCleared(
|
||||
entry.target.GUID,
|
||||
entry.zone.id,
|
||||
entry.unk1,
|
||||
|
|
@ -59,7 +59,7 @@ class HackClearActor() extends Actor {
|
|||
case Some(entry: HackClearActor.HackEntry) =>
|
||||
hackedObjects = hackedObjects.filterNot(x => x.target == target)
|
||||
entry.target.Actor ! CommonMessages.ClearHack()
|
||||
context.parent ! HackClearActor.ClearTheHack(
|
||||
context.parent ! HackClearActor.SendHackMessageHackCleared(
|
||||
entry.target.GUID,
|
||||
entry.zone.id,
|
||||
entry.unk1,
|
||||
|
|
@ -173,7 +173,7 @@ object HackClearActor {
|
|||
* @param obj the server object
|
||||
* @param zone_id the zone in which the object resides
|
||||
*/
|
||||
final case class ClearTheHack(obj: PlanetSideGUID, zone_id: String, unk1: Long, unk2: Long)
|
||||
final case class SendHackMessageHackCleared(obj: PlanetSideGUID, zone_id: String, unk1: Long, unk2: Long)
|
||||
|
||||
/**
|
||||
* Internal message used to signal a test of the queued door information.
|
||||
|
|
|
|||
|
|
@ -1,18 +1,10 @@
|
|||
package net.psforever.zones
|
||||
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
import net.psforever.objects.serverobject.terminals.{
|
||||
CaptureTerminal,
|
||||
CaptureTerminalDefinition,
|
||||
ProximityTerminal,
|
||||
ProximityTerminalDefinition,
|
||||
Terminal,
|
||||
TerminalDefinition
|
||||
}
|
||||
import net.psforever.objects.serverobject.terminals.{ProximityTerminal, ProximityTerminalDefinition, Terminal, TerminalDefinition}
|
||||
import net.psforever.objects.serverobject.mblocker.Locker
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import akka.actor.ActorContext
|
||||
import io.circe._
|
||||
import io.circe.parser._
|
||||
|
|
@ -21,18 +13,13 @@ import net.psforever.objects.ballistics.Projectile
|
|||
import net.psforever.objects.definition.BasicDefinition
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
import net.psforever.objects.serverobject.generator.Generator
|
||||
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
|
||||
import net.psforever.objects.serverobject.locks.IFFLock
|
||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnPad, VehicleSpawnPadDefinition}
|
||||
import net.psforever.objects.serverobject.painbox.{Painbox, PainboxDefinition}
|
||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
||||
import net.psforever.objects.serverobject.structures.{
|
||||
Building,
|
||||
BuildingDefinition,
|
||||
FoundationBuilder,
|
||||
StructureType,
|
||||
WarpGate
|
||||
}
|
||||
import net.psforever.objects.serverobject.structures.{Building, BuildingDefinition, FoundationBuilder, StructureType, WarpGate}
|
||||
import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalDefinition}
|
||||
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
import net.psforever.objects.serverobject.turret.{FacilityTurret, FacilityTurretDefinition}
|
||||
import net.psforever.objects.serverobject.zipline.ZipLinePath
|
||||
|
|
@ -246,7 +233,7 @@ object Zones {
|
|||
val structureType =
|
||||
if (towerTypes.contains(structure.objectType) || structure.objectType == "redoubt")
|
||||
StructureType.Tower
|
||||
else if (facilityTypes.contains(structure.objectType))
|
||||
else if (facilityTypes.contains(structure.objectType) || cavernBuildingTypes.contains(structure.objectType))
|
||||
StructureType.Facility
|
||||
else if (bunkerTypes.contains(structure.objectType))
|
||||
StructureType.Bunker
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import net.psforever.objects.guid.NumberPoolHub
|
|||
import net.psforever.objects.guid.source.MaxNumberSource
|
||||
import net.psforever.objects.serverobject.damage.Damageable
|
||||
import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl}
|
||||
import net.psforever.objects.serverobject.implantmech.{ImplantTerminalMech, ImplantTerminalMechControl}
|
||||
import net.psforever.objects.serverobject.structures.{Building, StructureType}
|
||||
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl, TerminalDefinition}
|
||||
import net.psforever.objects.serverobject.tube.SpawnTube
|
||||
|
|
@ -30,6 +29,7 @@ import org.specs2.mutable.Specification
|
|||
|
||||
import scala.concurrent.duration._
|
||||
import net.psforever.objects.avatar.Avatar
|
||||
import net.psforever.objects.serverobject.terminals.implant.{ImplantTerminalMech, ImplantTerminalMechControl}
|
||||
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||
import net.psforever.objects.vital.base.DamageResolution
|
||||
import net.psforever.objects.vital.projectile.ProjectileReason
|
||||
|
|
|
|||
|
|
@ -83,7 +83,9 @@ class IFFLockObjectBuilderTest extends FreedContextActorTest {
|
|||
}
|
||||
|
||||
class ImplantTerminalMechObjectBuilderTest extends FreedContextActorTest {
|
||||
import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
|
||||
|
||||
import net.psforever.objects.serverobject.terminals.implant.ImplantTerminalMech
|
||||
|
||||
"Implant terminal mech object" should {
|
||||
"build" in {
|
||||
val hub = ServerObjectBuilderTest.NumberPoolHub
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ import net.psforever.objects.definition.SeatDefinition
|
|||
import net.psforever.objects.guid.NumberPoolHub
|
||||
import net.psforever.objects.guid.source.MaxNumberSource
|
||||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.serverobject.implantmech.{ImplantTerminalMech, ImplantTerminalMechControl}
|
||||
import net.psforever.objects.serverobject.structures.{Building, StructureType}
|
||||
import net.psforever.objects.serverobject.terminals.Terminal
|
||||
import net.psforever.objects.serverobject.terminals.implant.{ImplantTerminalMech, ImplantTerminalMechControl}
|
||||
import net.psforever.objects.vehicles.Seat
|
||||
import net.psforever.objects.zones.{Zone, ZoneMap}
|
||||
import net.psforever.types.{CharacterGender, CharacterVoice, PlanetSideEmpire, Vector3}
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ class HackClearTest extends ActorTest {
|
|||
service ! Service.Join("test")
|
||||
service ! LocalServiceMessage("test", LocalAction.HackClear(PlanetSideGUID(10), obj, 0L, 1000L))
|
||||
expectMsg(
|
||||
LocalServiceResponse("/test/Local", PlanetSideGUID(10), LocalResponse.HackClear(PlanetSideGUID(40), 0L, 1000L))
|
||||
LocalServiceResponse("/test/Local", PlanetSideGUID(10), LocalResponse.SendHackMessageHackCleared(PlanetSideGUID(40), 0L, 1000L))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue