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:
- 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.
- 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.
- 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?