ward against spawning multiple LLU's during normal/wrested base capture

This commit is contained in:
Jason_DiDonato@yahoo.com 2021-05-18 13:59:50 -04:00
parent 2a5c50c9ec
commit afb1f4b279
4 changed files with 111 additions and 65 deletions

View file

@ -11,7 +11,19 @@ import net.psforever.types.Vector3
* It is used as a position reference for spawning the LLU in the correct location when the base is hacked
* @param tDef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
*/
class CaptureFlagSocket(tDef: CaptureFlagSocketDefinition) extends Amenity {
class CaptureFlagSocket(tDef: CaptureFlagSocketDefinition)
extends Amenity {
private var spawnedCaptureFlag: Option[CaptureFlag] = None
def captureFlag: Option[CaptureFlag] = spawnedCaptureFlag
def captureFlag_=(flag: CaptureFlag): Option[CaptureFlag] = captureFlag_=(Some(flag))
def captureFlag_=(flag: Option[CaptureFlag]): Option[CaptureFlag] = {
spawnedCaptureFlag = flag
captureFlag
}
def Definition : CaptureFlagSocketDefinition = tDef
}

View file

@ -129,7 +129,12 @@ class Building(
}
def GetFlagSocket: Option[CaptureFlagSocket] = this.Amenities.find(_.Definition == GlobalDefinitions.llm_socket).asInstanceOf[Option[CaptureFlagSocket]]
def GetFlag: Option[CaptureFlag] = this.Amenities.find(_.Definition == GlobalDefinitions.capture_flag).asInstanceOf[Option[CaptureFlag]]
def GetFlag: Option[CaptureFlag] = {
GetFlagSocket match {
case Some(socket) => socket.captureFlag
case None => None
}
}
def HackableAmenities: List[Amenity with Hackable] = {
Amenities.filter(x => x.isInstanceOf[Hackable]).map(x => x.asInstanceOf[Amenity with Hackable])

View file

@ -102,6 +102,7 @@ class CaptureFlagManager(val taskResolver: ActorRef, zone: Zone) extends Actor{
)
// Add the flag as an amenity and track it internally
socket.captureFlag = flag
TrackFlag(flag)
taskResolver ! CallBackForTask(
@ -128,15 +129,19 @@ class CaptureFlagManager(val taskResolver: ActorRef, zone: Zone) extends Actor{
HandleFlagDespawn(flag)
case CaptureFlagManager.Lost(flag: CaptureFlag, reason: CaptureFlagLostReasonEnum) =>
val message = reason match {
reason match {
case CaptureFlagLostReasonEnum.Resecured =>
CaptureFlagChatMessageStrings.CTF_Failed_SourceResecured(flag.Owner.asInstanceOf[Building])
case CaptureFlagLostReasonEnum.TimedOut =>
CaptureFlagChatMessageStrings.CTF_Failed_TimedOut(flag.Owner.asInstanceOf[Building].Name, flag.Target)
ChatBroadcast(
flag.Zone,
CaptureFlagChatMessageStrings.CTF_Failed_SourceResecured(flag.Owner.asInstanceOf[Building])
)
case CaptureFlagLostReasonEnum.TimedOut =>
ChatBroadcast(
flag.Zone,
CaptureFlagChatMessageStrings.CTF_Failed_TimedOut(flag.Owner.asInstanceOf[Building].Name, flag.Target)
)
case CaptureFlagLostReasonEnum.Ended => ; /* no message */
}
ChatBroadcast(flag.Zone, message)
HandleFlagDespawn(flag)
case CaptureFlagManager.PickupFlag(flag: CaptureFlag, player: Player) =>
@ -173,14 +178,13 @@ class CaptureFlagManager(val taskResolver: ActorRef, zone: Zone) extends Actor{
}
private def HandleFlagDespawn(flag: CaptureFlag): Unit = {
// Remove the flag as an amenity
flag.Owner.asInstanceOf[Building].GetFlagSocket.get.captureFlag = None
UntrackFlag(flag)
// Unregister LLU from clients,
flag.Zone.LocalEvents ! LocalServiceMessage(flag.Zone.id, LocalAction.LluDespawned(PlanetSideGUID(-1), flag))
// Then unregister it from the GUID pool
taskResolver ! TaskResolver.GiveTask(GUIDTask.UnregisterObjectTask(flag)(flag.Zone.GUID).task)
// Remove the flag as an amenity
UntrackFlag(flag)
}
private def ChatBroadcast(zone: Zone, message: String, fanfare: Boolean = true): Unit = {
@ -271,6 +275,7 @@ object CaptureFlagChatMessageStrings {
case PlanetSideEmpire.TR => "TerranRepublic"
case PlanetSideEmpire.NC => "NewConglomerate"
case PlanetSideEmpire.VS => "VanuSovereigncy" // Yes, this is wrong. It is like that in packet captures.
case _ => "TerranRepublic" //todo: BO message?
}
}
}
@ -278,5 +283,5 @@ object CaptureFlagChatMessageStrings {
object CaptureFlagLostReasonEnum extends Enumeration {
type CaptureFlagLostReasonEnum = Value
val Resecured, TimedOut = Value
val Resecured, TimedOut, Ended = Value
}

View file

@ -31,42 +31,36 @@ class HackCaptureActor(val taskResolver: ActorRef) extends Actor {
def receive: Receive = {
case HackCaptureActor.StartCaptureTerminalHack(target, zone, unk1, unk2, startTime) =>
log.trace(s"StartCaptureTerminalHack: ${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)
case None =>
log.error(s"Initial $target hack information is missing")
}
hackedObjects.find(_.target == target) match {
case Some(_) =>
log.trace(
s"StartCaptureTerminalHack: ${target.GUID} was already hacked - removing it from the hacked objects list before re-adding it."
)
hackedObjects = hackedObjects.filterNot(x => x.target == target)
case _ => ;
}
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()
NotifyHackStateChange(target, isResecured = false)
TrySpawnCaptureFlag(target)
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)
case None =>
log.error(s"Initial $target hack information is missing")
}
hackedObjects.find(_.target == target) match {
case Some(_) =>
log.trace(
s"StartCaptureTerminalHack: ${target.GUID} was already hacked - removing it from the hacked objects list before re-adding it."
)
hackedObjects = hackedObjects.filterNot(x => x.target == target)
case _ => ;
}
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()
NotifyHackStateChange(target, isResecured = false)
TrySpawnCaptureFlag(target)
case HackCaptureActor.ProcessCompleteHacks() =>
log.trace("Processing complete hacks")
@ -134,27 +128,57 @@ class HackCaptureActor(val taskResolver: ActorRef) extends Actor {
case _ => ;
}
private def TrySpawnCaptureFlag(terminal: CaptureTerminal): Unit = {
private def TrySpawnCaptureFlag(terminal: CaptureTerminal): Boolean = {
// Handle LLUs if the base contains a LLU socket
// If there are no neighbouring bases belonging to the hacking faction this will be handled as a regular timed hack (e.g. neutral base in enemy territory)
val owner = terminal.Owner.asInstanceOf[Building]
val hackingFaction = HackCaptureActor.GetHackingFaction(terminal).get
val hackingFactionNeighbourBases = owner.Neighbours(hackingFaction)
hackingFactionNeighbourBases match {
case Some(neighbours) =>
if(owner.IsCtfBase) {
// Find a random neighbouring base matching the hacking faction
val targetBase = neighbours.toVector((new Random).nextInt(neighbours.size))
// Request LLU is created by CaptureFlagActor via LocalService
terminal.Zone.LocalEvents ! CaptureFlagManager.SpawnCaptureFlag(terminal, targetBase, hackingFaction)
terminal.Owner match {
case owner: Building if owner.IsCtfBase =>
val socket = owner.GetFlagSocket.get
val flag = socket.captureFlag
val owningFaction = owner.Faction
val hackingFaction = HackCaptureActor.GetHackingFaction(terminal).get
owner.Neighbours(hackingFaction) match {
case Some(neighbours) =>
if (flag.isEmpty) {
log.info(s"An LLU is being spawned for facility ${owner.Name} by $hackingFaction")
spawnCaptureFlag(neighbours, terminal, hackingFaction)
true
} else if (hackingFaction != flag.get.Faction) {
log.info(s"$hackingFaction is overriding the ongoing LLU hack of facility ${owner.Name} by ${flag.get.Faction}")
terminal.Zone.LocalEvents ! CaptureFlagManager.Lost(flag.get, CaptureFlagLostReasonEnum.Ended)
NotifyHackStateChange(terminal, isResecured = false)
RestartTimer()
spawnCaptureFlag(neighbours, terminal, hackingFaction)
true
} else if (hackingFaction == owningFaction) {
log.error(s"Owning faction and hacking faction match for facility ${owner.Name}; should we be resecuring instead?")
false
} else {
log.warn(s"LLU hack of facility ${owner.Name} by $hackingFaction in progress - no change")
false
}
case None => ;
log.info(s"Couldn't find any neighbouring $hackingFaction facilities of ${owner.Name} for LLU hack")
false
}
case None =>
log.info("Couldn't find any neighbouring bases for LLU hack.")
case thing =>
log.error(s"Capture terminal has unexpected owner - $thing - that is not a facility")
false
}
}
private def spawnCaptureFlag(
neighbours: Set[Building],
terminal: CaptureTerminal,
hackingFaction: PlanetSideEmpire.Value
): Unit = {
// Find a random neighbouring base matching the hacking faction
val targetBase = neighbours.toVector((new Random).nextInt(neighbours.size))
// Request LLU is created by CaptureFlagActor via LocalService
terminal.Zone.LocalEvents ! CaptureFlagManager.SpawnCaptureFlag(terminal, targetBase, hackingFaction)
}
private def NotifyHackStateChange(terminal: CaptureTerminal, isResecured: Boolean): Unit = {
val attribute_value = HackCaptureActor.GetHackUpdateAttributeValue(terminal, isResecured)