I'm currently working on 2D rigid-body physics multiplayer game using Matter.js for physics (both the client and server side) and Phaser3 for rendering. Both the server and the client run the simulation and the server is authoritative.
The whole game consists of multiplayer players (who can collide with each other) and objects (boxes, balls, etc).
For now, I'm working on the client side prediction and while I understand the main concept, I just can't get it right.
Every game tick I gather the client inputs, store them locally, timestamp them and send to the server, while at the same time I start moving the player forward (by applying force). When the server receives the inputs, it processes them (by applying the same forces to the player). The server then sends the current state (coordinates, velocity, rotation, last processed input timestamp, etc) to all players at 20hz.
This is the part where it gets tricky. What I currently do, is I force the player to the coordinates sent from the server and apply the same velocity (both X and Y). After that, I remove all locally stored inputs which are older than the last processed input and apply them instantly in a single frame, by doing multiple physics steps.
With low latency (20-30ms) it works out quite well, with some jittering when rapidly changing the player direction or when I'm colliding with other players/objects, but with higher latencies 70-110ms, the prediction goes completely off. I can see the player constantly being pulled back and forth and the whole game becomes unplayable.
I can't wrap my head around, what exactly am I doing wrong? Is client side prediction even the correct way of handling this sort of thing in a rigid-body physics-based game?
Also, I turned off state updating from the server and noticed that the position in the server and locally sometimes goes completely off, for example when I'm jumping, running and quickly changing direction. I believe that means that the physics engine is not deterministic?