Client-side prediction of networked RaycastVehicle

Post Reply
McAkket
Posts: 2
Joined: Sat Nov 03, 2018 1:46 pm

Client-side prediction of networked RaycastVehicle

Post by McAkket »

I know this topic has been discussed before, but I'm interested in finding out what others are doing, and how successful they find their approaches.
I'm implementing some client-side prediction and state-correcting/synchronization in a little racing game I'm developing using Ammo.js/Bullet physics.
The core is simple. Every frame, the client collects inputs, sends these to the server and applied these to it's local simulation. At a rate of 20hz the server sends the authoritative states back to all clients.
Other players on the clients local simulation are updated as well using velocities, rotation and position. The graphics representation is then interpolated toward the physics body to smooth things out.

Here are some of the different approaches I've tried out to get this to be as precise as well as smooth as possible:
1) Replay-method based on this article: http://www.gabrielgambetta.com/client-s ... ation.html
The client stores all inputs in an array, and when it gets an authoritative state back from the server, it sets its transform to this state(position, rotation, linear velocity and angular velocity). Then it find all inputs Newer than the last one processed by the server, and replays all these inputs by applying those and stepping the world.

By smoothing the graphics representation of the vehicle, this seems to work very well in fact, as long as the world is simple and mostly static.
*However, if two bodies are manually changed using transforms, issues arise. If the local body has it's transforms altered at the same time as a remote player in the local simulation, AND these two collide while doing so, the body will disappear, to never come back even when the next update arrives.

2) Replay-method using stored velocities.
The client stores velocities at all inputs, and when a new state is received, it reverts back to this position and rotation and then replays a simple prediction using pseudo:
transform.setOrigin(transform.getOrigin()*(velocity*dt));
This actually works decently as well, although collision detection is buggy, as would be expected. At least until the world has stepped a couple of times.
This method is less resource-hungry as you won't have to step the world N frames, and can be used in very dynamic worlds without disabling bodies.
*Same issue as above, setting the transforms directly results in strange behavior and disappearing bodies. As well as the aforementioned issue with collision detection. The latter is less apparent with proper smoothing, though.

3) Constant interpolation toward authoritative state
At every state update, interpolate the position (and) rotation of the body from he predicted state toward the authoritative state by some percent(perhaps based on RTT). Les accurate than the previous two methods, but very smooth.
*Same issue once again, but this time happens more frequently due to constant manual transform setting.

4) Impulse-method using stored states
This method is an experiment at trying to solve the issues in the above methods.
At every input the client stores the predicted position. When a state is received, the authoritative state is compared to the predicted state when the input happened. The difference in position is used as a direction vector, and said vector is then scaled and used to slowly push the client into the proper authoritative position.
*The issue here being that local X axis impulses and forces on the vehicle are very weak do to the side-impulses of the vehicle. At the same time, if the distance becomes too big, the impulses will spiral the vehicle back and forth with increasing force. I guess this could be prevented by snapping the position with large distances, but this would occasionally result in the above issue as well.

So, have anyone done something similar, or are there apparent bugs in the above implementations?
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: Client-side prediction of networked RaycastVehicle

Post by drleviathan »

the body will disappear, to never come back even when the next update arrives
It sounds to me like there is an instability in your simulation. When NaN gets injected into BulletC++ it crashes but the JS implementation you are using may misbehave in other ways.
McAkket
Posts: 2
Joined: Sat Nov 03, 2018 1:46 pm

Re: Client-side prediction of networked RaycastVehicle

Post by McAkket »

When the instability happens, properties such as velocity, rotation and velocities indeed become NaN, although without crashing the engine itself.
I did some more tests, and the issue seems to stem from setting the rotation of the transform.
This is however an important feature for a networked game, and as I use one client to host the game and run the authoritative simulation, there are multiple places where this might happen.

On a client: When a server update is received, the position, rotation and velocities are set in the transform of the body. Sometimes this results in the body becoming invalid if it updates while colliding with another vehicle body.
On the server: Every game loop(About 60 Hz), the client sends its input as well as it's transforms quaternion. The reason for making rotation client-authoritative is, that prediction will be a lot more precise, and even at 200+ ms latency, the client will still run very smoothly.

I've tried setting the values of the quaternion directly, instead of setting the direction of the transform with a new quaternion, without any real luck.
I've also tried applying torqueImpulses to force body into the desired rotation, but setting the transform seems better, as it needs to be as precise as possible due to the nature of racing games.

I somehow seem to fixed it by now, using a mix of the above methods, as well as using current values for position and rotation instead of extrapolated predictions on the remote bodies. This makes remote bodies being in the past by latency+graphics-interpolation, but at this time it's a fair compromise considering the alternative.

I'm still open for suggestions on doing prediction, though. As I understand that re-running the simulation by steps not yet confirmed by the server might be a bit much for players with high latency. Having to step the world maybe 20 times in one frame might be a bit crazy, especially in a JavaScript environment.
Post Reply