Networked "Physics-Vehicle Movement Component": existing examples or implementation hints?

Ahh yeah fair call.

I’ve been looking into client-side prediction and server reconciliation for physics-based games in UE4, and it’s been pretty difficult.

For example, when you receive an authoritative update from the server, you’d need to roll-back to it and re-simulate your inputs from that time onwards. How are you doing this in your game? I got it working by using my own physics (i.e. something dumb like position += velocity * DeltaSeconds) and a fixed timestep (*DeltaSeconds *in that formula is always 0.016666666f or similar). It’d be nice to use PhysX for this, but I couldn’t see a way to manually resimulate x number of physics frames within one Tick, and physics updates are occurring at different rates/timesteps due to variable framerates between server and client. Because of this, the server and client should often disagree about where the client should end up, and so you’d often be correcting the results on the client’s end.

Would be interesting to hear how you’ve gone! :slight_smile:

Yeah see that’s where my problem comes from. In my example it’s technically already doing the ‘prediction’ client-side, since the client moves to a location instantly (in theory anyway) and the Server does the same with the same input data, albeit a few milliseconds later (in LAN at least).

The server does the move then hard-sets the location on the Client, so in theory they should be ‘close’ to in-sync so the player won’t notice the discrepancy. Over a network connection my example probably isn’t going to hold up.

Unfortunately since Unreal doesn’t use Deterministic Physics (which I can understand, since it’s bloody hard), it’s also impossible to completely leave the movement up to the physics engine and just provide input in the hope they will remain the same. Even with the same fixed time-step and latency offsets, you’ll still get slight differences. Especially when collisions occur too. I figured that the replication code for WheeledVehicleMovement would hold up but unfortunately I don’t think it will for very long, especially when I’m not testing via LAN or on the same PC.

I will start work on this again soon, since I’d like to get a demo of my game out asap. Unfortunately Quadtrees are taking up my time atm :stuck_out_tongue:

Yepp, that’ll fall apart in some LAN configurations - when I was developing my old space game, I had a lot of problems with packet ordering (it was at a college, with an ‘interesting’ setup that ran all connections through several firewalls and routers elsewhere in the building and then back). If you just hard-set your positions, that’s gonna get gross. It will be impossible over the internet.

The idea isn’t that the server and client are absolutely matched and deterministic when it comes to physics simulations. Instead, you just want to get them as close as possible.
When you ‘provide’ the input to the server, you include the timestamp the input was recorded, as well as storing that input in a local buffer. When the server sends back physics info, it includes that timestamp, as if to say “here’s your new physics data, and THIS is the latest input I’ve processed from you”. The client gets this info, and uses that position as the starting point, resimulating every input in the buffer from that returned timestamp onwards. The position after the input is all resimulated is where the client ‘should’ be. The difference between where it originally thought it should be and that position that it calculates from the input buffer needs to be as small as possible - you’ll end up lerping between them to cover any slight mistakes.

Hopefully that makes sense. It’s a more clumsy description of client-side prediction and server reconciliation than what’s available here.

You’ve probably seen this already btw, but Rama has put up a post on how he handled replicated Physics movement. I’m going to snoop over his code tonight and see how it all works.

[Video] Player-Controlled Replicating Physics Movement, Simulating Physics! - C++ Gameplay Programming - Unreal Engine Forums!

Yeah I’ve seen that before. Isn’t it client-authoritative? If it’s just talking about the physics of others in the world around you… that ain’t so hard :stuck_out_tongue: Even good ol’ buffer interpolation will get you there. The trick is server-authoritative physics without having the local client feel like ****.

EDIT: He’s not wrong with the applying impulses part, though. Jump down to ‘Network Physics’ and you’ll see that Nvidia recommends that approach over setting a position. Just gotta weigh the cost of that against a simple interpolation.

Yeah I just read over the code and unfortunately it does mean that it’s client-authoritative - which is fine for most people but for anything AAA or with a focus on Multiplayer it’s probably going to invite cheating. Also means it’s probably not suitable for me…

Thanks for that link from nVidia, that’s actually pretty useful! Never really considered that SetActorPosition() would cause it to be re-inserted into the broadphase collision check but that makes perfect sense really. It also looks as if they’re saying that using AngularVelocity to drive the updates. I guess this is because it’s more likely to change between updates than the other factors. One slight caveat of nVidia’s notes there is that it requires the latency of your packets (easy enough to get in Unreal mind, UT already does that in it’s PlayerController) - and you need to maintain your FPS while those corrections are taking place too which is probably the biggest hit. This does mean locking your frame rate most likely unless you can get the system to work with variable FPS. Mind you, if FPS is varying by that much at the time anyway I doubt the slight movement or jitter of your craft or pawn is going to be the most noticeable artefact.

In my somewhat unique case, I HAVE to send the input from the keyboard anyway because it’s used to drive other effects like Particles (engine thrusters etc) and audio for the engine for other players. That’s fine though since that can be made an unreliable RPC, and I’m hoping that I can massively quantize that data and pack it into a tiny cost for replication. That overhead is probably minimal if anything.

This is certainly an interesting problem…

I’m not sure where you got the “One slight caveat of nVidia’s notes there is that it requires the latency of your packets (easy enough to get in Unreal mind, UT already does that in it’s PlayerController) - and you need to maintain your FPS while those corrections are taking place” parts, though. Why is it that your FPS needs to be constant?

And you should be sending your input anyway - that’s how you drive the physics on the server from the client! You send the inputs whilst simulating them yourself, and the server simulates them once it receives them.

Well according to their suggestion/implementation as I understand it, if a packet is delayed by say 250 ms - at that point in their example the car could be far enough away from the correct position that moving it over too few frames would break the illusion, and doing it over too many frames would mean that you’ll still be blending to the old position when you get the next packet, so you’ll constantly be slightly out of sync if you don’t time the blending between packet updates. It could become that you’re actually never in the right position for any of those frames because you’re spending all your time blending to the next packet.

The idea is that they work out how long they have to blend to the correct position over time so that it’s a) smooth and b) accurate. Divide 250ms / 60 frames and you get 15 frames to perform the update, so you blend to the position over the next 15 frames, at which point you should have the next packet of data. Problem is that if your framerate isn’t pretty consistent and your latency isn’t either, you’ll never really get the ‘time I have to blend’ correct.

Really this isn’t a problem since if you’re shipping something, it nearly always makes sense to lock the framerate anyway (which BTW drives me insane when people complain about capping at 60 in some games). Most people probably have a stable enough connection that packet latency will stay fairly constant for a game session anyway too. There will definitely be some slight discrepancies though.

Also, correct on the input. I got confused looking at Rama’s implementation and this one at the same time :stuck_out_tongue:

Hmm, I haven’t gotten that far in, although I feel like our games wouldn’t necessarily need it - you can just use the ol’ Entity Interpolation method (my goal), for example, and smoothly interpolate (even using splines with velocities as part of the control points). Cars would definitely be harder, since it’s more difficult to seem realistic if they ‘correct’ from one position to another, particularly horizontally - my space ships and your hover tanks kinda move that way already.

The more I look into methods like the above, and considerations such as fixed timestep physics, having a synced network time for the interpolation buffer, and so on, the more I realize we’re gonna need to get in there in the engine and make some changes. First, though, how are you guys all handling the movement of other players? I know you haven’t done it over the internet yet, TheJamsh, but you have, piinecone, and I’d be interested in how.

Tough stuff!

Sorry I’ve been a bit quiet in my own thread - I have some research deadlines (plus wedding + honeymoon) that have been eating up my time. I’m glad to see this discussion going though.

TheJamsh - my recollection is that it will be helpful for you to look into CMC for another reason, which is I think they have some code to resolve the replication/prediction fighting. Something along the lines of replication being applied to a proxy rather than the local physics object. (Unless I’m completely forgetting which codebase I saw that in, which is possible, since it’s been a while since I looked at this first hand).

Yeah my implementation that’s pasted above is the same for other player input, so it works with other players - the call before the RPC just works out if it’s a local player or not and if so, send the input, and update local input from the server (which fails to work since server overrides local player input and it defeats the point altogether lol).

The major problem to start with seems to be the ability to sync latency times between the server and clients… pain in the ****. I’m going to have a look at UT’s code when I get back to this and see how they determine ping.

@elfprince13 - I think that’s right, and the same is also true to UT code (at least for projectiles). They create a dummy projectile on the clients rather than replying on replicated movement too.

Okay update time: I’m finally getting somewhere! I won’t directly post code for now because it’s definitely going to have to change and in no way useable or finished… but here’s some idea of how to get it working.

The first step is to sync time-stamps between client and server. In order to do this I use the Player Controller, and send an RPC to the server with nothing in it, all it does is ask the server to call a Client function. It logs the local time (in milliseconds) that it send the packet, and then logs the time when it receives it back (aka, runs the client function). From that, you can work out exactly what the Servers’ time is, on the Client. I’ve tied it into Ping, so as Ping is updated (every 0.5 seconds or so via unreliable RPC), it bounces the timestamp RPC again to keep it accurate, but only if the ping is different to what it was previously. You can find the calculation for working out the offset practically anywhere online, but here’s how you get the different times.

// Local Time on Client
int32 ANTPlayerController::GetLocalTime()
	return FMath::RoundToInt(AccumulativeDeltaTime * 1000.0f);

// Estimated time on server
int32 ANTPlayerController::GetNetworkTime()
	return GetLocalTime() + T_ServerOffsetTime;

Now the tricky part, you create a Buffer of moves on the client which is essentially it’s ‘Move History’ (which comprises of input and the ‘state’ of the object at that time). The key part is that you actually store that history with the ESTIMATED server Timestamp. The idea is that you’re literally ‘predicting’ what the Server is going to say your move was at time X. At the same time, you send the input to the server so that it can process it.

So, the Client processes the input locally and the Server processes it as soon as it receives it. The server then sends the move back to the client with the LOCAL time-stamp for that player controller (aka, actual server time). When the client receives the move, they go through the move buffer and pass over any moves that are stamped before that time (no point erasing them, just move on to the next index). Now they receive the move in the past but check in the history to see if the move was correct at that point.

So yeah… that’s where I’m at. The tricky part now is that the Client receives the corrected move at (Ping / 2) time late, so I now need to work out how to offset it’s current position, based on previous move data. Currently I’m just ‘snapping’ to the corrected move so the Client updates for a while until it gets the move, then ‘snaps’ to an old position, which is of course not right. However, early results are promising (the red outlines are the move history buffer).

This is simulating PktLag of 250Ms (So total 500ms Ping), and only sending packets 5 times a second. Bandwidth useage can definitely be optimized here, but it’s showing promising results. Let’s be honest, if you’re playing on a server with 500ms ping and 95% packet loss, you’re probably not going to stick around for long anyway…

EDIT: Oh and one more thing, I have ‘Replicates Movement’ checked still because I want to update other clients and proxies as well, but I ignore it if I’m the local client. This is kind of lame because it means I’m sending data that I don’t need to to one of the clients, and regularly, so I’ll change the way it’s done eventually. For now though:

void ANTPawn::OnRep_ReplicatedMovement()
	if (GetNetMode() == NM_Client && IsLocallyControlled())

Hey guys, apologies for the silence, I haven’t had much time to work on this in the past month or so. I did get a chance to get into it yesterday though, so here’s a brief progress update:

I am extending the WheeledVehicle and WheeledVehicleMovementComponent classes (since I’m focused on wheeled vehicles at the moment). I’m able to simulate physics on the client and server (and simulated proxies) by passing the inputs from the client to the server, and then the results from the server to all simulated proxies. I’m also able to determine if the server disagrees with the move it has received from the client and identify the move that needs adjustment on the client.

At the moment I’m stuck finding a nice way to replay physics states. I have the moves that need to be replayed, but I haven’t sorted out the replay logic yet. I attempted to just use a delta but it causes a feedback cycle (a correction causes a bigger delta causes a bigger delta, etc). I then tried simulating physics ticks but they refuse to occur faster than global delta time, so passing the stored move’s delta time in and attempting to tick N times didn’t work. I will probably spend some more time today trying to get the delta approach to work, although I would definitely prefer simulating real physics ticks so the engine could reconcile collisions appropriately.

@TheJamsh nice work! Please let me know if you solve the replay problem, I am perplexed at the moment and am feeling like I’m going to have to dig deeper.

@HateDread I’ve been using the default physics actor replication technique so far, which means that when you have a bad ping your input experience is terrible. The actual update to the physics state for each player happens in AActor::PostNetReceivePhysicState() (in ActorReplication.cpp). I overrode and disabled that for the autonomous proxy role so I could handle the replay, but I also had to create a new replicated movement struct with a timestamp so I could do the move syncing. In any case, there’s a handy function for syncing the simulated proxies against the server in PrimitiveComponentPhysics.cpp called ConditionalApplyRigidBodyState that I use to smooth the simulated proxy movement. I’m a bit scrambled at the moment but I hope that answers the question you had a few posts back. Also after I get this implemented WE SHOULD PLAY!

I think what I need to do in order to solve the replay problem, is fix my Physics simulation time-step and I’d be able to successfully replay through all the moves. The problem is actually fixing that timestep is easier said than done, and really I’d probably want to completely decouple it from the main thread which probably isn’t completely possible. I almost reached the point of writing my own physics system while still latching into PhysX collisions.

I’m gonna be away for the rest of the week probably so won’t get back to this until the weekend… but when I do I’m going to look into replaying the moves. I kept trying to find a way to do the correction without actually replaying moves at all, but it’s **** near impossible unless the input hasn’t changed at all since the packet was first sent.

Very cool update :slight_smile: One thought comes to mind in response to this:

One thing you can do that ought to work pretty well is snap the corrected derivative quantities (i.e. velocity), but lerp the corrected positional updates. This smooths the rendering jumps, but keeps the handling characteristics server-accurate.

piinecone: ConditionalApplyRigidBodyState is a sweet find :slight_smile:

I have made this point to TheJamsh via twitter, but for you guys, here are said tweets:

Basically, try making an FPhysScene on each client, and only contain within it that client’s player + the nearest x number of objects. You can then simulate forwards and backwards with whatever timestep you need (try your best to match server, i.e. if server ‘should’ tick physics at 60fps, you do the same locally and hope the server wasn’t slowed down too much at any point). When you get the results from resimulating, you set the ‘real’ position in the full-scale scene on the client, or better yet, you interpolate between your calculated result and the result of the server, as per normal client-server reconciliation.

Why have two scenes on each client? Well, it’s important to maintain a full-scale scene on each client so they can simulate collision of projectiles and FX, etc, otherwise explosions and the like will occur a bit weirdly - you want the clients being able to decide when a fake projectile they make on their end should go ‘bang’, rather than waiting for the server to tell them where and when, since that will most likely not be in-time.

Give it a try! :smiley:

When you say “(…) interpolate between your calculated result and the result of the server (…)”, by calculated result do you mean the result from the resimulation?

And by “result of the server” do you mean the current position sent by the server? Assuming that calculated result is in fact the result from the resimulation, why interpolate between a result calculated with inputs yet to be acknowledged by the server and the current result of server, which was calculated with an older input?