Many bullets and multiplayer

I am currently making a game with bullets (~100 at any time) with small multiplayer (4 players max) and would like to hear some of your opinions since I’m still fairly new to UE4 and have no experience in networking outside a trivial scale. I have googled a bit and this thread came out which I found some issues the OP encountered in implementing numerous instances of homing missiles.

Some info on my planned projectiles / bullets:
-most will be quite stupid and moves in a straight line;
-most will only stay in the screen of any one player for at most a few seconds, and generally less than 10 in terms of life span;
-the spawn frequency of individual bullets will be somewhat clustered; a close example is the heavy machine gun in Metal Slug (for those who don’t know what it is, a rough estimate is around 50 of them in a 3-4 seconds span).

Since it looks like my projectiles are a lot cheaper than the OP’s who encountered latency problems with 30-40 projectiles, I am wondering if I really need to bear optimization in mind on implementing it or I can get away even if I am doing things in an not ideal way :smiley:

The most straight-forward way I think of now is to make each shoot request from players a Server RPC who then :

  1. check for validness (ammo, rate of fire, etc.);
  2. spawns the replicated projectile with UE4’s built in movement replication;
  3. call a NetMulticast RPC to inform clients to update the shooter’s visual (animations, effects, sound etc).

Some form of optimization I can imagine and don’t know if it will be enough:

  1. use a “bullet manager” to batch shoot requests over a non-noticeable small interval then use a single multicast RPC to signal multiple shoot requests;
  2. for simple bullets, as long as the starting position, direction and velocity are correct on clients I should be able to use a low rate of syncing.

Would love to hear from you guys’ experiences and rough estimates (C++ only workarounds are welcomed too), in any case, I will update my progress in this thread just in case anyone is interested.

I think your steps are good, the only thing I would do differently is your step 2 (that spanws replicated bullets). Instead of that, multicast spawning of non-replicated bullets.

Server already validated the attack, so all clients (and server) can execute non-replicated bullet.

The problem you have with this approach is that you are now relying on the clients to handle the path and impact of the bullets … if there are packet drops or network issues you will end up with weird things happening.

It is a better approach to use replicated bullets … because of this.

Thanks for the input guys!

For the moment I think I will try out my planned setup first, hope I don’t come up with any problems.
If this doesn’t work out I will try xulture’s idea, but instead of not having replicated bullets at all I think I can replicate a stream of bullets as one bullet, since the bullets will have a predictable offset from each other I should be able to achieve the same result with less replicated bodies. I can see this will be considerably more complicate and require care to implement though when they interact with other other things.

I’d consider bullet trajectory cosmetic – it doesn’t matter how client shows it; impact of this bullet would be handled by the server, and server will multicast whatever happens to impacted surface/actor.

If bullet starts travelling based on starting parameters, these parameters will be determined by the server. Bullet that executes on the server could then interact with impacted actor. This actor will then replicate what happens next.

In this approach, only initial ‘shoot’ packet needs to be multicast, and later ‘impact’, without having to replicate, on each frame, movements of all those 100 or so bullets that Lyan said he’d have…

This is how I handle it:

  • Use a replicated variable (Burst Counter) and use RepNotify to spawn effects and sounds. It’s cheaper than an RPC and way easier to setup. It also means that if Clients are far away from each other, they won’t pointlessly receive multicasts for irrelevant actors.
  • Server handles all ‘Spawning’. BOTH clients AND Server do all the spawning logic and build-up like determining the weapons’ state, shot delays etc, but the Server still spawns the actual object which in turn replicates to clients.
  • For bullets that will never change trajectory, I use ‘bNetTemporary’, which means that yes the client processes the movement and there is room for error, but it’s marginal. However, none of this matters because:

The SERVER decides what the bullet hits and what it doesn’t. Both my Server and Client bullets can collide, and when they do they turn off their particles and hide themselves and prepare to be removed. However, the SERVER is the only one that actually deals the damage, and tells the client that the bullet has ‘exploded’, which replicates to clients and the clients then also ‘kill’ their own. However, it the client has already ‘exploded’ their own bullet, they do nothing and the actor is removed when the server destroys it.

ShooterGame does a bunch of this stuff already, so look at that for projectiles. However, SG also uses traces for it’s bullets which have next to no replication cost next to projectiles.

What I also intend to do going forward is store a ‘pool’ of inactive bullets in the world at game start, and just activate and move them and change their properties when I need to and turn replication on and off. This should massively reduce the spawning cost and help a bit with replication costs too. Unfortunately you lose flexibility if you do that but if you know you’ll never need more than 100 bullets, spawn them at the start of the game and assign that memory early. It’ll always be faster in the long run than dynamically allocating the memory to spawn them. Actor spawning is an expensive operation.

Unreal Tournament spawns projectiles both Client & Server side, the clients is a ‘fake’ projectile while the servers’ is the real one. When the real one dies, it kills the clients’ fake one for example. No real bullets are ever spawned on the client. It also uses the Players latency and ping to do movement prediction.

Xulture is right, you don’t want to be replicating movement for hundreds of bullets. You’ll swamp the connection and cause problems elsewhere, so you have to bite the bullet (Sorry) so-to-speak and deal with slight inconsistencies on the client-side, which most of the time won’t be noticeable anyway.

Ah okay … fair point. 8-}

I really want to stress this! When you start talking about lots of temporary objects, the biggest performance cost is usually in actor spawning. It is critical to use a pool.

I don’t understand why everyone wants to replicate their bullets, it is MUCH more network efficient to spawn the non-replicated bullets for everyone locally. Are there any cons to using this method that I am not aware of?

I assume you still mean spawning the initial bullet on the Server since there’s no alternative to that method? The problem with having them not replicate anything at all is that clients are then never in sync. The clients should never have the authority over what the bullets are doing, Server should always deal damage and deal any effects. The use of bNetTemporary is useful only when you have very low latency, or if your bullet simulation is deterministic and/or the game is running in lockstep. Unreal is neither of those things.

It depends on how accurate the client simulation needs to be. If you’re doing a first-person shooter, you absolutely need replicated bullets because otherwise people will never be in sync with each other, and they’ll never be able to hit anything because their simulation will radically differ from the Servers’. Don’t forget that when you shoot something on a Client, it’s no longer in the same place on the Server. In some cases you do ‘prediction’ to counter balance that but it’s never perfect. Multiplayer games are just a big hall of mirrors but you need to keep it as accurate as possible, or it falls apart. It really depends on the Game though, if I was doing something that wasn’t as ‘Serious’ and didn’t rely on it so much, I wouldn’t put much effort into it.

Also if it’s anything like my game, bullets can be affected by various objects such as repelling volumes or shields etc, if any outside force can affect the trajectory, you really should replicate it. Otherwise you’re client will never see the same thing as the Server, and it’ll in some cases make the game totally unplayable.

I will say though, if you want hundreds or thousands of bullets to be possible at once, you need to plan ahead for that, and trace-based weapons exist for this reason. Gears of War uses all trace-weapons for all of it’s bullet-based weapons, but with some particle magic they look and feel like bullets!

While I am comfortable with pooling, how do I toggle replication on/off for pooled objects run-time? I see that there are several methods that can be called SetReplicates(), SetReplicateMovement() and TearOff(). The docs doesn’t say much but from the look am I right to assume that the first two are reversible and TearOff() being a one-way journey?

Thanks for your detailed view.

So it seem that a hundred replicated bodies is likely to cause problem. One thing is that I don’t think trace style bullets are going to be of much help in my case as they will move rather slow compare to the FPS style bullets. I do want the bullets to be simulated closely on client side as being able to react and dodge them is one of the planned features in the game, luckily it seem in my case I have room for this to be done through prediction.

by the way, I thought multicasts will be skipped if the actor is not network relevant and never get received by those clients?
one of the posters in this thread below claims so, is that incorrect/changed?