Networked physics with Farseer & Lidgren

In addition to SCP-CB v.1.1, for the past few weeks I’ve been working on the networking part of Subsurface (physics in particular). It turned out that the way I’d implemented framerate-independent physics made it almost impossible to keep the networked clients in sync, but after some modifications everything seems to be running smoothly even with high latency.

Previously I was using a variable timestep, which basically means that if the game is running at 50% of the preferred framerate for example, we’ll just move everything twice as fast each frame. This worked somewhat acceptably in single player mode, but the problem was that the behavior of the physics simulation wasn’t 100% consistent on every framerate. As the framerate dropped and the timestep increased, the physics and the animations of the characters started to get a little “off”, and with a large enough timestep, the physics joints would just go crazy and the characters would basically explode. The physics seemed to work fine on framerates above ~25 though, so I just limited the timestep so that the game doesn’t try to compensate the dropping FPS too much and break the physics.

When I started testing on syncing the physics between a server and client, it turned out the variable timestep just doesn’t work. Even though the physics looked consistent on framerates above ~25 FPS in the single player mode, the slightest differences between the two simulations would cause them to get out of sync. An item hits the ground in a slightly different angle in one of the simulations, bounces off to a different direction and ends up in a different place than in the other simulation.

After some googling I found this fantastic article about fixed timesteps. The technique described in the article wasn’t hard to implement, and now I have a physics simulation that behaves the same no matter what the framerate is.

I took some shortcuts and simplified the way the physics are synced between the clients. The characters or any other physics objects that can be manipulated by the players don’t collide with each other, and this isn’t an FPS game where it can be crucial if a character is “actually” in a slightly different position than it appears to be on your screen, so I decided to skip latency compensation altogether. This means that the server and other clients are always a little behind a client controlling a character (you can see it in the second half of the youtube clip below). As there is essentially no way for a client to collide with or otherwise manipulate any of the other clients, at the moment it doesn’t make much difference. The only case were it could be noticeable, would be if two players were trying to pick up an item at the same time: it might look like you picked it up first, but actually the other player was closer to it and his key press arrived to the server before you, in which case the item would be pulled from your character to the other one as soon as the “player 2 just picked up that item (not you)”-message from the server arrives.

There will be various weapons (or tools that can be used as weapons) in the game though, so this kind of simple system might turn out to be insufficient in the future in which case I’ll have to implement some kind of latency compensation, but atm it works great!

 

FacebookTwitterTumblrGoogle+Share

4 comments

  1. How do you handle packet loss? Does the server trust the players (i.e. allow them to apply arbitrary forces to themselves) or does it perform actions based on keypresses?

    1. Currently I don’t handle packet loss in any way. The clients just periodically send their state to server and if a packet is lost, the server will use the next packet to update the player. This isn’t a problem when it comes to moving the characters, but losing a packet that notifies about someone picking up an item definitely is.

      I have a couple of ideas on how it could be fixed though. At the moment when someone picks up an item for example or does anything else that the other clients should know about, they send a message to the server and the server passes it to each client. If the client who picked the item up doesn’t receive their message back, it must’ve been lost at some point and the client resends it.

      That obviously doesn’t help if the packet is lost between the server and one of the other clients it’s been passed to. Something that might work would be for the server to keep track of maybe a dozen of the last “important events” that have occurred, along the lines of “this button was pushed | this item was picked up | this door was closed” and sending that data to the clients every few seconds. That way the clients can see if they’ve missed any important packets and ask for them to be resent.

      And yeah, the server trusts the players, another thing that I hope “will be good enough”. Because it’s essentially a co-op game, where it’s easy to grief even without hacking (open an airlock, break a window or something), I hope it won’t be a major problem considering that the host can just kick hackers out of the server.

      1. I read that Lidgren supports optional packet reliability, maybe you can make pickup-packets reliable to guarantee that players will pick up items if they try.

        1. I’m actually already using it for those “important packets”, but it seems that some packets are still lost if packet loss is very high (= if I set Lidgren’s SimulatedPacketLoss config to something like 0.4). Or it could be that it’s just some bug in my code, I’ll have to look into it more closely.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>