Fix implant terminal hacking (#246)

* Fix hacking implant terminals

* Remove duplication and apply some DRY
This commit is contained in:
Mazo 2019-04-04 19:26:43 +01:00 committed by Fate-JH
parent cd5caf58a0
commit 8313ce6491
10 changed files with 99 additions and 50 deletions

View file

@ -0,0 +1,27 @@
package net.psforever.objects.serverobject.hackable
import akka.actor.Actor
import net.psforever.objects.serverobject.CommonMessages
object HackableBehavior {
/**
* The logic governing generic `Hackable` objects that use the `Hack` and `ClearHack` message.
* This is a mix-in trait for combining with existing Receive` logic.
* @see `Hackable`
*/
trait GenericHackable {
this : Actor =>
def HackableObject : Hackable
val hackableBehavior : Receive = {
case CommonMessages.Hack(player) =>
val obj = HackableObject
obj.HackedBy = player
sender ! true
case CommonMessages.ClearHack() =>
val obj = HackableObject
obj.HackedBy = None
}
}
}

View file

@ -3,18 +3,24 @@ package net.psforever.objects.serverobject.implantmech
import net.psforever.objects.Player import net.psforever.objects.Player
import net.psforever.objects.definition.ObjectDefinition import net.psforever.objects.definition.ObjectDefinition
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.mount.Mountable import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.serverobject.structures.Amenity import net.psforever.objects.serverobject.structures.Amenity
import net.psforever.objects.vehicles.Seat import net.psforever.objects.vehicles.Seat
import net.psforever.packet.game.TriggeredSound
/** /**
* A structure-owned server object that is the visible and `Mountable` component of an implant terminal. * A structure-owned server object that is the visible and `Mountable` component of an implant terminal.
* For the most part, it merely implements the support data structures indicated by `Mountable`. * For the most part, it merely implements the support data structures indicated by `Mountable`.
* @param idef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields * @param idef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
*/ */
class ImplantTerminalMech(private val idef : ImplantTerminalMechDefinition) extends Amenity with Mountable { class ImplantTerminalMech(private val idef : ImplantTerminalMechDefinition) extends Amenity with Mountable with Hackable {
private val seats : Map[Int, Seat] = Map( 0 -> new Seat(idef.Seats(0)) ) private val seats : Map[Int, Seat] = Map( 0 -> new Seat(idef.Seats(0)) )
HackSound = TriggeredSound.HackTerminal
HackEffectDuration = Array(0, 30, 60, 90)
HackDuration = Array(0, 10, 5, 3)
def Seats : Map[Int, Seat] = seats def Seats : Map[Int, Seat] = seats
def Seat(seatNum : Int) : Option[Seat] = seats.get(seatNum) def Seat(seatNum : Int) : Option[Seat] = seats.get(seatNum)

View file

@ -4,20 +4,23 @@ package net.psforever.objects.serverobject.implantmech
import akka.actor.Actor import akka.actor.Actor
import net.psforever.objects.serverobject.mount.MountableBehavior import net.psforever.objects.serverobject.mount.MountableBehavior
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.hackable.HackableBehavior
/** /**
* An `Actor` that handles messages being dispatched to a specific `ImplantTerminalMech`. * An `Actor` that handles messages being dispatched to a specific `ImplantTerminalMech`.
* @param mech the "mech" object being governed * @param mech the "mech" object being governed
*/ */
class ImplantTerminalMechControl(mech : ImplantTerminalMech) extends Actor with FactionAffinityBehavior.Check class ImplantTerminalMechControl(mech : ImplantTerminalMech) extends Actor with FactionAffinityBehavior.Check
with MountableBehavior.Mount with MountableBehavior.Dismount { with MountableBehavior.Mount with MountableBehavior.Dismount with HackableBehavior.GenericHackable {
def MountableObject = mech //do not add type! def MountableObject = mech //do not add type!
def HackableObject = mech
def FactionObject : FactionAffinity = mech def FactionObject : FactionAffinity = mech
def receive : Receive = checkBehavior def receive : Receive = checkBehavior
.orElse(mountBehavior) .orElse(mountBehavior)
.orElse(dismountBehavior) .orElse(dismountBehavior)
.orElse(hackableBehavior)
.orElse { .orElse {
case _ => ; case _ => ;
} }

View file

@ -4,22 +4,20 @@ package net.psforever.objects.serverobject.locks
import akka.actor.Actor import akka.actor.Actor
import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.hackable.HackableBehavior
/** /**
* An `Actor` that handles messages being dispatched to a specific `IFFLock`. * An `Actor` that handles messages being dispatched to a specific `IFFLock`.
* @param lock the `IFFLock` object being governed * @param lock the `IFFLock` object being governed
* @see `CommonMessages` * @see `CommonMessages`
*/ */
class IFFLockControl(lock : IFFLock) extends Actor with FactionAffinityBehavior.Check { class IFFLockControl(lock : IFFLock) extends Actor with FactionAffinityBehavior.Check with HackableBehavior.GenericHackable {
def FactionObject : FactionAffinity = lock def FactionObject : FactionAffinity = lock
def HackableObject = lock
def receive : Receive = checkBehavior.orElse { def receive : Receive = checkBehavior
case CommonMessages.Hack(player) => .orElse(hackableBehavior)
lock.HackedBy = player .orElse {
sender ! true case _ => ; //no default message
case CommonMessages.ClearHack() =>
lock.HackedBy = None
case _ => ; //no default message
} }
} }

View file

@ -2,22 +2,20 @@
package net.psforever.objects.serverobject.mblocker package net.psforever.objects.serverobject.mblocker
import akka.actor.Actor import akka.actor.Actor
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.hackable.HackableBehavior
/** /**
* An `Actor` that handles messages being dispatched to a specific `Locker`. * An `Actor` that handles messages being dispatched to a specific `Locker`.
* @param locker the `Locker` object being governed * @param locker the `Locker` object being governed
*/ */
class LockerControl(locker : Locker) extends Actor with FactionAffinityBehavior.Check { class LockerControl(locker : Locker) extends Actor with FactionAffinityBehavior.Check with HackableBehavior.GenericHackable {
def FactionObject : FactionAffinity = locker def FactionObject : FactionAffinity = locker
def HackableObject = locker
def receive : Receive = checkBehavior.orElse { def receive : Receive = checkBehavior
case CommonMessages.Hack(player) => .orElse(hackableBehavior)
locker.HackedBy = player .orElse {
sender ! true case _ => ;
case CommonMessages.ClearHack() =>
locker.HackedBy = None
case _ => ;
} }
} }

View file

@ -5,6 +5,7 @@ import akka.actor.Actor
import net.psforever.objects.PlanetSideGameObject import net.psforever.objects.PlanetSideGameObject
import net.psforever.objects.entity.{Identifiable, WorldEntity} import net.psforever.objects.entity.{Identifiable, WorldEntity}
import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.affinity.FactionAffinity
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.turret.TurretDefinition import net.psforever.objects.serverobject.turret.TurretDefinition
import net.psforever.types.Vector3 import net.psforever.types.Vector3
@ -25,7 +26,14 @@ object MountableBehavior {
val obj = MountableObject val obj = MountableObject
obj.Seat(seat_num) match { obj.Seat(seat_num) match {
case Some(seat) => case Some(seat) =>
if(user.Faction == obj.Faction && (seat.Occupant = user).contains(user)) {
var isHacked = false
if(MountableObject.isInstanceOf[Hackable]) {
// This is a special case for implant terminals, since they're both mountable and hackable, but not jackable.
isHacked = MountableObject.asInstanceOf[Hackable].HackedBy.isDefined
}
if((user.Faction == obj.Faction || isHacked) && (seat.Occupant = user).contains(user)) {
user.VehicleSeated = obj.GUID user.VehicleSeated = obj.GUID
sender ! Mountable.MountMessages(user, Mountable.CanMount(obj, seat_num)) sender ! Mountable.MountMessages(user, Mountable.CanMount(obj, seat_num))
} }

View file

@ -4,18 +4,16 @@ package net.psforever.objects.serverobject.terminals
import akka.actor.Actor import akka.actor.Actor
import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.hackable.HackableBehavior
class CaptureTerminalControl(terminal : CaptureTerminal) extends Actor with FactionAffinityBehavior.Check { class CaptureTerminalControl(terminal : CaptureTerminal) extends Actor with FactionAffinityBehavior.Check with HackableBehavior.GenericHackable {
def FactionObject : FactionAffinity = terminal def FactionObject : FactionAffinity = terminal
def HackableObject = terminal
def receive : Receive = checkBehavior.orElse { def receive : Receive = checkBehavior
case CommonMessages.Hack(player) => .orElse(hackableBehavior)
terminal.HackedBy = player .orElse {
sender ! true
case CommonMessages.ClearHack() =>
terminal.HackedBy = None
case _ => ; //no default message case _ => ; //no default message
} }
} }

View file

@ -5,6 +5,7 @@ import akka.actor.{Actor, ActorRef, Cancellable}
import net.psforever.objects._ import net.psforever.objects._
import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.hackable.HackableBehavior
import services.{Service, ServiceManager} import services.{Service, ServiceManager}
import scala.collection.mutable import scala.collection.mutable
@ -16,13 +17,14 @@ import scala.concurrent.duration._
* it returns the same type of messages - wrapped in a `TerminalMessage` - to the `sender`. * it returns the same type of messages - wrapped in a `TerminalMessage` - to the `sender`.
* @param term the proximity unit (terminal) * @param term the proximity unit (terminal)
*/ */
class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor with FactionAffinityBehavior.Check { class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor with FactionAffinityBehavior.Check with HackableBehavior.GenericHackable {
var service : ActorRef = ActorRef.noSender var service : ActorRef = ActorRef.noSender
var terminalAction : Cancellable = DefaultCancellable.obj var terminalAction : Cancellable = DefaultCancellable.obj
val callbacks : mutable.ListBuffer[ActorRef] = new mutable.ListBuffer[ActorRef]() val callbacks : mutable.ListBuffer[ActorRef] = new mutable.ListBuffer[ActorRef]()
val log = org.log4s.getLogger val log = org.log4s.getLogger
def FactionObject : FactionAffinity = term def FactionObject : FactionAffinity = term
def HackableObject = term
def TerminalObject : Terminal with ProximityUnit = term def TerminalObject : Terminal with ProximityUnit = term
@ -41,7 +43,8 @@ class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor
} }
def Run : Receive = checkBehavior def Run : Receive = checkBehavior
.orElse { .orElse(hackableBehavior)
.orElse {
case CommonMessages.Use(_, Some(target : PlanetSideGameObject)) => case CommonMessages.Use(_, Some(target : PlanetSideGameObject)) =>
if(term.Definition.asInstanceOf[ProximityDefinition].Validations.exists(p => p(target))) { if(term.Definition.asInstanceOf[ProximityDefinition].Validations.exists(p => p(target))) {
Use(target, term.Continent, sender) Use(target, term.Continent, sender)
@ -79,13 +82,6 @@ class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor
} }
}) })
case CommonMessages.Hack(player) =>
term.HackedBy = player
sender ! true
case CommonMessages.ClearHack() =>
term.HackedBy = None
case ProximityUnit.Action(_, _) => case ProximityUnit.Action(_, _) =>
//reserved //reserved

View file

@ -4,26 +4,24 @@ package net.psforever.objects.serverobject.terminals
import akka.actor.Actor import akka.actor.Actor
import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior}
import net.psforever.objects.serverobject.hackable.HackableBehavior
/** /**
* An `Actor` that handles messages being dispatched to a specific `Terminal`. * An `Actor` that handles messages being dispatched to a specific `Terminal`.
* @param term the `Terminal` object being governed * @param term the `Terminal` object being governed
*/ */
class TerminalControl(term : Terminal) extends Actor with FactionAffinityBehavior.Check { class TerminalControl(term : Terminal) extends Actor with FactionAffinityBehavior.Check with HackableBehavior.GenericHackable {
def FactionObject : FactionAffinity = term def FactionObject : FactionAffinity = term
def HackableObject = term
def receive : Receive = checkBehavior.orElse { def receive : Receive = checkBehavior
case Terminal.Request(player, msg) => .orElse(hackableBehavior)
sender ! Terminal.TerminalMessage(player, msg, term.Request(player, msg)) .orElse {
case Terminal.Request(player, msg) =>
sender ! Terminal.TerminalMessage(player, msg, term.Request(player, msg))
case CommonMessages.Hack(player) => case _ => ;
term.HackedBy = player }
sender ! true
case CommonMessages.ClearHack() =>
term.HackedBy = None
case _ => ;
}
override def toString : String = term.Definition.Name override def toString : String = term.Definition.Name
} }

View file

@ -3673,6 +3673,23 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.info(s"UseItem: not $player's locker") log.info(s"UseItem: not $player's locker")
} }
case Some(implant_terminal : ImplantTerminalMech) =>
if(implant_terminal.Faction != player.Faction && implant_terminal.HackedBy.isEmpty) {
player.Slot(player.DrawnSlot).Equipment match {
case Some(tool: SimpleItem) =>
if (tool.Definition == GlobalDefinitions.remote_electronics_kit) {
val hackSpeed = GetPlayerHackSpeed(implant_terminal)
if(hackSpeed > 0) {
progressBarValue = Some(-hackSpeed)
self ! WorldSessionActor.HackingProgress(progressType = 1, player, implant_terminal, tool.GUID, hackSpeed, FinishHacking(implant_terminal, 3212836864L))
log.info("Hacking an implant terminal")
}
}
case _ => ;
}
}
case Some(captureTerminal : CaptureTerminal) => case Some(captureTerminal : CaptureTerminal) =>
val hackedByCurrentFaction = (captureTerminal.Faction != player.Faction && !captureTerminal.HackedBy.isEmpty && captureTerminal.HackedBy.head._1.Faction == player.Faction) val hackedByCurrentFaction = (captureTerminal.Faction != player.Faction && !captureTerminal.HackedBy.isEmpty && captureTerminal.HackedBy.head._1.Faction == player.Faction)
val ownedByPlayerFactionAndHackedByEnemyFaction = (captureTerminal.Faction == player.Faction && !captureTerminal.HackedBy.isEmpty) val ownedByPlayerFactionAndHackedByEnemyFaction = (captureTerminal.Faction == player.Faction && !captureTerminal.HackedBy.isEmpty)