As a bit of background, I have a client sending input updates to the server 20 times a second. Similarly, the server's update loop also runs at 20 times a second where it processes all client messages in the message queue, updates game state, and sends game state updates to the clients.
However, despite my client and server loops running at the same rate, my server sometimes doesn't receive a single input message from the client during the 50ms window between server updates. I believe this stems from my client's update timer not being completely reliable and the non-deterministic delays inherent in sending a UDP packet over a network.
The problem I'm running into here is when I try to reconcile client-side prediction with server updates. Let's say my server just acknowledged input message M from the client with state S. The client runs the game simulation with the buffered messages M+1, M+2, and M+3 to get the predicted position. This is fine, but let's say the server does not receive message M+1 before the next server update loop starts. In that update loop, the server will have no new client messages to acknowledge, and will send out an updated game state S+1 to the client while still acknowledging input message M. When the client tries to do client-side prediction, it will start from state S+1, but now it will simulate with inputs M+1, M+2, M+3, and M+4 which effectively results in a prediction in the "future" because we advanced S without increasing M.
None of the articles I read online discussed this problem, so I feel like I'm missing something fundamental here. Could anyone shed some light on my issue?