0
\$\begingroup\$

Context: This is for a multi-player shooting game. It's coded in TypeScript however this question is moreso a netcode/theoretical question rather than a question regarding implementation.

My game currently employs client side prediction to give an "lag-free" experience. We use a home-grown reliable (but unordered) UDP for data packet transmission. Each client sends the server their inputs and only their inputs, and the server then returns them with any data needed regarding the game state.

Problem:

In client-side prediction, there are various things that cannot be predicted, one thing being shooting (we can calculate a person's position based on their current inputs but we can't magically predict when they will click their mouse.)

Approach 1:

  • When a client presses their button, we transmit them sending a mouse click to the server including a timestamp as to when that mouse was clicked.
  • On the client side, we begin rendering bullets immediately.
  • On the server side, we begin creating bullet entities and emitting these new entities to the other connected clients.
  • With the mouse click and time of when that mouse clicked and the current position of the player at that time, we can easily figure out where a bullet will be at any given moment, and henceforth, we're able to compensate for this lack of ability to predict.

To kind of visualize this better: If we're in a 0 ping world, our bullets will spawn directly at the tip of a gun barrel. In our current world, instead, they'll be spawning several pixels away from that gun barrel, and the distance away from the barrel is effected by RTT.

Now this works for pressing a button, but what about letting go?

We need to wait for the client to send the server the data packet indicating that they just let go their mouse. Let's say this data packet takes a minute to get from the client to the server, by the time that happens, the server believes that they completely emptied their mag while the client let go their firing button a minute ago.

This is clearly a problem. Network spikes, bandwidth issues, etc., can create a discrepancy regarding how a client vs server understands how this shooting happened.

Approach 2:

Instead of sending inputs, why don't we send the bullets the player spawned in to the server? (There's potential security flaws to this, but for now we'll ignore it and say we're able to validate things on the server side.)

The next issue becomes if a player has really spotty internet connection, their bullets can be sent in all different kinds of rates such that on a receiving end, even if they were holding down their mouse button, their bullets fire-rate would be jittery and not consistent.

Question:

I feel like this problem is a solved problem, since this is a fundamental aspect to any shooting game for the past several decades. And so I wish to clarify: How would you go about networking the event of shooting such that we are able to account for various networking edge cases? What are things that need to be sacrificed to achieve this sort of idealized gameplay?

Thanks a bunch for reading through this!

\$\endgroup\$
2
  • \$\begingroup\$ Have you watched "I shot you first" about replicating combat actions in Halo Reach? \$\endgroup\$
    – DMGregory
    Jun 14, 2023 at 5:22
  • \$\begingroup\$ @DMGregory Funnily enough, I have two days ago but I was not able to make a conclusive method to solve this specific situation. \$\endgroup\$
    – leozheng
    Jun 14, 2023 at 5:44

2 Answers 2

0
\$\begingroup\$

I've never encountered a situation in a shooter where lag caused me to fire more/fewer bullets, or where there was rubberband-y behavior around how many bullets I had left. This leads me to think that shooting isn't treated like movement (input pressed/input released messages).

Instead of a "mouse pressed" and "mouse released" message, it's more likely handled through a series of "shot fired" messages. Because these games have lag compensation, they avoid the issue in your approach #2. A client can say "I fired a bullet on tick X with position Y and rotation Z", and the server will roll back to tick X to see if that bullet hit anything.

\$\endgroup\$
0
\$\begingroup\$

If you have any kind of prioritization system shooting should be near the top of it.

instead, they'll be spawning several pixels away from that gun barrel, and the distance away from the barrel is effected by RTT.

Yes this is fine. You can hide some of this in animations like:

  • spin-up time on the weapon (maybe a couple ms)
  • recoil + muzzle flash: Even though the muzzle flash might only last a few ms, it's bright enough and disorienting to your vision that you'll have to re-track where the bullet might be. Coupled with the recoil (deterministic recoil) you have more time to make sure you put your bullets in the right place without the user noticing.

As @Archduke said, shooting is not movement. You can trigger your "shot fired" event at your rate of fire. It's ok to penalize the shooter for being laggy, it's not ok to penalize the other players by over-spawning bullets.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .