Is Lyra doing bullet hit detection incorrectly/incompletely?

I see Lyra lauded as the “correct way” to do bullet detection but I am finding some issues. Please correct me if I am wrong because I am still quite new to the GAS.

Here is how I see the bullet detection happening:

  • Ability Activates

  • ActivateAbility happens on CLIENT

    • Gets the return value of ASC->AbilityTargetDataSetDelegate, giving it the ability spec handle and current activation key, which is bound to OnTargetDataReadyCallback. The handle is cached for later removal.
  • StartRangedWeaponTargeting happens only on the local player

    • Performs LOCAL targeting.
      • Calls TraceBulletsInCartridge
        • Calls DoSingleBulletTrace, for BulletsPerCartridge (int).
    • For each hit that was locally targeted, dynamically allocate memory for a FLyraGameplayAbilityTargetData_SingleTargetHit and add it to TargetData.
    • Calls AddUnconfirmedServerSideHitMarkers
      • All hits are added to a FLyraServerSideHitMarkerBatch which is inserted into UnconfirmedServerSideHitMarkers.
    • Calls OnTargetDataReadyCallback with the created TargetData.
  • OnTargetDataReadyCallback happens on CLIENT

    • ONLY Client calls ASC->CallServerSetReplicatedTargetData, which sends an RPC to the server to replicate the target data.
  • Event On Ranged Weapon Target Data Ready happens on CLIENT (in blueprint)

  • ActivateAbility happens on SERVER, same stuff happens

  • OnTargetDataReadyCallback happens on SERVER, because of the client’s earlier RPC, and having bound this function in ActivateAbility.

    • ONLY the server checks each hit in the target data and replaces the hits if invalid.
      • The hits are NEVER invalid because of const bool bIsTargetDataValid = true; above it, and from what I can see hits are never replaced because you have to call ReplaceHitWith on the target data, which never happens.
    • The server calls ClientConfirmTargetData, which asks the client to remove all hits from the UnconfirmedServerSideHitMarkers.
  • Event On Ranged Weapon Target Data Ready happens on SERVER (in blueprint)

    • Server passes a HasAuthority node and inflicts damage with ApplyGameplayEffectToTarget

The problems I see with this are:

  1. This never validates the client data. The server never appears to run their own hitscans and simply accepts the client’s target data that was replicated to it.
  2. Why should the client hitscan data be replicated to the server at all? I am thinking the server should run its own hitscans and use that as the end-all-be-all in case the client is lagging or cheating. The client can still run its own hitscans for feedback even if it is not correct.
  3. In ULyraGameplayAbility_RangedWeapon::StartRangedWeaponTargeting(), the local player opens a scoped prediction window with: FScopedPredictionWindow ScopedPrediction(MyAbilityComponent, CurrentActivationInfo.GetActivationPredictionKey());. The code specifies that this constructor is “To be called on server when a new prediction key is received from the client (In an RPC).” but the block of code that created the scoped prediction window is only ever run on the client. Is something incorrect here?
2 Likes

I’m having a similar problem as you tracing Lyra’s shooting logic.

Regarding 1, it is known that Lyra was designed with client authoritative hit detection. In this Youtube vid they explicitly mention that.

Unfortunately, they also mention that “somewhere in the code there’s a comment that say ‘if you want to do server validation for the shots do it here’”. As far as I can tell this comment does not exist. So I’m still struggling to figure out how to convert the code to server authoritative.

For point 2, I also don’t know why that happens. Maybe it’s more convenient to just directly give the targets for replication purposes and just replace those that aren’t correct? who knows

I do wish there was a lot more detail on this aspect of Lyra… considering how it’s such a core gameplay feature…

2 Likes