stop reporting the origin of a projectile as a valid hit position; creating pipelines for either damage proxy management and no damage proxy management

This commit is contained in:
Fate-JH 2025-12-08 17:38:57 -05:00
parent b02de0f80a
commit 01c014a59c
3 changed files with 65 additions and 37 deletions

View file

@ -127,14 +127,14 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
val list = ops.composeDirectDamageInformation(pkt)
if (!player.spectator) {
list.foreach {
case (target, projectile, _, _) =>
ops.resolveProjectileInteraction(target, projectile, DamageResolution.Hit, target.Position)
case (target, projectile, _, targetPos) =>
ops.resolveProjectileInteractionAndProxy(target, projectile, DamageResolution.Hit, targetPos)
}
//...
if (list.isEmpty) {
ops.handleProxyDamage(pkt.projectile_guid, pkt.hit_info.map(_.hit_pos).getOrElse(Vector3.Zero)).foreach {
case (target, proxy, hitPos, _) =>
ops.resolveProjectileInteraction(target, proxy, DamageResolution.Hit, hitPos)
case (target, proxy, _, targetPos) =>
ops.resolveProjectileInteraction(target, proxy, DamageResolution.Hit, targetPos)
}
}
}
@ -157,12 +157,12 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
//...
val (direct, others) = list.partition { case (_, _, hitPos, targetPos) => hitPos == targetPos }
direct.foreach {
case (target, _, _, _) =>
ops.resolveProjectileInteraction(target, projectile, resolution1, target.Position)
case (target, _, _, targetPos) =>
ops.resolveProjectileInteractionAndProxy(target, projectile, resolution1, targetPos)
}
others.foreach {
case (target, _, _, _) =>
ops.resolveProjectileInteraction(target, projectile, resolution2, target.Position)
case (target, _, _, targetPos) =>
ops.resolveProjectileInteraction(target, projectile, resolution2, targetPos)
}
//...
if (
@ -183,11 +183,11 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
if (profile.ExistsOnRemoteClients && projectile.HasGUID) {
continent.Projectile ! ZoneProjectile.Remove(projectileGuid)
}
}
//...
ops.handleProxyDamage(pkt.projectile_uid, pkt.projectile_pos).foreach {
case (target, proxy, hitPos, _) =>
ops.resolveProjectileInteraction(target, proxy, DamageResolution.Splash, hitPos)
} else {
ops.handleProxyDamage(pkt.projectile_uid, pkt.projectile_pos).foreach {
case (target, proxy, _, targetPos) =>
ops.resolveProjectileInteraction(target, proxy, DamageResolution.Splash, targetPos)
}
}
}
}

View file

@ -144,9 +144,9 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
val projectileGuid = pkt.projectile_guid
val list = ops.composeDirectDamageInformation(pkt)
.collect {
case (target, projectile, hitPos, _) =>
ops.checkForHitPositionDiscrepancy(projectileGuid, hitPos, target)
ops.resolveProjectileInteraction(target, projectile, DamageResolution.Hit, hitPos)
case (target, projectile, hitPos, targetPos) =>
ops.checkForHitPositionDiscrepancy(projectileGuid, hitPos, targetPos)
ops.resolveProjectileInteractionAndProxy(target, projectile, DamageResolution.Hit, hitPos)
projectile
}
//...
@ -174,9 +174,9 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
//...
val (direct, others) = list.partition { case (_, _, hitPos, targetPos) => hitPos == targetPos }
direct.foreach {
case (target, _, hitPos, _) =>
ops.checkForHitPositionDiscrepancy(projectileGuid, hitPos, target)
ops.resolveProjectileInteraction(target, projectile, resolution1, hitPos)
case (target, _, hitPos, targetPos) =>
ops.checkForHitPositionDiscrepancy(projectileGuid, hitPos, targetPos)
ops.resolveProjectileInteractionAndProxy(target, projectile, resolution1, hitPos)
}
others.foreach {
case (target, _, hitPos, _) =>
@ -202,19 +202,19 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
//cleanup
continent.Projectile ! ZoneProjectile.Remove(projectile.GUID)
}
}
//...
ops.handleProxyDamage(pkt.projectile_uid, pkt.projectile_pos).foreach {
case (target, proxy, hitPos, _) =>
ops.resolveProjectileInteraction(target, proxy, DamageResolution.Splash, hitPos)
} else {
ops.handleProxyDamage(pkt.projectile_uid, pkt.projectile_pos).foreach {
case (target, proxy, hitPos, _) =>
ops.resolveProjectileInteraction(target, proxy, DamageResolution.Splash, hitPos)
}
}
}
def handleLashHit(pkt: LashMessage): Unit = {
val list = ops.composeLashDamageInformation(pkt)
list.foreach {
case (target, projectile, hitPos, _) =>
ops.checkForHitPositionDiscrepancy(projectile.GUID, hitPos, target)
case (target, projectile, hitPos, targetPos) =>
ops.checkForHitPositionDiscrepancy(projectile.GUID, hitPos, targetPos)
ops.resolveProjectileInteraction(target, projectile, DamageResolution.Lash, hitPos)
}
}
@ -223,8 +223,8 @@ class WeaponAndProjectileLogic(val ops: WeaponAndProjectileOperations, implicit
val list = ops.composeAIDamageInformation(pkt)
if (ops.confirmAIDamageTarget(pkt, list.map(_._1))) {
list.foreach {
case (target, projectile, hitPos, _) =>
ops.checkForHitPositionDiscrepancy(pkt.attacker_guid, hitPos, target)
case (target, projectile, hitPos, targetPos) =>
ops.checkForHitPositionDiscrepancy(pkt.attacker_guid, hitPos, targetPos)
ops.resolveProjectileInteraction(target, projectile, DamageResolution.Hit, hitPos)
}
}

View file

@ -513,6 +513,7 @@ class WeaponAndProjectileOperations(
hit_info match {
case Some(hitInfo) =>
val hitPos = hitInfo.hit_pos
projectile.Position = hitPos
sessionLogic.validObject(hitInfo.hitobject_guid, decorator = "Hit/hitInfo") match {
case _ if projectile.profile == GlobalDefinitions.flail_projectile =>
val radius = projectile.profile.DamageRadius * projectile.profile.DamageRadius
@ -522,7 +523,7 @@ class WeaponAndProjectileOperations(
.map(target => (target, projectile, hitPos, target.Position))
case Some(target: PlanetSideGameObject with FactionAffinity with Vitality) =>
List((target, projectile, hitInfo.shot_origin, hitPos))
List((target, projectile, hitPos, target.Position))
case None =>
Nil
@ -585,7 +586,9 @@ class WeaponAndProjectileOperations(
FindProjectileEntry(projectile_guid)
.flatMap {
projectile =>
sessionLogic
//projectile may still be moving, and may lash other targets in the future when in a different position
projectile.Position = hit_pos
sessionLogic
.validObject(victim_guid, decorator = "LashHit/victim_guid")
.collect {
case target: PlanetSideGameObject with FactionAffinity with Vitality =>
@ -826,6 +829,26 @@ class WeaponAndProjectileOperations(
context.system.scheduler.scheduleOnce(500.milliseconds) { explosionFunc() }
}
/**
* Find a projectile with the given globally unique identifier and mark it as a resolved shot.
* A `Resolved` shot has either encountered an obstacle or is being cleaned up for not finding an obstacle.
* Check if we are required to deal with damage proxy management as well.
* @param projectile projectile
* @param resolution resolution status to promote the projectile
* @return package that contains information about the damage
*/
def resolveProjectileInteractionAndProxy(
target: PlanetSideGameObject with FactionAffinity with Vitality,
projectile: Projectile,
resolution: DamageResolution.Value,
hitPosition: Vector3
): Option[DamageInteraction] = {
if (projectile.profile.DamageProxyOnDirectHit.exists(_.test(target))) {
handleProxyDamage(projectile, hitPosition)
}
resolveProjectileInteraction(target, projectile, resolution, hitPosition)
}
/**
* Find a projectile with the given globally unique identifier and mark it as a resolved shot.
* A `Resolved` shot has either encountered an obstacle or is being cleaned up for not finding an obstacle.
@ -842,9 +865,6 @@ class WeaponAndProjectileOperations(
if (projectile.isMiss) {
None
} else {
if (projectile.profile.DamageProxyOnDirectHit.exists(_.test(target))) {
handleProxyDamage(projectile, hitPosition)
}
val outProjectile = ProjectileQuality.modifiers(projectile, resolution, target, hitPosition, Some(player))
if (projectile.tool_def.Size == EquipmentSize.Melee && outProjectile.quality == ProjectileQuality.Modified(25)) {
avatarActor ! AvatarActor.ConsumeStamina(10)
@ -1462,15 +1482,23 @@ class WeaponAndProjectileOperations(
}
def checkForHitPositionDiscrepancy(
projectile_guid: PlanetSideGUID,
hitPos: Vector3,
projectileGuid: PlanetSideGUID,
hitPosition: Vector3,
target: PlanetSideGameObject with Vitality
): Unit = {
val hitPositionDiscrepancy = Vector3.DistanceSquared(hitPos, target.Position)
checkForHitPositionDiscrepancy(projectileGuid, hitPosition, target.Position)
}
def checkForHitPositionDiscrepancy(
projectileGuid: PlanetSideGUID,
hitPosition: Vector3,
targetPosition: Vector3
): Unit = {
val hitPositionDiscrepancy = Vector3.DistanceSquared(hitPosition, targetPosition)
if (hitPositionDiscrepancy > Config.app.antiCheat.hitPositionDiscrepancyThreshold) {
// If the target position on the server does not match the position where the projectile landed within reason there may be foul play
log.warn(
s"${player.Name}'s shot #${projectile_guid.guid} has hit discrepancy with target. Target: ${target.Position}, Reported: $hitPos, Distance: $hitPositionDiscrepancy / ${math.sqrt(hitPositionDiscrepancy).toFloat}; suspect"
s"${player.Name}'s shot #${projectileGuid.guid} has hit discrepancy with target. Target: $targetPosition, Reported: $hitPosition, Distance: $hitPositionDiscrepancy / ${math.sqrt(hitPositionDiscrepancy).toFloat}; suspect"
)
}
}