Multiplayer Shooting Considerations

Started by
11 comments, last by oliii 15 years, 2 months ago
I wanted to start a little discussion about the networked implementation of shooting in multiplayer FPS games (similar to Counter-Strike, Quake, etc.). I'll throw out a few ideas/thoughts I had on the subject, and perhaps people can comment or add their thoughts. Basically, I'm working on finishing my long overdue 2d Counter-Strike clone. My networking model is heavily influenced by the Half-Life networking model. It's a standard server-client model (where the server is 100% authoritative). Maximizing cheating-safety is an important goal. This is important info pertaining to the following shooting discussion. Local player is always rendered at current-time by using client-side prediction. Remote players are always displayed 100 ms (adjustable) in the past. When shooting at other players, you should not lead the target other than because of bullet speed (it's not instantaneous). Instead, you should be trying to hit the players as they're displayed (even though it's their position 100 ms in the past). The server will take that into consideration and 'rewind' other players 100 ms back when doing hit/miss calculations. UDP will be used for sending packets related to shooting. Ok, now my ideas on the shooting model. There is one choice to be made at the beginning. Since the player movement is already locally predicted, it makes sense to do the same with local gun firing. But when you fire a certain number of shots, do you guarantee that they will indeed be fired (despite of any lag/dropped packets), or only attempt your best to make sure of that? To better explain that, suppose you have an automatic rifle and you fire 23 bullets (locally). Suppose at that time there is heavy lag and many of your UDP packets are dropped. By the time the server is made aware that you've fired 23 shots, 5 seconds have already passed. Does that mean the server will need to really fire those 23 shots, or perhaps ignore some of them? Or will it tell the client, that some of those 23 shots were not fired? If you take CS as an example, as far as I can tell, you will never get your fired bullets back, even if there's certain lag/dropped packets. That means the server must've either eventually fired them, or maybe ignored some. The underlying issue here is if you allow bullets to always be fired, even 5, 10, or 20 seconds in the past, that creates the possibility of a hacked client who tells the server that it made all these shots in the past, which in fact it never did. It seems to be a good idea not to allow shots to be fired more than a few seconds in the past... Ok, to go ahead with the discussion, for now I will assume that the choice made here is that the client will never get bullets back, even if they couldn't be fired due to lag/dropped packets. I'll continue talking about different weapon types and what kind of information needs to be sent reliably or can be afforded to be sent unreliably to the server/from the server to client/other clients. [Edited by - shurcool on January 10, 2009 4:44:38 PM]
Advertisement
I'm going to talk about what kind of information needs to be sent between client/server/other clients for different weapon types, and whether or not it needs to be sent reliably (this adds to bandwidth requirements, and thus needs to be kept to minimum).

Note: I'm not going to talk about the implementation details of reliable UDP transmissions here, in attempts to shorten this post. Just assume it's possible by resending packets until the other end acknowledges their receipt by an ack-sequence-number.

Weapon Types

Ok, the choice of weapons in my game (as currently planned) will be very similar to that of Counter-Strike 1.6. However, they all fire fast-moving, but not instantaneous projectiles.

That includes weapon types such as: non-auto guns (pistols), auto guns (automatic rifles), grenades.

The assumption made earlier on is that once fired on the client side, those shots must be reliably sent to the server. This is true for all weapon types.

Obviously, if someone receives damage as a result of a hit, that information will need to be sent to all clients reliably. This is also true for all weapon types.

The server will also need to let all other clients know whenever client A has fired his weapon, so that you will see other people shooting. However, for most guns, this doesn't need to be reliably sent. It's ok if due to lag/dropped packet you don't see someone's else bullet.

On the other hand, for grenades this info needs to be sent reliably, because it's not OK to die from a grenade that landed beside you because you never knew it was there. The same goes for flashbangs that must blind you, smoke grenades that must produce smoke, etc.

Well, that about covers it I think. This part was easier and less ambiguous to decide for me.



I think the original question I debated in the 1st post is much more difficult.

What happens if a client tells the server that he fired a shot 5 seconds ago. That happens to 'save his ass' by killing the other guys before they could kill him. But what if that was just a fake packet sent by a hacked client? Basically, you'd be allowing for any client to 'change the past' so to say. That seems to make it very hack-friendly.

[Edited by - shurcool on January 10, 2009 4:22:59 PM]
Quote:Original post by shurcool

What happens if a client tells the server that he fired a shot 5 seconds ago. That happens to 'save his ass' by killing the other guys before they could kill him. But what if that was just a fake packet sent by a hacked client? Basically, you'd be allowing for any client to 'change the past' so to say. That seems to make it very hack-friendly.


What if it's legit type of lag?

I remember this being mentioned in relation to some or another mmo. Players would throttle their outgoing bandwidth using one of the off-the-shelf applications.

This would increase their outgoing latency without affecting incoming data.

End result was, that while they were always fully up-to-date on world state, their input was severely lagged. But since server tried to be smart and fair, it simulated them far in the past (2 seconds or so), meaning they players would get killed without knowing what hit them.

The important part here is that this type of exploit is indistinguishable from other forms of latency, accomplished without touching the client.

There's a fundamental problem with trying to compensate for arbitrary latency. What good is a client 3 seconds behind? Or 30 seconds for that matter.

Pick a number. If below that number, do a rollback to compensate. If above, either disconnect them, or ignore their input.

And after all, FPS with over 250ms latency is unplayable for all practical purposes. Some would say that 25ms is bad enough. So lag or no lag, if a client doesn't update their state in 500ms or so, ignore everything from them until they sync up. IMHO, it's the easiest way.
Prediction can work differently. What the client sends is pure inputs. So, if the fire button is up or down, or reload requested, that gets sent to the server along with the movement requests.

Then, the server takes those inputs and shoot bullets if he can, according to his information about the player ammo, weapon and state.

On the client side, you just predict what the server does. According to the client info about ammo, you play the firing anim, reload, ect... hoping that the server will match the moves.

At the next server updates, the client anims and ammo count are kept in check. Most of the time, the server and client will be in agreement.

There is a command in counterstrike to hide the client side prediction for weapon firing / reload (which is just a 'fake' animation to hide roundtrip lag lag).

And yeah, if the client input is lagged too much, make the server ignores them, correction packet will most likely be sent as the server and client will be then out of sync.

Everything is better with Metal.

Here's an interesting issue to consider.

LPB vs. HPB: Fairness?, also How long to wait before making a decision

The key issue that needs to be resolved here is: When the server lets a player know that he died, is his death permanent? Or is it possible to 'undo' his death? If it is permanent, does the server wait at all before making that decision?

Consider the following situation.

There are two players, a Low-Ping Bastard with a round-trip latency of 10 ms and a High-Ping Bastard with a latency of 50 ms (not a very high latency, but it's enough for the issue at hand).

Assume they both have instant-kill instant-projectile weapons and they don't miss.

At time t = 500 ms, they both end up right in front of each other.

LPB has a reaction time of 200 ms and fires his weapon at time t=700ms.
HPB has a better reaction time of 190 ms, and thus figures his weapon at t=690ms.

At time 705ms (700 + half the RTT latency), the server receives the packet telling it that LPB fired his weapon at time 500 ms. It does its hit-miss calcs and of course it is a shot that kills HPB.

Now there is a decision to be made:
1. Does the server immediately make the decision to send packets to LPB and HPB, telling them the results (LPB gets a kill, HPB dies)?
2. Does the server wait a certain amount of time to see if perhaps HPB has fired his weapon even earlier, but the packet hasn't arrived yet?
3. (not a very feasible idea) Does the server send the death packet to HPB now, and 'undoes' it later on?

Of course, at time 715ms (690 + 25ms) the server will receive the fire weapon packet from HPB.

At this time, it knows the correct thing to do was have the LPB die, since he was shot dead first.

Depending on what the server did earlier, this may or may not be possible.

------

My thoughts:

I think the best thing to do would be to have a certain time out giving a higher-ping player a chance to save himself before making the kill/death decision. This time limit can be correlated based on the average latency of that player, up to a hard limit of say 50-100 ms or so.

That way, having the lowest ping and decent reaction time will not prevent you from being killed by a player with a higher latency but even better reaction time.

Of course, this issue becomes less prevalent the less instant-projectile weapons there are. That's why I'm planning to have bullets travel at a certain non-infinite speed. But of course, there are always exceptions, like shotgun battle from point-blank distance.
usually, it's on the first come first served basis. If you have a high latency, you will be more punished. You have to consider how often these situations will occur, and if they are worth fixing. Personnaly, I think it's not worth it, and you also have to consider potential exploits for high ping players.

Everything is better with Metal.

These are rare edge conditions, given a difference of only 40 ms round trip ping between the 2 users, in average cases their reaction and action cycles would not sync up that closely, so the advantage would overwhelmingly be the one who saw the other first.

For instance in any FPS game you'd get the jump on your opponeent with easily .5-1 sec lead time before they even know where their being shot from. My understanding is most FPS don't do any sort of predtictive logic anymore as the latency have come down to such a point where it's not worthwhile to give people with 150+ms lag good play expereinces, as most people wouldn't play on servers with that kind of lag either way.

Just go with the simplest just in time solution, first come first serve and I think that will be more than adequate.

Good Luck!

-ddn
First of all, thanks for the comments.

I've finally gotten around to implementing some of those weapon firing ideas into my game. I did take the simple approach of doing 'first-come-first-serve' hit taking, so a lower ping player will have the advantage. I might change this to a more complicated system later on.

Right now all the packets are mostly sent via unreliable UDP, so I still have to add a layer to ensure some of them are reliable. But in any case, it seems to work pretty well so far.

I'm surprised that I haven't run into any other issues/questions about this topic when implementing it, so hopefully everything'll go as I planned.

Quote:There is a command in counterstrike to hide the client side prediction for weapon firing / reload (which is just a 'fake' animation to hide roundtrip lag lag).

Would you happen to what know the command/cvar is called? It'd be very interesting to see this in action. I'm guessing I might have to host my own server to be able to see this, but that's fine.

Thanks. :)
Quote:Original post by shurcool

I'm surprised that I haven't run into any other issues/questions about this topic when implementing it, so hopefully everything'll go as I planned.


Are you testing over LAN or internet?

When developing I was mainly using localhost (<1 ms latency). I've also used a remote server I have access to, but my latency to it is only 10-15 ms.

At the end of the day I've played it with a few guys over the internet (server was hosted remotely, again), and we all had 10-50 ms ping at most.

So the conditions were as good as they get, so anything less than perfectly smooth performance would've been unacceptable.

In fact, since I'm using an 'interp' value of 100 ms, meaning you see remote players 100 ms in the past on your client, then as long as your latency to the server is under 100 ms (and the other guy too), your client will be able to perfectly predict the damage and hit/miss calculations as the server (who is the final authority over hit dmg/miss calcs).

That was just something I used to make sure my weapon firing/bullet collision detection systems were working properly.

Like I said, I'm still early into the development of the shooting part, so for now it's a very simple system with unreliable UDPs being used for the packets. So if your 'weapon fire' command packet is dropped, the server won't ever know you've fired a shot. This is something I'm planning to improve on later.

The good thing is, there is absolutely no way to cheat in the sense that the server is completely authoritative over all the commands that occur. Basically, that means a speedhack or rapid-weapon-fire-hack are simply not possible. Even if the client sends 100 'weapon fired' packets in 1 second, the server will execute the first, and then ignore the rest since the weapon is still 'in the process of firing a bullet'.

The whole idea, of course, is that everything works well in all scenarios: whether you are a client, the server, locally hosting an offline game, etc.

Btw, if anyone's interested in trying it out as it is now, let me know. I have the client Win32 and Linux binaries already compiled and uploaded, hehe.

This topic is closed to new replies.

Advertisement