secondary queue that keeps track of the previous test shot subjects when none have been tested, allowing for a packet to be skipped during subsequent test shots

This commit is contained in:
Fate-JH 2024-01-27 23:09:17 -05:00
parent 1ff0577db7
commit e6ec5b1ee4
3 changed files with 100 additions and 19 deletions

View file

@ -24,7 +24,7 @@ import net.psforever.objects.serverobject.resourcesilo.ResourceSiloDefinition
import net.psforever.objects.serverobject.structures.{AmenityDefinition, 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.{Automation, AutoChecks, FacilityTurretDefinition, AutoRanges, TurretUpgrade}
import net.psforever.objects.serverobject.turret.{AutoChecks, AutoCooldowns, AutoRanges, Automation, FacilityTurretDefinition, TurretUpgrade}
import net.psforever.objects.vehicles.{DestroyedVehicle, InternalTelepadDefinition, UtilityType, VehicleSubsystemEntry}
import net.psforever.objects.vital.base.DamageType
import net.psforever.objects.vital.damage._
@ -9128,7 +9128,13 @@ object GlobalDefinitions {
EffectTarget.Validation.AircraftDetectedByAutoTurret
)
),
cooldowns = AutoCooldowns(
targetSelect = 0L,
missedShot = 0L
),
detectionSweepTime = 500.milliseconds,
retaliatoryDelay = 1L, //8000L
retaliationOverridesTarget = false,
refireTime = 200.milliseconds //150.milliseconds
)
spitfire_cloaked.innateDamage = new DamageWithPosition {

View file

@ -224,6 +224,10 @@ case class Avatar(
false
}
override def hashCode(): Int = {
id
}
/** Avatar assertions
* These protect against programming errors by asserting avatar properties have correct values
* They may or may not be disabled for live applications

View file

@ -16,6 +16,7 @@ import net.psforever.objects.zones.{InteractsWithZone, Zone}
import net.psforever.objects.{Default, PlanetSideGameObject, Player, Vehicle}
import net.psforever.types.{PlanetSideGUID, Vector3}
import scala.annotation.unused
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
@ -37,15 +38,18 @@ trait AutomatedTurretBehavior {
/** timer managing the available target qualifications test
* whether or not a previously valid target is still a valid target */
private var periodicValidationTest: Cancellable = Default.Cancellable
/** targets that have been the subject of test shots just recently;
* emptied when switching from the test shot cycle to actually selecting a target */
private var previouslyTestedTargets: Seq[SourceUniqueness] = Seq[SourceUniqueness]()
/** timer managing the trailing target qualifications self test
* where the a source will shot directly at some target */
* where the source will shoot directly at some target
* expecting a response in return */
private var selfReportedRefire: Cancellable = Default.Cancellable
/** self-reported weapon fire produces projectiles that were shot;
* due to the call and response nature of this mode, they also count as shots that were landed */
private var shotsFired: Int = 0
/** self-reported weapon fire produces targets that were eliminated;
* due to the call and response nature of this mode, they also count as shots that were landed;
* this may duplicate information processed during some other database update call */
private var targetsDestroyed: Int = 0
@ -91,6 +95,7 @@ trait AutomatedTurretBehavior {
if (!previousState && state) {
trySelectNewTarget()
} else if (previousState && !state) {
previouslyTestedTargets = Seq()
cancelSelfReportedAutoFire()
AutomatedTurretObject.Target.foreach(noLongerEngageDetectedTarget)
}
@ -159,7 +164,7 @@ trait AutomatedTurretBehavior {
AutomatedTurretObject.Detected(target)
.collect { out =>
AutomatedTurretObject.RemoveTarget(target)
testTargetListQualifications(size)
testTargetQualificationsForOngoingChecks(size)
out
}
.flatMap {
@ -179,6 +184,7 @@ trait AutomatedTurretBehavior {
AutomatedTurretObject.Clear()
currentTargetToken = None
currentTargetLocation = None
previouslyTestedTargets = Seq()
}
/* Normal automated turret behavior */
@ -198,6 +204,7 @@ trait AutomatedTurretBehavior {
if (currentTargetToken.isEmpty) {
currentTargetLastShotTime = now
currentTargetLocation = Some(target.Position)
previouslyTestedTargets = Seq()
cancelSelfReportedAutoFire()
engageNewDetectedTarget(target)
true
@ -289,7 +296,8 @@ trait AutomatedTurretBehavior {
* Explicitly order certain unrepresented targets to stop being tested
* in case the packets between the server and the client do not get transmitted properly
* or the turret is not assembled correctly in its automatic fire definition.
* @return something the turret can potentially shoot at
* @return something the turret can potentially shoot at;
* it doesn't really matter which something is returned but, rather, if anything is returned
*/
protected def trySelectNewTarget(): Option[Target] = {
AutomatedTurretObject.Target.orElse {
@ -302,13 +310,21 @@ trait AutomatedTurretBehavior {
val selectedTargets = AutomatedTurretObject
.Targets
.collect { case target
if /*target.Faction != faction &&*/
if target.Faction != faction &&
AutomatedTurretBehavior.shapedDistanceCheckAgainstValue(autoStats, target.Position, turretPosition, radius, result = -1) &&
validation.exists(func => func(target))=>
validation.exists(func => func(target)) =>
target
}
.sortBy(target => Vector3.DistanceSquared(target.Position, turretPosition))
selectedTargets.foreach(processForTestingDetectedTarget(_, turretGuid, weaponGuid))
val (previousTargets, newTargets) = selectedTargets.partition(target => previouslyTestedTargets.contains(SourceEntry(target).unique))
val newTargetsFunc: Iterable[(Target, (Target, PlanetSideGUID, PlanetSideGUID) => Option[(Target, Target)])] =
newTargets.map(target => (target, processForTestingNewDetectedTarget))
val previousTargetsFunc: Iterable[(Target, (Target, PlanetSideGUID, PlanetSideGUID) => Option[(Target, Target)])] =
previousTargets.map(target => (target, processForTestingKnownDetectedTarget))
previouslyTestedTargets = (newTargetsFunc ++ previousTargetsFunc)
.toSeq
.sortBy { case (target, _) => Vector3.DistanceSquared(target.Position, turretPosition) }
.flatMap { case (target, func) => func(target, turretGuid, weaponGuid) }
.map { case (target, _) => SourceEntry(target).unique }
selectedTargets.headOption
}
}
@ -324,11 +340,11 @@ trait AutomatedTurretBehavior {
* something the turret can potentially shoot at
* something that will report whether the test shot struck the target
*/
private def processForTestingDetectedTarget(
target: Target,
turretGuid: PlanetSideGUID,
weaponGuid: PlanetSideGUID
): Option[(Target, Target)] = {
private def processForTestingNewDetectedTarget(
target: Target,
turretGuid: PlanetSideGUID,
weaponGuid: PlanetSideGUID
): Option[(Target, Target)] = {
target match {
case target: Player =>
AutomatedTurretDispatch.Generic.testNewDetected(target, target.Name, turretGuid, weaponGuid)
@ -345,6 +361,57 @@ trait AutomatedTurretBehavior {
}
}
/**
* Dispatch packets in the direction of a client perspective
* to determine if this target can be reliably struck with a projectile from the turret's weapon.
* This resolves to a player avatar entity usually and is communicated on that player's personal name channel.
* @param target something the turret can potentially shoot at
* @param turretGuid not used
* @param weaponGuid turret's weapon
* @return a tuple composed of:
* something the turret can potentially shoot at
* something that will report whether the test shot struck the target
*/
private def processForTestingKnownDetectedTarget(
target: Target,
@unused turretGuid: PlanetSideGUID,
weaponGuid: PlanetSideGUID
): Option[(Target, Target)] = {
processForTestingKnownDetectedTarget(target, weaponGuid)
}
/**
* Dispatch packets in the direction of a client perspective
* to determine if this target can be reliably struck with a projectile from the turret's weapon.
* This resolves to a player avatar entity usually and is communicated on that player's personal name channel.
* @param target something the turret can potentially shoot at
* @param weaponGuid turret's weapon
* @return a tuple composed of:
* something the turret can potentially shoot at
* something that will report whether the test shot struck the target
*/
private def processForTestingKnownDetectedTarget(
target: Target,
weaponGuid: PlanetSideGUID
): Option[(Target, Target)] = {
target match {
case target: Player =>
AutomatedTurretDispatch.Generic.startShooting(target, target.Name, weaponGuid)
AutomatedTurretDispatch.Generic.stopShooting(target, target.Name, weaponGuid)
Some((target, target))
case target: Vehicle =>
target.Seats.values
.flatMap(_.occupants)
.collectFirst { passenger =>
AutomatedTurretDispatch.Generic.startShooting(passenger, passenger.Name, weaponGuid)
AutomatedTurretDispatch.Generic.stopShooting(passenger, passenger.Name, weaponGuid)
(target, passenger)
}
case _ =>
None
}
}
/**
* Cull all targets that have been detected by this turret at some point
* by determining which targets are either destroyed
@ -356,7 +423,7 @@ trait AutomatedTurretBehavior {
val size = AutomatedTurretObject.Targets.size
val list = performDistanceCheck()
performCurrentTargetDecayCheck()
testTargetListQualifications(size)
testTargetQualificationsForOngoingChecks(size)
list
}
@ -465,12 +532,15 @@ trait AutomatedTurretBehavior {
* If there are no available targets,
* and no current target,
* stop the evaluation of available targets.
* @param beforeSize size of the list of available targets before some operation took place
* @param beforeListSize size of the list of available targets before some operation took place
* @return `true`, if the evaluation of available targets was stopped;
* `false`, otherwise
*/
private def testTargetListQualifications(beforeSize: Int): Boolean = {
beforeSize > 0 && AutomatedTurretObject.Targets.isEmpty && cancelPeriodicTargetChecks()
private def testTargetQualificationsForOngoingChecks(beforeListSize: Int): Boolean = {
beforeListSize > 0 &&
AutomatedTurretObject.Targets.isEmpty &&
AutomatedTurretObject.Target.isEmpty &&
cancelPeriodicTargetChecks()
}
/**
@ -512,6 +582,7 @@ trait AutomatedTurretBehavior {
* @see `Default.Cancellable`
*/
private def cancelPeriodicTargetChecks(): Boolean = {
previouslyTestedTargets = Seq()
periodicValidationTest.cancel()
periodicValidationTest = Default.Cancellable
true
@ -620,7 +691,7 @@ trait AutomatedTurretBehavior {
if (currentTargetLocation.exists(loc => Vector3.DistanceSquared(loc, target.Position) > 1f)) {
cancelSelfReportedAutoFire()
noLongerEngageDetectedTarget(target)
processForTestingDetectedTarget(
processForTestingNewDetectedTarget(
target,
AutomatedTurretObject.GUID,
AutomatedTurretObject.Weapons.values.head.Equipment.get.GUID