Friday, September 1, 2017

first person shooter - The state that is synced when a window is broken in a FPS game


In order to better understand how state syncing works in games, I wanted to pose a specific example. Say you are in a FPS game with multiple players and someone breaks a window in some building. Wondering what happens in terms of state syncing. For example, there are these aspects:



  1. Someone else is watching the player break the window in the game. So it should probably be a real-time update.


  2. Someone isn't watching but they hear the sound of the window breaking.

  3. Someone is out of range of hearing or seeing the window break, so wondering if it still updates their game world, or it somehow lazily picks up the changes when they go to the area with the broken window.


Wondering how it generally works in these different scenarios. Any FPS game is fine. For example, in (1), if they are sending each individual particle of broken glass over the internet, or somehow they just send the generic action "user x threw object y at window z" and it replays the action on everyone's computer who is watching. In which case somehow the game world is broken up into macro chunks rather than being composed of particles. In (2), maybe they divide the level into zones of seeing and hearing range, and somehow send just the right amount of state then (wondering what kind of state, and how much). Then in (3), if it is lazily picked up, then what sort of queue is kept on the backend. If the data is compressed when it is sent, etc.



Answer



I will go over the how state synchronization works, and later will go over your particular cases.


But before that, let me tell you that you do not need to implement all that I say here. If you have a reasonably small number of players, a small map, and your matchmaking won't put together players with bad latency... you can get away with just sending every input to the host, and the host sending global state to everybody.




Prediction and synchronization


The first thing we want is responsiveness. As you know, it is not good to have to wait for the host for every input. Instead, we will send the input commands to the host and simulate/predict locally while waiting for the host. Then, when the host response, we decide if we have to correct the simulation.



Perhaps we have moved beyond the position the host responds, but what we need to check is if we were there - with some margin of error - when we should have been there. That means that the client will have a log of recent states to compare with the host response.


If an enemy makes an stun attack on you, this will be send to the host. The client is not aware of this, but the host is. As a result, the response of the host differs from the local simulation, and we have to make a correction. The client need to resimulate starting from the data recieved from the host. And what is presented to player will - usually - be an interpolation from the wrong simulation to the corrected one, over time.




We can improve the simulation quality if we send input data from player to player. That is, if we send input data from your client to the clients of players that are nearby you, they will be able to simulate what your avatar does based on your input.


The key here is to do send this information only for players that are nearby and could see you according to your simulation. That way saving bandwidth in not sending this data to everybody.




In some situations, the client will have to waiting for the host to simulate some aspects of the game. That would be in cases where the client needs permision to do an action (as cheat prevention), or can't predict the result of the action on its own.




Notes:





  • There will be discrepancies between client and host, which - for your FPS - means that the player is aiming to a past position, and thus a high latency player could virtually never hit. You have two solutions: 1) extrapolate on the client the future position of the target. 2) Send semantic information about what the player is aiming at to the host. However, in either case, it is up to the host to decide if it hits.




  • The host should be aware of latency, and take it into account in any simulation that requires a delay. That is, it should compensate for the lag. Interpolation on the client does not cover all cases of lag compensation.




  • There is also host side prediction. for instance, if the player has been advancing, and then there are network problems and we get nothing from the player, the host can asume the player is still doing the same thing.





  • Do not wait for the server for windup animations. Let the animation happen on the client as soon as possible after input. In fact, hide the latency in the animations... instead use it for the actual effects. However, wait for the host before you play the animation that notifies the player that the effect has taken place.








Managing Bandwidth


Sending notifications of events about everybody to everybody does not scale. It can work for small number of players on a good network, but we do not really know how good the network is.


Thus, we do not want to send notifications to everybody. Instead, we will divide the world in areas. And each player will be subscribed to one or more areas, from which they get notifications.


To be able to send the notifications to players that are out of date, the host will need to have a backlog. However, the hostcan remove the oldest entries to save memory.


Now, given an stream of notification about what happens to the objects in the world, the client should be able to compute the state of the world.



However, if the player did not recieve a large enough number of notification (because the player wasn't subscribed, because the network failed, because the host has removed the entries that it would need to send, or whatever) or if the client has unloaded the object from memory, the host should be able to send the whole state. This can happen when the client reconnects, gets subscribed to a new area, or heuristically on host discretion (in fact, some events may force a whole state update, for example if we need to guarantee that two avatars are in reach for melee).


Note: do not let whole state updates eat the bandwidth.




On the structure of network code


The host will have the complete state of the world, and will have it ready to be send to the clients.


However, as said above, the host will not send everything. Thus, we need to decide what to send and what not, and what is the priority for the things that we will send.


Once we have decided to send something to a client, we need to resolve if we are sending notifications of events or whole state updates.


Then we have throttling (to not saturate the network, and also for testing), error handling (for the case of a disconection or time outs), and we monitor latency. And of course, we have to deal with any other low level aspects of whatever network solution we are using.


Remember that we prioritize what to send, if the client only got a few updates, they should have been about the most important parts of the game. Your priorities will be different for every game, however, in general, you want to prioritize updates about what can affect the player, and what the player has done.





As long as the client is connected, it should - eventually - recieve the most up to date state of the world. Either by notifications of events, or as whole state updates. And the client will interpolate them.


Note: there is no guaraantee that the clients will all the intermediate position, only that they will eventually get the most up to date.




Your Question




  1. Someone else is watching the player break the window in the game. So it should probably be a real-time update.

  2. Someone isn't watching but they hear the sound of the window breaking.

  3. Someone is out of range of hearing or seeing the window break, so wondering if it still updates their game world, or it somehow lazily picks up the changes when they go to the area with the broken window.






  1. The host will send the notification of the event to the player watching. And from there, the client will simulate the glass. Futhermore, if you are sending input data among player, that input can arrive early, allowing to predict that the window will be broken.




  2. The notification for the player who is not watching will not have a high priority, and thus it can be delayed to allow for more important events. It means that this player could hear the broken window a bit later.




  3. Usually, this is the same case as the one above. However, if you have your map divided in areas, and this player is not subscribed to the area where the event happens, there will be no notification at all. Instead it would get a whole state update sometime after getting closer, and will see the window already broken.






(1), if they are sending each individual particle of broken glass over the internet, or somehow they just send the generic action "user x threw object y at window z" and it replays the action on everyone's computer who is watching. In which case somehow the game world is broken up into macro chunks rather than being composed of particles.



It is up to the game designers to decide if the pieces of glass are only decorative. If they are, then there is no need to synchronize them. However, if they aren't, then the host has to decide on sending this notifications. Each client will simulate them, and correct when they get the notification from the server... It is very likely that they will have very low priority for most players, thus, a high latency player may not get the updates, until the priority goes up (for example an enemy picks up the piece of glass and uses it to try to attack the player).


I am not sure what you mean by macro chunks, hopefully I covered your question.



(2), maybe they divide the level into zones of seeing and hearing range, and somehow send just the right amount of state then (wondering what kind of state, and how much).




This is the state of the art. However it is not true of every game and every map.



(3), if it is lazily picked up, then what sort of queue is kept on the backend. If the data is compressed when it is sent, etc.



There is a queue of events, to which I refer as the backlog. However, the events are not the truth. The truth is the state of the game in the host. At some point the log will be long enough that it is cheaper to just send the up to date state of objects than to send the log. At that point you should be removing old entries from the log to keep memory requirements of the host in check.


Yes, compresion is used.




This answer is based on networking code from Halo: Reach, and Overwatch.


References:




Additional Resources:



No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...