I am currently working on a rather simple multiplayer platformer. I read quite a bit of articles on the techniques used to hide latency, but I still fail to get my head around certain of the concepts. I find the topic very interesting and like to try ideas by myself, but I think asking gamedev stackexchange will be more efficient for my question. I will try my best to describe my current situation and what question arose along the way.
Right now, I only want to have a single player synced with the server. Theoretically, I assumed a player by himself with client-side prediction would not require server corrections, since there are no external factors that influence his movement. Therefore, my prototype currently only has one player synced with a server without server corrections being sent.
If you are familiar with game networking, I think you may skip the context sections, though I may have done something wrong along the way as well.
The client loop (once per frame, once every ~16.67ms)
The simplified client loop looks like:
Check for local input (WASD) and package them as actions (e.g.
Type=MoveLeft, Time=132.0902, ID=15). We keep the packaged actions to eventually send them later. Also, we directly apply the desired action to the local physics simulation of the game. For example, if we have a
MoveLeftaction, we apply a force towards the left on the player's velocity.
Check to send actions. To prevent from abusing the bandwidth of the client, only send the packaged actions at certain intervals (e.g. 30ms).
Apply server modifications. At a certain point this will handle deltas and corrections received by the server and apply them to the local simulation of the game. For this particular question, this is not used.
Update local physics. Run the physics loop on the main player. Basically, this does the client-side prediction of the player's movement. This adds gravity to the player's velocity, applies the player's velocity to his position, fixes collisions along the way, etc. I should specify that the physics simulation is always executed with a fixed delta seconds (called multiple times depending on the real delta seconds).
I'm skipping a few specific details about the physics and other sections because I feel they are not required for the question, but feel free to let me know if they would be relevant to the question.
The server loop (every 15ms)
The simplified server loop looks like:
Handle actions. Check for received action packages from clients and apply them to the server physics simulation. For example, we could receive 5
MoveLeftactions, and we would apply the force to the velocity 5 times. It is important to note, a whole action package is executed on one "frame", contrarily to the client where it is applied as soon as the action happens.
Update the game logic. We update the game physics, moving players and fixing collisions, etc. We also package any important events that happened to be sent to players (e.g. a player's health dropped, a player died, etc.) later on.
Send corrections. We regularly (e.g. once every 35ms) send deltas to other players (e.g. player positions, health, etc.) if they recently changed. This part is not currently implemented, as I want the simulation of a single player to give the same results on the client and server without corrections, to make sure that the client-side prediction works fine.
The current system works fine under simple circumstances, and I was happily surprised to see that it gave very similar results with simple horizontal movements(the inaccuracies are due to floating point precision errors, I believe):
Please ignore the prototype graphics. White rectangle = player, Red rectangles = obstacles, Blue = background
However, I'm getting sync errors after doing time sensitive movements, such as jumping and moving close to an isolated obstacle:
In theory, I would expect both to always end up with the same results, as there are no external factors to the client influencing his position. In practice, though, I think I understand the problem.
Since jumping around an obstacle like that is very dependant on the timing of the player, small variations of when the velocity is applied to the position will have repercutions on the outcome (e.g. the client could move away just in time to avoid a collision with the obstacle, while the server would do it as it receives the whole action package later on and stay stuck on the obstacle for a small amount of time, changing the final result). The difference between how the client and server handle it is mainly that the client does all of its actions as they happen, while the server does them all in bulks as it receives them.
This long context finally leads to my question (thank you for reading this far): Is it normal to require server corrections even when there is only one player synced with the server, or should I use certain techniques to avoid desynchronization on time-sensitive situations?
I have thought of certain possible solutions, some of which I am less comfortable with:
Implement server correction. Simply assume that this is normal behavior and correct errors as they happen. I wanted to implement it anyway, but I just wanted to make sure that what I have done so far is acceptable.
Use the provided client time to apply the desired actions. I guess this would be similar to lag compensation, requiring to "go back in time" and check for movement. Kind of like applying server corrections, go back in time and reapply the next actions after that. I really dislike the idea. It looks complex, expensive in resources and requires trusting the client's given time (although I do plan to really check that the time looks relatively legit).
Ask GameDevelopment StackExchange for a great new idea that will fix all of my problems.
I am just starting out in the world of game networking, so please feel free to correct/criticize/insult any of the concepts above or give ideas/resources that could help me along my journey in the Wonderful World of Networking. Pardon me if I could have found my answer elsewhere, I have failed at that.
Thank you very much for your precious time.