Implant Terminal Hack (#1221)

* preliminary functional implant hacking; am going to explore better hacking policies

* HackMessage now uses custom fields and a float value rather than integers (2, 32, 32); separate hack state and hack clear states; everything changes
This commit is contained in:
Fate-JH 2024-08-03 00:12:16 -04:00 committed by GitHub
parent 8afe7fa248
commit 02f95a293e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 488 additions and 204 deletions

View file

@ -13,7 +13,7 @@ import net.psforever.objects.avatar.scoring.{Assist, Death, EquipmentStat, KDASt
import net.psforever.objects.sourcing.{TurretSource, VehicleSource}
import net.psforever.packet.game.ImplantAction
import net.psforever.services.avatar.AvatarServiceResponse
import net.psforever.types.{ChatMessageType, StatisticalCategory, StatisticalElement, Vector3}
import net.psforever.types.{ChatMessageType, StatisticalCategory, StatisticalElement}
import net.psforever.zones.Zones
import org.joda.time.{LocalDateTime, Seconds}

View file

@ -7,7 +7,7 @@ import net.psforever.objects.ce.Deployable
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.vehicles.MountableWeapons
import net.psforever.objects.{BoomerDeployable, ExplosiveDeployable, TelepadDeployable, Tool, TurretDeployable}
import net.psforever.packet.game.{ChatMsg, DeployableObjectsInfoMessage, GenericActionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HackMessage, HackState, InventoryStateMessage, ObjectAttachMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, OrbitalShuttleTimeMsg, PadAndShuttlePair, PlanetsideAttributeMessage, ProximityTerminalUseMessage, SetEmpireMessage, TriggerEffectMessage, TriggerSoundMessage, TriggeredSound, VehicleStateMessage}
import net.psforever.packet.game.{ChatMsg, DeployableObjectsInfoMessage, GenericActionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HackMessage, HackState, HackState1, InventoryStateMessage, ObjectAttachMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, OrbitalShuttleTimeMsg, PadAndShuttlePair, PlanetsideAttributeMessage, ProximityTerminalUseMessage, SetEmpireMessage, TriggerEffectMessage, TriggerSoundMessage, TriggeredSound, VehicleStateMessage}
import net.psforever.services.Service
import net.psforever.services.local.LocalResponse
import net.psforever.types.{ChatMessageType, PlanetSideGUID, Vector3}
@ -136,7 +136,7 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act
DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
case LocalResponse.SendHackMessageHackCleared(targetGuid, unk1, unk2) =>
sendResponse(HackMessage(unk1=0, targetGuid, guid, progress=0, unk1, HackState.HackCleared, unk2))
sendResponse(HackMessage(HackState1.Unk0, targetGuid, guid, progress=0, unk1.toFloat, HackState.HackCleared, unk2))
case LocalResponse.HackObject(targetGuid, unk1, unk2) =>
sessionLogic.general.hackObject(targetGuid, unk1, unk2)

View file

@ -7,7 +7,7 @@ import net.psforever.objects.ce.Deployable
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects.vehicles.MountableWeapons
import net.psforever.objects.{BoomerDeployable, ExplosiveDeployable, TelepadDeployable, Tool, TurretDeployable}
import net.psforever.packet.game.{ChatMsg, DeployableObjectsInfoMessage, GenericActionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HackMessage, HackState, InventoryStateMessage, ObjectAttachMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, OrbitalShuttleTimeMsg, PadAndShuttlePair, PlanetsideAttributeMessage, ProximityTerminalUseMessage, SetEmpireMessage, TriggerEffectMessage, TriggerSoundMessage, TriggeredSound, VehicleStateMessage}
import net.psforever.packet.game.{ChatMsg, DeployableObjectsInfoMessage, GenericActionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HackMessage, HackState, HackState1, InventoryStateMessage, ObjectAttachMessage, ObjectCreateMessage, ObjectDeleteMessage, ObjectDetachMessage, OrbitalShuttleTimeMsg, PadAndShuttlePair, PlanetsideAttributeMessage, ProximityTerminalUseMessage, SetEmpireMessage, TriggerEffectMessage, TriggerSoundMessage, TriggeredSound, VehicleStateMessage}
import net.psforever.services.Service
import net.psforever.services.local.LocalResponse
import net.psforever.types.{ChatMessageType, PlanetSideGUID, Vector3}
@ -125,7 +125,7 @@ class LocalHandlerLogic(val ops: SessionLocalHandlers, implicit val context: Act
DeconstructDeployable(obj, dguid, pos, obj.Orientation, effect)
case LocalResponse.SendHackMessageHackCleared(targetGuid, unk1, unk2) =>
sendResponse(HackMessage(unk1=0, targetGuid, guid, progress=0, unk1, HackState.HackCleared, unk2))
sendResponse(HackMessage(HackState1.Unk0, targetGuid, guid, progress=0, unk1.toFloat, HackState.HackCleared, unk2))
case LocalResponse.HackObject(targetGuid, unk1, unk2) =>
sessionLogic.general.hackObject(targetGuid, unk1, unk2)

View file

@ -562,8 +562,8 @@ class GeneralOperations(
* @param unk1 na
* @param unk2 na
*/
def hackObject(targetGuid: PlanetSideGUID, unk1: Long, unk2: Long): Unit = {
sendResponse(HackMessage(unk1=0, targetGuid, player_guid=Service.defaultPlayerGUID, progress=100, unk1, HackState.Hacked, unk2))
def hackObject(targetGuid: PlanetSideGUID, unk1: Long, unk2: HackState7): Unit = {
sendResponse(HackMessage(HackState1.Unk0, targetGuid, player_guid=Service.defaultPlayerGUID, progress=100, unk1, HackState.Hacked, unk2))
}
/**

View file

@ -17,7 +17,7 @@ import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.turret.auto.AutomatedTurret
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource}
import net.psforever.objects.vital.{InGameHistory, IncarnationActivity, ReconstructionActivity, SpawningActivity}
import net.psforever.packet.game.{CampaignStatistic, ChangeFireStateMessage_Start, MailMessage, ObjectDetectedMessage, SessionStatistic}
import net.psforever.packet.game.{CampaignStatistic, ChangeFireStateMessage_Start, HackState7, MailMessage, ObjectDetectedMessage, SessionStatistic}
import net.psforever.services.chat.DefaultChannel
import scala.collection.mutable
@ -1084,7 +1084,10 @@ class ZoningOperations(
* @param zone the zone being loaded
*/
def configZone(zone: Zone): Unit = {
zone.Buildings.values.foreach(building => {
zone
.Buildings
.values
.foreach { building =>
val guid = building.GUID
sendResponse(SetEmpireMessage(guid, building.Faction))
// power
@ -1103,7 +1106,8 @@ class ZoningOperations(
case obj if obj.Destroyed => configAmenityAsDestroyed(obj)
case obj => configAmenityAsWorking(obj)
}
})
//sendResponse(HackMessage(HackState1.Unk3, guid, Service.defaultPlayerGUID, progress=0, -1f, HackState.HackCleared, HackState7.Unk8))
}
}
/**
@ -1156,7 +1160,7 @@ class ZoningOperations(
HackCaptureActor.GetHackUpdateAttributeValue(amenity.asInstanceOf[CaptureTerminal], isResecured = false)
)
case _ =>
sessionLogic.general.hackObject(amenity.GUID, 1114636288L, 8L) //generic hackable object
sessionLogic.general.hackObject(amenity.GUID, unk1 = 1114636288L, HackState7.Unk8) //generic hackable object
}
// sync capture flags

View file

@ -11,6 +11,7 @@ import net.psforever.objects.serverobject.turret.{MountableTurret, WeaponTurrets
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.sourcing.SourceEntry
import net.psforever.objects.vital.{InGameActivity, ShieldCharge}
import net.psforever.packet.game.HackState1
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import net.psforever.types.PlanetSideGUID
@ -54,7 +55,7 @@ class FieldTurretControl(turret: TurretDeployable)
sender() ! CommonMessages.Progress(
GenericHackables.GetHackSpeed(player, turret),
WeaponTurrets.FinishHackingTurretDeployable(turret, player),
GenericHackables.HackingTickAction(progressType = 1, player, turret, item.GUID)
GenericHackables.HackingTickAction(HackState1.Unk1, player, turret, item.GUID)
)
case CommonMessages.ChargeShields(amount, motivator) =>

View file

@ -10,7 +10,7 @@ import net.psforever.objects.serverobject.transfer.TransferContainer
import net.psforever.objects.serverobject.structures.WarpGate
import net.psforever.objects.vehicles._
import net.psforever.objects.zones.Zone
import net.psforever.packet.game.TriggeredSound
import net.psforever.packet.game.{HackMessage, HackState, HackState1, HackState7, TriggeredSound}
import net.psforever.types.{DriveState, PlanetSideEmpire, PlanetSideGUID, Vector3}
import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
@ -73,7 +73,7 @@ object Vehicles {
}
vehicle.AssignOwnership(None)
val empire = VehicleLockState.Empire.id
val factionChannel = s"${vehicle.Faction}"
//val factionChannel = s"${vehicle.Faction}"
(0 to 2).foreach(group => {
vehicle.PermissionGroup(group, empire)
/*vehicle.Zone.VehicleEvents ! VehicleServiceMessage(
@ -131,7 +131,7 @@ object Vehicles {
if (vehicle.OwnerGuid.contains(pguid)) {
vehicle.AssignOwnership(None)
//vehicle.Zone.VehicleEvents ! VehicleServiceMessage(player.Name, VehicleAction.Ownership(pguid, PlanetSideGUID(0)))
val vguid = vehicle.GUID
//val vguid = vehicle.GUID
val empire = VehicleLockState.Empire.id
(0 to 2).foreach(group => {
vehicle.PermissionGroup(group, empire)
@ -230,11 +230,23 @@ object Vehicles {
* Change the faction of the vehicle to the hacker's faction and remove all occupants.
* @param target The `Vehicle` object that has been hacked/jacked
* @param hacker the one whoi performed the hack and will inherit ownership of the target vehicle
* @param unk na; used by `HackMessage` as `unk5`
*/
def FinishHackingVehicle(target: Vehicle, hacker: Player, unk: Long)(): Unit = {
def FinishHackingVehicle(target: Vehicle, hacker: Player)(): Unit = {
log.info(s"${hacker.Name} has jacked a ${target.Definition.Name}")
val tGuid = target.GUID
val hGuid = hacker.GUID
val hFaction = hacker.Faction
val zone = target.Zone
val zoneid = zone.id
val vehicleEvents = zone.VehicleEvents
vehicleEvents ! VehicleServiceMessage(
zoneid,
VehicleAction.SendResponse(
Service.defaultPlayerGUID,
HackMessage(HackState1.Unk2, tGuid, hGuid, 100, 0f, HackState.Hacked, HackState7.Unk8)
)
)
target.Actor ! CommonMessages.Hack(hacker, target)
// Forcefully dismount any cargo
target.CargoHolds.foreach { case (_, cargoHold) =>
cargoHold.occupant.collect {
@ -244,18 +256,16 @@ object Vehicles {
// Forcefully dismount all seated occupants from the vehicle
target.Seats.values.foreach(seat => {
seat.occupant.collect {
tplayer: Player =>
seat.unmount(tplayer)
tplayer.VehicleSeated = None
if (tplayer.HasGUID) {
zone.VehicleEvents ! VehicleServiceMessage(
zone.id,
VehicleAction.KickPassenger(tplayer.GUID, 4, unk2 = false, target.GUID)
player: Player =>
seat.unmount(player)
player.VehicleSeated = None
vehicleEvents ! VehicleServiceMessage(
zoneid,
VehicleAction.KickPassenger(player.GUID, 4, unk2 = false, tGuid)
)
}
}
})
// If the vehicle can fly and is flying deconstruct it, and well played to whomever managed to hack a plane in mid air. I'm impressed.
// If the vehicle can fly and is flying: deconstruct it; and well played to whomever managed to hack a plane in mid air
if (target.Definition.CanFly && target.isFlying) {
// todo: Should this force the vehicle to land in the same way as when a pilot bails with passengers on board?
target.Actor ! Vehicle.Deconstruct()
@ -273,19 +283,18 @@ object Vehicles {
Vehicles.Disown(tplayer, target)
}
// Now take ownership of the jacked vehicle
target.Actor ! CommonMessages.Hack(hacker, target)
target.Faction = hacker.Faction
target.Faction = hFaction
Vehicles.Own(target, hacker)
//todo: Send HackMessage -> HackCleared to vehicle? can be found in packet captures. Not sure if necessary.
// And broadcast the faction change to other clients
zone.AvatarEvents ! AvatarServiceMessage(
zone.id,
AvatarAction.SetEmpire(Service.defaultPlayerGUID, target.GUID, hacker.Faction)
zoneid,
AvatarAction.SetEmpire(Service.defaultPlayerGUID, tGuid, hFaction)
)
}
zone.LocalEvents ! LocalServiceMessage(
zone.id,
LocalAction.TriggerSound(hacker.GUID, TriggeredSound.HackVehicle, target.Position, 30, 0.49803925f)
zoneid,
LocalAction.TriggerSound(hGuid, TriggeredSound.HackVehicle, target.Position, 30, 0.49803925f)
)
// Clean up after specific vehicles, e.g. remove router telepads
// If AMS is deployed, swap it to the new faction
@ -298,9 +307,17 @@ object Vehicles {
util.Actor ! TelepadLike.Activate(util)
}
case GlobalDefinitions.ams if target.DeploymentState == DriveState.Deployed =>
zone.VehicleEvents ! VehicleServiceMessage.AMSDeploymentChange(zone)
vehicleEvents ! VehicleServiceMessage.AMSDeploymentChange(zone)
case _ => ()
}
vehicleEvents ! VehicleServiceMessage(
zoneid,
VehicleAction.SendResponse(
Service.defaultPlayerGUID,
HackMessage(HackState1.Unk2, tGuid, tGuid, 0, 1L, HackState.HackCleared, HackState7.Unk8)
)
)
target.Actor ! CommonMessages.ClearHack()
}
def FindANTChargingSource(

View file

@ -165,7 +165,7 @@ object GlobalDefinitionsMiscellaneous {
cert_terminal.Geometry = GeometryForm.representByCylinder(radius = 0.66405f, height = 1.09374f)
implant_terminal_mech.Name = "implant_terminal_mech"
implant_terminal_mech.MaxHealth = 1500 //TODO 1000; right now, 1000 (mech) + 500 (interface)
implant_terminal_mech.MaxHealth = 1000
implant_terminal_mech.Damageable = true
implant_terminal_mech.Repairable = true
implant_terminal_mech.autoRepair = AutoRepairStats(1.6f, 5000, 2400, 0.05f)
@ -176,7 +176,7 @@ object GlobalDefinitionsMiscellaneous {
implant_terminal_interface.Name = "implant_terminal_interface"
implant_terminal_interface.Tab += 0 -> ImplantPage(ImplantTerminalDefinition.implants)
implant_terminal_interface.MaxHealth = 500
implant_terminal_interface.Damageable = false //TODO true
implant_terminal_interface.Damageable = false //true
implant_terminal_interface.Repairable = true
implant_terminal_interface.autoRepair = AutoRepairStats(1, 5000, 200, 1)
implant_terminal_interface.RepairIfDestroyed = true

View file

@ -6,7 +6,7 @@ import net.psforever.objects.serverobject.environment.{EnvironmentAttribute, Env
import net.psforever.objects.sourcing.SourceEntry
import net.psforever.objects.vital.etc.SuicideReason
import net.psforever.objects.vital.interaction.DamageInteraction
import net.psforever.objects.vital.{IncarnationActivity, ReconstructionActivity, Vitality}
import net.psforever.objects.vital.{IncarnationActivity, Vitality}
import net.psforever.objects.zones.InteractsWithZone
import scala.annotation.unused

View file

@ -3,7 +3,7 @@ package net.psforever.objects.serverobject.hackable
import net.psforever.objects.{Player, Vehicle}
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.packet.game.{HackMessage, HackState}
import net.psforever.packet.game.{HackMessage, HackState, HackState1, HackState7}
import net.psforever.types.PlanetSideGUID
import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
@ -68,43 +68,34 @@ object GenericHackables {
* @see `HackState`
* @param progressType 1 - remote electronics kit hack (various ...);
* 2 - nano dispenser (upgrade canister) turret upgrade
* @param tplayer the player performing the action
* @param hacker the player performing the action
* @param target the object being affected
* @param tool_guid the tool being used to affest the object
* @param progress the current progress value
* @return `true`, if the next cycle of progress should occur;
* `false`, otherwise
*/
def HackingTickAction(progressType: Int, tplayer: Player, target: PlanetSideServerObject, tool_guid: PlanetSideGUID)(
def HackingTickAction(progressType: HackState1, hacker: Player, target: PlanetSideServerObject, tool_guid: PlanetSideGUID)(
progress: Float
): Boolean = {
//hack state for progress bar visibility
val vis = if (progress <= 0L) {
HackState.Start
val (progressState, progressGrade) = if (progress <= 0L) {
(HackState.Start, 0)
} else if (progress >= 100L) {
HackState.Finished
} else if (target.isMoving(test = 1f)) {
// If the object is moving (more than slightly to account for things like magriders rotating, or the last velocity reported being the magrider dipping down on dismount) then cancel the hack
HackState.Cancelled
(HackState.Finished, 100)
} else if (target.isMoving(test = 1f) || target.Destroyed || !target.HasGUID) {
(HackState.Cancelled, 0)
} else {
HackState.Ongoing
(HackState.Ongoing, progress.toInt)
}
target.Zone.AvatarEvents ! AvatarServiceMessage(
tplayer.Name,
hacker.Name,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
if (!target.HasGUID) {
//cancel the hack (target is gone)
HackMessage(progressType, target.GUID, tplayer.GUID, 0, 0L, HackState.Cancelled, 8L)
} else if (vis == HackState.Cancelled) {
//cancel the hack (e.g. vehicle drove away)
HackMessage(progressType, target.GUID, tplayer.GUID, 0, 0L, vis, 8L)
} else {
HackMessage(progressType, target.GUID, tplayer.GUID, progress.toInt, 0L, vis, 8L)
}
HackMessage(progressType, target.GUID, hacker.GUID, progressGrade, 0L, progressState, HackState7.Unk8)
)
)
vis != HackState.Cancelled
progressState != HackState.Cancelled
}
/**
@ -112,12 +103,12 @@ object GenericHackables {
* Pass the message onto the hackable object and onto the local events system.
* @param target the `Hackable` object that has been hacked
* @param user the player that is performing this hacking task
* @param unk na;
* used by `HackMessage` as `unk5`
* @param hackValue na;
* @param hackClearValue na
* @see `HackMessage`
*/
//TODO add params here depending on which params in HackMessage are important
def FinishHacking(target: PlanetSideServerObject with Hackable, user: Player, unk: Long)(): Unit = {
def FinishHacking(target: PlanetSideServerObject with Hackable, user: Player, hackValue: Int, hackClearValue: Int)(): Unit = {
import akka.pattern.ask
import scala.concurrent.duration._
// Wait for the target actor to set the HackedBy property, otherwise LocalAction.HackTemporarily will not complete properly
@ -138,7 +129,7 @@ object GenericHackables {
zone.LocalEvents ! LocalServiceMessage(
zoneId,
LocalAction
.HackTemporarily(pguid, zone, target, unk, target.HackEffectDuration(user.avatar.hackingSkillLevel()))
.HackTemporarily(pguid, zone, target, hackValue, hackClearValue, target.HackEffectDuration(user.avatar.hackingSkillLevel()))
)
case Failure(_) =>
log.warn(s"Hack message failed on target: ${target.Definition.Name}@${target.GUID.guid}")

View file

@ -4,9 +4,10 @@ package net.psforever.objects.serverobject.locks
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.affinity.FactionAffinityBehavior
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
import net.psforever.objects.serverobject.structures.Building
import net.psforever.packet.game.HackState1
import net.psforever.types.{PlanetSideEmpire, Vector3}
/**
@ -18,8 +19,8 @@ class IFFLockControl(lock: IFFLock)
extends Actor
with FactionAffinityBehavior.Check
with HackableBehavior.GenericHackable {
def FactionObject: FactionAffinity = lock
def HackableObject = lock
def FactionObject: IFFLock = lock
def HackableObject: IFFLock = lock
def receive: Receive =
checkBehavior
@ -28,16 +29,17 @@ class IFFLockControl(lock: IFFLock)
case CommonMessages.Use(player, Some(item: SimpleItem))
if item.Definition == GlobalDefinitions.remote_electronics_kit =>
if (lock.Faction != player.Faction) {
// 300 / 1
sender() ! CommonMessages.Progress(
GenericHackables.GetHackSpeed(player, lock),
GenericHackables.FinishHacking(lock, player, 1114636288L),
GenericHackables.HackingTickAction(progressType = 1, player, lock, item.GUID)
GenericHackables.FinishHacking(lock, player, hackValue = 60, hackClearValue = 60),
GenericHackables.HackingTickAction(HackState1.Unk1, player, lock, item.GUID)
)
} else if (lock.Faction == player.Faction && lock.HackedBy.nonEmpty) {
sender() ! CommonMessages.Progress(
GenericHackables.GetHackSpeed(player, lock),
IFFLocks.FinishResecuringIFFLock(lock),
GenericHackables.HackingTickAction(progressType = 1, player, lock, item.GUID)
GenericHackables.HackingTickAction(HackState1.Unk1, player, lock, item.GUID)
)
} else {
val log = org.log4s.getLogger

View file

@ -4,8 +4,9 @@ package net.psforever.objects.serverobject.mblocker
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.affinity.FactionAffinityBehavior
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
import net.psforever.packet.game.HackState1
/**
* An `Actor` that handles messages being dispatched to a specific `Locker`.
@ -15,8 +16,8 @@ class LockerControl(locker: Locker)
extends Actor
with FactionAffinityBehavior.Check
with HackableBehavior.GenericHackable {
def FactionObject: FactionAffinity = locker
def HackableObject = locker
def FactionObject: Locker = locker
def HackableObject: Locker = locker
def receive: Receive =
checkBehavior
@ -28,8 +29,8 @@ class LockerControl(locker: Locker)
if (locker.Faction != player.Faction && locker.HackedBy.isEmpty) {
sender() ! CommonMessages.Progress(
GenericHackables.GetHackSpeed(player, locker),
GenericHackables.FinishHacking(locker, player, 3212836864L),
GenericHackables.HackingTickAction(progressType = 1, player, locker, item.GUID)
GenericHackables.FinishHacking(locker, player, hackValue = -1, hackClearValue = -1),
GenericHackables.HackingTickAction(HackState1.Unk1, player, locker, item.GUID)
)
}
case _ => ;

View file

@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.terminals
import akka.actor.{ActorRef, Cancellable}
import net.psforever.objects.sourcing.AmenitySource
import net.psforever.packet.game.HackState1
import org.log4s.Logger
import scala.annotation.unused
@ -73,8 +74,8 @@ class ProximityTerminalControl(term: Terminal with ProximityUnit)
case b: Building if (b.Faction != player.Faction || b.CaptureTerminalIsHacked) && term.HackedBy.isEmpty =>
sender() ! CommonMessages.Progress(
GenericHackables.GetHackSpeed(player, term),
GenericHackables.FinishHacking(term, player, 3212836864L),
GenericHackables.HackingTickAction(progressType = 1, player, term, item.GUID)
GenericHackables.FinishHacking(term, player, hackValue = -1, hackClearValue = -1),
GenericHackables.HackingTickAction(HackState1.Unk1, player, term, item.GUID)
)
case _ => ;
}

View file

@ -11,6 +11,7 @@ import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBe
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableAmenity}
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.packet.game.HackState1
import net.psforever.services.Service
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
@ -48,10 +49,11 @@ class TerminalControl(term: Terminal)
//TODO setup certifications check
term.Owner match {
case b: Building if (b.Faction != player.Faction || b.CaptureTerminalIsHacked) && term.HackedBy.isEmpty =>
//order terminals are 90 / 1, or 60 / ?
sender() ! CommonMessages.Progress(
GenericHackables.GetHackSpeed(player, term),
GenericHackables.FinishHacking(term, player, 3212836864L),
GenericHackables.HackingTickAction(progressType = 1, player, term, item.GUID)
GenericHackables.FinishHacking(term, player, hackValue = -1, hackClearValue = -1),
GenericHackables.HackingTickAction(HackState1.Unk1, player, term, item.GUID)
)
case _ => ()
}

View file

@ -1,9 +1,7 @@
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}
import scala.annotation.unused
@ -24,28 +22,7 @@ trait CaptureTerminalAwareBehavior {
protected def captureTerminalIsResecured(@unused terminal: CaptureTerminal): Unit = { /* intentionally blank */ }
protected def captureTerminalIsHacked(@unused terminal: CaptureTerminal): Unit = {
// Remove seated occupants for mountables
CaptureTerminalAwareObject match {
case mountable: Mountable =>
val guid = mountable.GUID
val zone = mountable.Zone
val zoneId = zone.id
val events = zone.VehicleEvents
mountable.Seats.values.zipWithIndex.foreach {
case (seat, seat_num) =>
seat.occupant.collect {
case player =>
seat.unmount(player)
player.VehicleSeated = None
if (player.HasGUID) {
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, seat_num, unk2=true, guid))
}
}
}
case _ => ()
}
}
protected def captureTerminalIsHacked(@unused terminal: CaptureTerminal): Unit = { /* intentionally blank */ }
}
object CaptureTerminalAwareBehavior {

View file

@ -3,16 +3,17 @@ package net.psforever.objects.serverobject.terminals.capture
import akka.actor.Actor
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
import net.psforever.objects.serverobject.hackable.{GenericHackables, HackableBehavior}
import net.psforever.objects.{GlobalDefinitions, SimpleItem}
import net.psforever.packet.game.HackState1
class CaptureTerminalControl(terminal: CaptureTerminal)
extends Actor
with FactionAffinityBehavior.Check
with HackableBehavior.GenericHackable {
def FactionObject: FactionAffinity = terminal
def HackableObject = terminal
def FactionObject: CaptureTerminal = terminal
def HackableObject: CaptureTerminal = terminal
def receive: Receive =
checkBehavior
@ -27,11 +28,11 @@ class CaptureTerminalControl(terminal: CaptureTerminal)
if (canHack) {
sender() ! CommonMessages.Progress(
GenericHackables.GetHackSpeed(player, terminal),
CaptureTerminals.FinishHackingCaptureConsole(terminal, player, 3212836864L),
GenericHackables.HackingTickAction(progressType = 1, player, terminal, item.GUID)
CaptureTerminals.FinishHackingCaptureConsole(terminal, player, unk = -1),
GenericHackables.HackingTickAction(HackState1.Unk1, player, terminal, item.GUID)
)
}
case _ => ; //no default message
case _ => () //no default message
}
}

View file

@ -20,16 +20,16 @@ object CaptureTerminals {import scala.concurrent.duration._
* @see `HackMessage`
*/
//TODO add params here depending on which params in HackMessage are important
def FinishHackingCaptureConsole(target: CaptureTerminal, hackingPlayer: Player, unk: Long)(): Unit = {
def FinishHackingCaptureConsole(target: CaptureTerminal, hackingPlayer: Player, unk: Int)(): Unit = {
import akka.pattern.ask
log.info(s"${hackingPlayer.toString} hacked a ${target.Definition.Name}")
// Wait for the target actor to set the HackedBy property
import scala.concurrent.ExecutionContext.Implicits.global
ask(target.Actor, CommonMessages.Hack(hackingPlayer, target))(timeout = 2 second)
.mapTo[CommonMessages.EntityHackState]
.onComplete {
case Success(_) =>
log.info(s"${hackingPlayer.toString} hacked a ${target.Definition.Name}")
val zone = target.Zone
val zoneid = zone.id
val events = zone.LocalEvents

View file

@ -0,0 +1,39 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.serverobject.terminals.implant
import akka.actor.ActorRef
import net.psforever.objects.Player
import net.psforever.objects.serverobject.hackable.{GenericHackables, Hackable}
import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl}
import net.psforever.objects.zones.Zone
import net.psforever.types.PlanetSideGUID
object ImplantInterfaceControl {
private def FindPairedTerminalMech(
zone: Zone,
interfaceGuid: PlanetSideGUID
): Option[Amenity with Hackable] = {
zone
.map
.terminalToInterface
.find { case (_, guid) => guid == interfaceGuid.guid }
.flatMap { case (mechGuid, _) => zone.GUID(mechGuid) }
.collect { case mech: ImplantTerminalMech if !mech.Destroyed && mech.HackedBy.isEmpty => mech }
}
}
class ImplantInterfaceControl(private val terminal: Terminal)
extends TerminalControl(terminal) {
override def performHack(player: Player, data: Option[Any], replyTo: ActorRef): Unit = {
HackableObject.HackedBy
.orElse {
super.performHack(player, data, replyTo)
ImplantInterfaceControl
.FindPairedTerminalMech(terminal.Zone, terminal.GUID)
.foreach(GenericHackables.FinishHacking(_, player, hackValue = -1, hackClearValue = -1)())
None
}
}
}

View file

@ -0,0 +1,25 @@
// Copyright (c) 2024 PSForever
package net.psforever.objects.serverobject.terminals.implant
import akka.actor.ActorContext
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalDefinition}
import net.psforever.types.Vector3
object ImplantTerminalInterface {
/**
* Instantiate and configure a `Terminal` object
* @param pdef `ObjectDefinition` that constructs this object and maintains some of its immutable fields
* @param pos position
* @param id the unique id that will be assigned to this entity
* @param context a context to allow the object to properly set up `ActorSystem` functionality
* @return the `Terminal` object
*/
def Constructor(pos: Vector3, pdef: TerminalDefinition)(id: Int, context: ActorContext): Terminal = {
import akka.actor.Props
val obj = Terminal(pdef)
obj.Position = pos
obj.Actor = context.actorOf(Props(classOf[ImplantInterfaceControl], obj), s"${obj.Definition.Name}_$id")
obj
}
}

View file

@ -1,18 +1,40 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.serverobject.terminals.implant
import akka.actor.ActorRef
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.hackable.{GenericHackables, Hackable, HackableBehavior}
import net.psforever.objects.serverobject.mount.{Mountable, MountableBehavior}
import net.psforever.objects.serverobject.repair.{AmenityAutoRepair, RepairableAmenity, RepairableEntity}
import net.psforever.objects.serverobject.structures.{Building, PoweredAmenityControl}
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminalAwareBehavior
import net.psforever.objects.serverobject.structures.{Amenity, Building, PoweredAmenityControl}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, CaptureTerminalAwareBehavior}
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.objects.zones.Zone
import net.psforever.objects.{GlobalDefinitions, Player, SimpleItem}
import net.psforever.packet.game.HackState1
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID}
import scala.annotation.unused
object ImplantTerminalMechControl {
private def FindPairedTerminalInterface(
zone: Zone,
mechGuid: PlanetSideGUID
): Option[Amenity with Hackable] = {
zone
.map
.terminalToInterface
.find { case (guid, _) => guid == mechGuid.guid }
.flatMap { case (_, interfaceGuid) => zone.GUID(interfaceGuid) }
.collect { case terminal: Terminal if !terminal.Destroyed && terminal.HackedBy.isEmpty => terminal }
}
}
/**
* An `Actor` that handles messages being dispatched to a specific `ImplantTerminalMech`.
@ -41,9 +63,11 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
.orElse(takesDamage)
.orElse(canBeRepairedByNanoDispenser)
.orElse(autoRepairBehavior)
.orElse(captureTerminalAwareBehaviour)
def poweredStateLogic : Receive =
commonBehavior
.orElse(hackableBehavior)
.orElse(mountBehavior)
.orElse {
case CommonMessages.Use(player, Some(item: SimpleItem))
@ -53,18 +77,19 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
case b: Building if (b.Faction != player.Faction || b.CaptureTerminalIsHacked) && mech.HackedBy.isEmpty =>
sender() ! CommonMessages.Progress(
GenericHackables.GetHackSpeed(player, mech),
GenericHackables.FinishHacking(mech, player, 3212836864L),
GenericHackables.HackingTickAction(progressType = 1, player, mech, item.GUID)
GenericHackables.FinishHacking(mech, player, hackValue = -1, hackClearValue = -1),
GenericHackables.HackingTickAction(HackState1.Unk1, player, mech, item.GUID)
)
case _ => ;
case _ => ()
}
case _ => ;
case _ => ()
}
def unpoweredStateLogic: Receive =
commonBehavior
.orElse(hackableBehavior)
.orElse {
case _ => ;
case _ => ()
}
override protected def mountTest(
@ -120,15 +145,12 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
val zoneId = zone.id
val events = zone.VehicleEvents
mech.Seats.values.foreach(seat =>
seat.occupant match {
case Some(player) =>
seat.occupant.collect {
case player =>
seat.unmount(player)
player.VehicleSeated = None
if (player.HasGUID) {
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, unk2=false, guid))
}
case None => ;
}
)
}
@ -140,4 +162,123 @@ class ImplantTerminalMechControl(mech: ImplantTerminalMech)
super.Restoration(obj)
RepairableAmenity.RestorationOfHistory(obj)
}
override def performHack(player: Player, data: Option[Any], replyTo: ActorRef): Unit = {
//todo don't now how to properly hack this amenity
super.performHack(player, data, replyTo)
val zone = HackableObject.Zone
val guid = HackableObject.GUID
val localFaction = mech.Faction
val events = zone.LocalEvents
if (player.Faction == localFaction) {
if (mech.Owner.asInstanceOf[Building].CaptureTerminalIsHacked) {
//this is actually futile, as a hacked base does not grant access to the terminal
events ! LocalServiceMessage(localFaction.toString, LocalAction.SetEmpire(guid, localFaction))
}
kickAllOccupantsNotOfFaction(zone, guid, mech, localFaction)
} else {
opposingFactionsMayAccess(zone, guid, localFaction)
kickAllOccupantsOfFaction(zone, guid, mech, localFaction)
}
ImplantTerminalMechControl
.FindPairedTerminalInterface(zone, guid)
.foreach(GenericHackables.FinishHacking(_, player, hackValue = -1, hackClearValue = -1)())
}
override def performClearHack(data: Option[Any], replyTo: ActorRef): Unit = {
//todo don't now how to properly unhack this amenity
HackableObject.HackedBy.collect { _ =>
super.performClearHack(data, replyTo)
val toFaction = HackableObject.Faction
val zone = HackableObject.Zone
val guid = HackableObject.GUID
noAccessByOpposingFactions(zone, guid, toFaction)
kickAllOccupantsNotOfFaction(zone, guid, mech, toFaction)
}
}
override protected def captureTerminalIsHacked(@unused terminal: CaptureTerminal): Unit = {
//todo don't now how to properly handle a hacked mech
super.captureTerminalIsHacked(terminal)
val zone = HackableObject.Zone
val guid = HackableObject.GUID
kickAllOccupantsNotOfFactionWithTest(zone, guid, mech, (_: PlanetSideEmpire.Value) => { true })
}
override protected def captureTerminalIsResecured(terminal: CaptureTerminal): Unit = {
//todo don't now how to properly handle a hacked mech
super.captureTerminalIsResecured(terminal)
//if hacked, correct
val zone = HackableObject.Zone
val guid = HackableObject.GUID
val toFaction = HackableObject.Faction
HackableObject.HackedBy.collect {
case hackInfo if hackInfo.hackerFaction != toFaction =>
opposingFactionsMayAccess(zone, guid, toFaction)
}
}
private def opposingFactionsMayAccess(
zone: Zone,
guid: PlanetSideGUID,
setToFaction: PlanetSideEmpire.Value
): Unit = {
val events = zone.LocalEvents
opposingFactionsAre(setToFaction).foreach { faction =>
events ! LocalServiceMessage(faction.toString, LocalAction.SetEmpire(guid, faction))
}
}
private def noAccessByOpposingFactions(
zone: Zone,
guid: PlanetSideGUID,
setToFaction: PlanetSideEmpire.Value
): Unit = {
val events = zone.LocalEvents
opposingFactionsAre(setToFaction).foreach { faction =>
events ! LocalServiceMessage(faction.toString, LocalAction.SetEmpire(guid, setToFaction))
}
}
private def opposingFactionsAre(faction: PlanetSideEmpire.Value): PlanetSideEmpire.ValueSet = {
PlanetSideEmpire
.values
.filterNot { f => f == PlanetSideEmpire.NEUTRAL && f == faction }
}
private def kickAllOccupantsOfFaction(
zone: Zone,
guid: PlanetSideGUID,
obj: Mountable,
isFaction: PlanetSideEmpire.Value
): Unit = {
kickAllOccupantsNotOfFactionWithTest(zone, guid, obj, (a: PlanetSideEmpire.Value) => { a == isFaction })
}
private def kickAllOccupantsNotOfFaction(
zone: Zone,
guid: PlanetSideGUID,
obj: Mountable,
isFaction: PlanetSideEmpire.Value
): Unit = {
kickAllOccupantsNotOfFactionWithTest(zone, guid, obj, (a: PlanetSideEmpire.Value) => { a != isFaction })
}
private def kickAllOccupantsNotOfFactionWithTest(
zone: Zone,
guid: PlanetSideGUID,
obj: Mountable,
test: PlanetSideEmpire.Value => Boolean
): Unit = {
val zoneId = zone.id
val events = zone.LocalEvents
obj.Seats.values.foreach(seat =>
seat.occupant.collect {
case player if test(player.Faction) =>
seat.unmount(player)
player.VehicleSeated = None
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, 4, unk2 = false, guid))
}
)
}
}

View file

@ -13,7 +13,7 @@ import net.psforever.objects.serverobject.terminals.capture.{CaptureTerminal, Ca
import net.psforever.objects.serverobject.turret.auto.AutomatedTurret.Target
import net.psforever.objects.serverobject.turret.auto.{AffectedByAutomaticTurretFire, AutomatedTurret, AutomatedTurretBehavior}
import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.packet.game.ChangeFireModeMessage
import net.psforever.packet.game.{ChangeFireModeMessage, HackState1}
import net.psforever.services.Service
import net.psforever.services.vehicle.support.TurretUpgrader
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}
@ -67,7 +67,7 @@ class FacilityTurretControl(turret: FacilityTurret)
sender() ! CommonMessages.Progress(
1.25f,
WeaponTurrets.FinishUpgradingMannedTurret(TurretObject, player, item, upgrade),
WeaponTurrets.TurretUpgradingTickAction(progressType = 2, player, TurretObject, item.GUID)
WeaponTurrets.TurretUpgradingTickAction(HackState1.Unk2, player, TurretObject, item.GUID)
)
}
case TurretUpgrader.UpgradeCompleted(_) =>
@ -330,6 +330,21 @@ class FacilityTurretControl(turret: FacilityTurret)
}
override protected def captureTerminalIsHacked(terminal: CaptureTerminal): Unit = {
super.captureTerminalIsHacked(terminal)
// Remove seated occupants
val guid = turret.GUID
val zone = turret.Zone
val zoneId = zone.id
val events = zone.VehicleEvents
turret.Seats.values.zipWithIndex.foreach {
case (seat, seat_num) =>
seat.occupant.collect {
case player =>
seat.unmount(player)
player.VehicleSeated = None
events ! VehicleServiceMessage(zoneId, VehicleAction.KickPassenger(player.GUID, seat_num, unk2=true, guid))
}
}
captureTerminalChanges(terminal, super.captureTerminalIsHacked, actionDelays = 3000L)
}

View file

@ -5,7 +5,7 @@ import net.psforever.objects.avatar.Certification
import net.psforever.objects.ce.Deployable
import net.psforever.objects.serverobject.hackable.GenericHackables.updateTurretUpgradeTime
import net.psforever.objects.{Player, Tool, TurretDeployable}
import net.psforever.packet.game.{HackMessage, HackState, InventoryStateMessage}
import net.psforever.packet.game.{HackMessage, HackState, HackState1, HackState7, InventoryStateMessage}
import net.psforever.services.Service
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
@ -73,26 +73,27 @@ object WeaponTurrets {
* @return `true`, if the next cycle of progress should occur;
* `false`, otherwise
*/
def TurretUpgradingTickAction(progressType: Int, tplayer: Player, turret: FacilityTurret, tool_guid: PlanetSideGUID)(
def TurretUpgradingTickAction(progressType: HackState1, tplayer: Player, turret: FacilityTurret, tool_guid: PlanetSideGUID)(
progress: Float
): Boolean = {
//hack state for progress bar visibility
val vis = if (progress <= 0L) {
HackState.Start
val (progressState, progressGrade) = if (progress <= 0L) {
(HackState.Start, 0)
} else if (progress >= 100L) {
HackState.Finished
(HackState.Finished, 100)
} else if (turret.Destroyed) {
(HackState.Cancelled, 0)
} else {
updateTurretUpgradeTime()
HackState.Ongoing
(HackState.Ongoing, progress.toInt)
}
turret.Zone.AvatarEvents ! AvatarServiceMessage(
tplayer.Name,
AvatarAction.SendResponse(
Service.defaultPlayerGUID,
HackMessage(progressType, turret.GUID, tplayer.GUID, progress.toInt, 0L, vis, 8L)
HackMessage(progressType, turret.GUID, tplayer.GUID, progressGrade, -1f, progressState, HackState7.Unk8)
)
)
vis != HackState.Cancelled
progressState != HackState.Cancelled
}
def FinishHackingTurretDeployable(target: TurretDeployable, hacker: Player)(): Unit = {

View file

@ -179,8 +179,8 @@ class VehicleControl(vehicle: Vehicle)
if (vehicle.Faction != player.Faction) {
sender() ! CommonMessages.Progress(
GenericHackables.GetHackSpeed(player, vehicle),
Vehicles.FinishHackingVehicle(vehicle, player, 3212836864L),
GenericHackables.HackingTickAction(progressType = 1, player, vehicle, item.GUID)
Vehicles.FinishHackingVehicle(vehicle, player),
GenericHackables.HackingTickAction(HackState1.Unk1, player, vehicle, item.GUID)
)
}

View file

@ -1,10 +1,52 @@
// Copyright (c) 2017 PSForever
package net.psforever.packet.game
import enumeratum.values.{IntEnum, IntEnumEntry}
import net.psforever.packet.GamePacketOpcode.Type
import net.psforever.packet.{GamePacketOpcode, Marshallable, PacketHelpers, PlanetSideGamePacket}
import net.psforever.types.PlanetSideGUID
import scodec.Codec
import scodec.bits.BitVector
import scodec.{Attempt, Codec}
import scodec.codecs._
import shapeless.{::, HNil}
sealed abstract class HackState1(val value: Int) extends IntEnumEntry
object HackState1 extends IntEnum[HackState1] {
val values: IndexedSeq[HackState1] = findValues
case object Unk0 extends HackState1(value = 0)
case object Unk1 extends HackState1(value = 1)
case object Unk2 extends HackState1(value = 2)
case object Unk3 extends HackState1(value = 3)
implicit val codec: Codec[HackState1] = PacketHelpers.createIntEnumCodec(this, uint2)
}
sealed abstract class HackState7(val value: Int) extends IntEnumEntry
object HackState7 extends IntEnum[HackState7] {
val values: IndexedSeq[HackState7] = findValues
case object Unk0 extends HackState7(value = 0)
case object Unk1 extends HackState7(value = 1)
case object Unk2 extends HackState7(value = 2)
case object Unk3 extends HackState7(value = 3)
case object Unk4 extends HackState7(value = 4)
case object Unk5 extends HackState7(value = 5)
case object Unk6 extends HackState7(value = 6)
case object Unk7 extends HackState7(value = 7)
case object Unk8 extends HackState7(value = 8)
implicit val codec: Codec[HackState7] = (PacketHelpers.createIntEnumCodec(this, uint8) :: ignore(size = 24)).xmap[HackState7](
{
case a :: _ :: HNil => a
},
{
a => a :: () :: HNil
}
)
}
/**
* An `Enumeration` of the various states and activities of the hacking process.
@ -16,12 +58,21 @@ import scodec.codecs._
* `Hacked` modifies the target of the hack.<br>
* `HackCleared` modifies the target of the hack, opposite of `Hacked`.
*/
object HackState extends Enumeration {
type Type = Value
sealed abstract class HackState(val value: Int) extends IntEnumEntry
val Unknown0, Start, Cancelled, Ongoing, Finished, Unknown5, Hacked, HackCleared = Value
object HackState extends IntEnum[HackState] {
val values: IndexedSeq[HackState] = findValues
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint8L)
case object Unknown0 extends HackState(value = 0)
case object Start extends HackState(value = 1)
case object Cancelled extends HackState(value = 2)
case object Ongoing extends HackState(value = 3)
case object Finished extends HackState(value = 4)
case object Unknown5 extends HackState(value = 5)
case object Hacked extends HackState(value = 6)
case object HackCleared extends HackState(value = 7)
implicit val codec: Codec[HackState] = PacketHelpers.createIntEnumCodec(this, uint8L)
}
/**
@ -45,6 +96,7 @@ object HackState extends Enumeration {
* will assist the conversion of allied facility turreted weapons to their upgraded armaments.
* @param unk1 na;
* 0 commonly;
* 1 unknown;
* 2 when performing (phalanx) upgrades;
* 3 for building objects during login phase;
* hack type?
@ -58,31 +110,44 @@ object HackState extends Enumeration {
* doesn't seem to be `char_id`?
* @param hack_state hack state
* @param unk7 na;
* usually 8;
* values 3-7 noted for the Hacked state;
* 5 - boost pain field at matrixing terminal?
* usually, 8?
*/
final case class HackMessage(
unk1: Int,
unk1: HackState1,
target_guid: PlanetSideGUID,
player_guid: PlanetSideGUID,
progress: Int,
unk5: Long,
hack_state: HackState.Value,
unk7: Long
unk5: Float,
hack_state: HackState,
unk7: HackState7
) extends PlanetSideGamePacket {
type Packet = HackMessage
def opcode = GamePacketOpcode.HackMessage
def encode = HackMessage.encode(this)
def opcode: Type = GamePacketOpcode.HackMessage
def encode: Attempt[BitVector] = HackMessage.encode(this)
}
object HackMessage extends Marshallable[HackMessage] {
def apply(
unk1: HackState1,
target_guid: PlanetSideGUID,
player_guid: PlanetSideGUID,
progress: Int,
unk5: Int,
hack_state: HackState,
unk7: HackState7
): HackMessage = {
new HackMessage(unk1, target_guid, player_guid, progress, unk5.toFloat, hack_state, unk7)
}
implicit val codec: Codec[HackMessage] = (
("unk1" | uint2L) ::
("unk1" | HackState1.codec) ::
("object_guid" | PlanetSideGUID.codec) ::
("player_guid" | PlanetSideGUID.codec) ::
("progress" | uint8L) ::
("unk5" | uint32L) ::
("unk5" | floatL) ::
("hack_state" | HackState.codec) ::
("unk7" | uint32L)
("unk7" | HackState7.codec)
).as[HackMessage]
}

View file

@ -96,10 +96,10 @@ class LocalService(zone: Zone) extends Actor {
LocalResponse.SendHackMessageHackCleared(target.GUID, unk1, unk2)
)
)
case LocalAction.HackTemporarily(player_guid, _, target, unk1, duration, unk2) =>
hackClearer ! HackClearActor.ObjectIsHacked(target, zone, unk1, unk2, duration)
case LocalAction.HackTemporarily(player_guid, _, target, hackValue, hackClear, duration, unk2) =>
hackClearer ! HackClearActor.ObjectIsHacked(target, zone, hackClear, unk2, duration)
LocalEvents.publish(
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackObject(target.GUID, unk1, unk2))
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackObject(target.GUID, hackValue, unk2))
)
case LocalAction.ClearTemporaryHack(_, target) =>
hackClearer ! HackClearActor.ObjectIsResecured(target)

View file

@ -14,7 +14,7 @@ import net.psforever.objects.{PlanetSideGameObject, TelepadDeployable, Vehicle}
import net.psforever.packet.PlanetSideGamePacket
import net.psforever.packet.game.GenericObjectActionEnum.GenericObjectActionEnum
import net.psforever.packet.game.PlanetsideAttributeEnum.PlanetsideAttributeEnum
import net.psforever.packet.game.{ChatMsg, DeployableInfo, DeploymentAction, GenericAction, TriggeredSound}
import net.psforever.packet.game.{ChatMsg, DeployableInfo, DeploymentAction, GenericAction, HackState7, TriggeredSound}
import net.psforever.services.hart.HartTimer.OrbitalShuttleEvent
import net.psforever.types.{PlanetSideEmpire, PlanetSideGUID, Vector3}
@ -43,15 +43,16 @@ object LocalAction {
pos: Vector3,
deletionEffect: Int
) extends Action
final case class HackClear(player_guid: PlanetSideGUID, target: PlanetSideServerObject, unk1: Long, unk2: Long = 8L)
final case class HackClear(player_guid: PlanetSideGUID, target: PlanetSideServerObject, unk1: Long, unk2: HackState7 = HackState7.Unk8)
extends Action
final case class HackTemporarily(
player_guid: PlanetSideGUID,
continent: Zone,
target: PlanetSideServerObject,
unk1: Long,
hackValue: Long,
hackClearValue: Long,
duration: Int,
unk2: Long = 8L
unk2: HackState7 = HackState7.Unk8
) extends Action
final case class ClearTemporaryHack(player_guid: PlanetSideGUID, target: PlanetSideServerObject with Hackable)
extends Action

View file

@ -34,8 +34,8 @@ object LocalResponse {
pos: Vector3,
deletionEffect: Int
) 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 SendHackMessageHackCleared(target_guid: PlanetSideGUID, unk1: Long, unk2: HackState7) extends Response
final case class HackObject(target_guid: PlanetSideGUID, unk1: Long, unk2: HackState7) extends Response
final case class SendPacket(packet: PlanetSideGamePacket) extends Response
final case class PlanetsideAttribute(target_guid: PlanetSideGUID, attribute_number: PlanetsideAttributeEnum, attribute_value: Long)

View file

@ -10,7 +10,7 @@ import net.psforever.objects.serverobject.structures.Building
import net.psforever.objects.serverobject.terminals.capture.CaptureTerminal
import net.psforever.objects.zones.Zone
import net.psforever.objects.Default
import net.psforever.packet.game.{GenericAction, PlanetsideAttributeEnum}
import net.psforever.packet.game.{GenericAction, HackState7, PlanetsideAttributeEnum}
import net.psforever.objects.sourcing.PlayerSource
import net.psforever.services.Service
import net.psforever.services.local.support.HackCaptureActor.GetHackingFaction
@ -231,7 +231,7 @@ class HackCaptureActor extends Actor {
}
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
context.parent ! HackClearActor.SendHackMessageHackCleared(building.GUID, terminal.Zone.id, 3212836864L, HackState7.Unk8) //call up
}
private def RestartTimer(): Unit = {

View file

@ -2,12 +2,12 @@
package net.psforever.services.local.support
import java.util.concurrent.TimeUnit
import akka.actor.{Actor, Cancellable}
import net.psforever.objects.Default
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.HackState7
import net.psforever.types.PlanetSideGUID
import scala.annotation.tailrec
@ -156,7 +156,7 @@ object HackClearActor {
target: PlanetSideServerObject,
zone: Zone,
unk1: Long,
unk2: Long,
unk2: HackState7,
duration: Int,
time: Long = System.currentTimeMillis()
)
@ -172,7 +172,7 @@ object HackClearActor {
* @param obj the server object
* @param zone_id the zone in which the object resides
*/
final case class SendHackMessageHackCleared(obj: PlanetSideGUID, zone_id: String, unk1: Long, unk2: Long)
final case class SendHackMessageHackCleared(obj: PlanetSideGUID, zone_id: String, unk1: Long, unk2: HackState7)
/**
* Internal message used to signal a test of the queued door information.
@ -192,7 +192,7 @@ object HackClearActor {
target: PlanetSideServerObject,
zone: Zone,
unk1: Long,
unk2: Long,
unk2: HackState7,
time: Long,
duration: Long
)

View file

@ -21,7 +21,7 @@ import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
import net.psforever.objects.serverobject.shuttle.OrbitalShuttlePad
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.terminals.implant.{ImplantTerminalInterface, ImplantTerminalMech}
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.serverobject.turret.{FacilityTurret, FacilityTurretDefinition, VanuSentry}
import net.psforever.objects.serverobject.zipline.ZipLinePath
@ -637,7 +637,7 @@ object Zones {
zoneMap.addLocalObject(
closestTerminal.guid,
Terminal.Constructor(closestTerminal.position, GlobalDefinitions.implant_terminal_interface),
ImplantTerminalInterface.Constructor(closestTerminal.position, GlobalDefinitions.implant_terminal_interface),
owningBuildingGuid = ownerGuid
)

View file

@ -14,20 +14,20 @@ class HackMessageTest extends Specification {
"decode" in {
PacketCoding.decodePacket(string).require match {
case HackMessage(unk1, target_guid, player_guid, progress, unk5, hack_state, unk7) =>
unk1 mustEqual 0
unk1 mustEqual HackState1.Unk0
target_guid mustEqual PlanetSideGUID(1024)
player_guid mustEqual PlanetSideGUID(3607)
progress mustEqual 0
unk5 mustEqual 3212836864L
unk5 mustEqual -1.0f
hack_state mustEqual HackState.Start
unk7 mustEqual 8L
unk7 mustEqual HackState7.Unk8
case _ =>
ko
}
}
"encode" in {
val msg = HackMessage(0, PlanetSideGUID(1024), PlanetSideGUID(3607), 0, 3212836864L, HackState.Start, 8L)
val msg = HackMessage(HackState1.Unk0, PlanetSideGUID(1024), PlanetSideGUID(3607), 0, -1.0f, HackState.Start, HackState7.Unk8)
val pkt = PacketCoding.encodePacket(msg).require.toByteVector
pkt mustEqual string
}

View file

@ -136,9 +136,9 @@ class HackClearTest extends ActorTest {
"pass HackClear" in {
val service = system.actorOf(Props(classOf[LocalService], Zone.Nowhere), "l_service")
service ! Service.Join("test")
service ! LocalServiceMessage("test", LocalAction.HackClear(PlanetSideGUID(10), obj, 0L, 1000L))
service ! LocalServiceMessage("test", LocalAction.HackClear(PlanetSideGUID(10), obj, 0L, HackState7.Unk8))
expectMsg(
LocalServiceResponse("/test/Local", PlanetSideGUID(10), LocalResponse.SendHackMessageHackCleared(PlanetSideGUID(40), 0L, 1000L))
LocalServiceResponse("/test/Local", PlanetSideGUID(10), LocalResponse.SendHackMessageHackCleared(PlanetSideGUID(40), 0L, HackState7.Unk8))
)
}
}