Hi Kaos. I’ll try to answer this the best I can though I need to give the standard disclaimer that this stuff is far from perfect. High rate of fire hitscan weapons weren’t the first class citizen when designing the ability system and I’ve frankly never seen an integration with it that felt great. We would do things differently if we ever wrote a new version. But that said, I think its workable and you get a lot of other advantages in using the ability system with your weapons, so don’t let that discourage you.
You are correct that in Fortnite, for the vast majority of hit scan guns, we send an RPC with each bullet shot. That is, 1 bullet = 1 ability activation -> 1 target data sync -> 1 ability end. For better or worse, that is how the FN weapon system evolved with the ability system. I’ll mention some alternatives at the end but I think you are most looking for “how Fortnite works”, so I’ll focus on that.
Given the above, out of the box this would generate three(!) RPCs. One RPC gives me heartburn, so three was a problem. That is what the RPC batching that you reference does. It condenses those three RPCs (ServerTryActivateAbility, ServerSetReplicatedTargetData, and ServerEndAbility) into a single RPC.
This batching technique is in the engine and something you should be able to make use of. I am not sure exactly what engine version it was first available in, but here is the github commit that has it (sorry its part of a large merge changelist, the relevant changes are in AbilitySystemComponent.h and AbilitySystemComponent_Abilities.cpp).
https://github.com/EpicGames/UnrealE…ab524fa9c88105 (Need to be logged into github to see)
Using it should be relatively straightforward… I think:
FScopedServerAbilityRPCBatcher ScopedRPCBatcher(ASC, PrimaryAbilitySpecHandle /** Your weapon's ability spec handle */);
That is the only line of code in Fortnite that enables the batching. It is at the top of our “TryToFireWeapon” function. I need to explain here that the “Fortnite Weapon System” sits between input bindings and the actual call to activate the gameplay ability that the weapon uses. If you are in a world where you bind input directly to ability activation, I think you could add this to UAbilitySystemComponent::TryActivateAbility and it would work. But that is untested. You may still need to create your own thin layer here.
The point is really this: you are about to call some function that may try to call ServerActivateAbility, ServerSetReplicatedTargetData, and ServerEndAbility within that single function scope. If you can put a FScopedServerAbilityRPCBatcher on the stack before that happens, it will suppress the calls to the actual RPCs, cache of the data you are trying to send, then call the batch RPC when the FScopedServerAbilityRPCBatcher goes out of scope (see UAbilitySystemComponent::CallServerTryActivateAbility, UAbilitySystemComponent::CallServerSetReplicatedTargetData, UAbilitySystemComponent::CallServerEndAbility). “ServerAbilityRPCBatch” is the actual batch RPC I am referring to.
That should be it… on the receiving side (server), it should unpack the parameters and call those three functions in a way that is transparent to the rest of the system.
So, that takes care of client->server communication. I will just mention that we also did batching on the server -> everyone else side with respect to gameplay cues. Each weapon shot can generate several GCs: the muzzle flash (predicted), the impact effect (predicted), the damage effect (not predicted), + other stuff (crits, etc - not predicted). By default each GC replicates independently, not as an RPC but as replicated data in the ability system component. And all of those GCs mostly share the same data (E.g, source/target, hit location, magnitude, ability level, etc). The optimization we did was to built another batching structure, intercept those specific GCs, and replicate them all together in a more efficient, less generic fashion.
The other alternatives I mentioned above are probably obvious: for automatic weapons you could just have 1 ability activation, N target data syncs, 1 ability end (so, don’t end the ability until input is released). And, yes, additionally if you want a more “pure” classic FPS shooting model, the client doesn’t need to tell the server who he hit. The target data can be generated server side based on the server’s view of the world. Of course that leads to more mispredictions and other issues that will crop up depending on how your server is running the game (E.g, its harder to do location based damage if your server doesn’t run animation code. Different frame rates between client and server make things harder to stay in sync for precise queries, etc.). For Fortnite, the fast paced building (lots of shot blocking actors being quickly created and destroyed) and 100 players make it hard to do traditional shooting models.
Hope that helps