Godot Multiplayer Tutorial - Clock Synchronization | Godot Dedicated Server #14

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this codop multiplayer tutorial i'll teach you how you can synchronize the clocks of the server and the various clients connected to that server let's get started various viewers of the gold multiplayer series have pointed out that it's important to synchronize the clock between the server and the various clients instead of depending on the operating system time on both sides of the connection independently this is very true as there's various situations in which those two clocks might not be synchronized and if that is the case our world state loop is either going to be continuously extrapolating instead of interpolating which will be preferred or it's going to show a delay of seconds minutes maybe even more which is of course completely undesirable so we have to sync these two clocks up and there's various way we can go about it there's various ways that we can do clock synchronization in a decentralized system like a multiplayer game network before we dive into the code i want to share with you what i believe are the general requirements for such a synchronization system so that you can determine if these requirements indeed apply to your game as well or where you may have to make some small adjustments to the clock synchronization method that i'm about to be teaching you firstly we wanted to be light on bandwidth requirements so that our players are not using any unnecessary bandwidth for this clock synchronization process secondly we also wanted to be light on server processing as any unnecessary server power that has to go into this clock synchronization is wasted resources that we would have otherwise been able to use for concurrently connected players thirdly we want to make sure that these clocks stay synchronized within 25 milliseconds of maximum deviation preferably it is of course zero but the closer you want to get this to zero at all times the more bandwidth you're gonna require and the more server processing power you're gonna require so there's a balance between element one two and three of these requirements i believe of 25 milliseconds as a maximum deviation we still have enough padding in our buffer for the interpolation which is 100 milliseconds to allow smooth movement for the player the fourth requirement is how long we guarantee that that clock stays synchronized within those requirements within a single game session i've said that at 24 hours because i don't believe there's going to be many players that will be continuously playing your game within one single game session for 24 4 hours on end now of course there might be some crazy live streamers which are going to be live streaming for you know 48 72 hours but a simple relog into the game will automatically reset um the timer and you can go on for another 24 hours now most likely the code that i have made is gonna guarantee the synchronization for much longer than this 24 hours maybe even up to weeks or months but yeah we got to make sure we set the maximum somewhere and 24 hours is pretty much going to cover 99.8 of the players that will be logging into our game then lastly requirement number five is going to be that we want the client clock to flow and what i mean with that is that we don't want the client clock to jump around all the time maybe as a result of latency changes because if the client clock is jumping around that will translate into jitter when we render the world state on the player screen and that jitter is of course going to be bad player experience now with these five requirements we can start coding this clock in so follow me along as we go step by step through this process to start coding we have to determine two things which clock are we going to be synchronizing to and when are we going to be synchronizing the question of which clock is pretty easy as the clock doesn't really matter you can have a custom function that synchronizes your server to a gps satellite you could have a custom clock on your server you could even use the operating system time of your server all that really matters is that that clock is centrally available for all pairs and ticks consistently it doesn't even matter what time it is according to the clock it can be 700 before christ or 4000 ad all that really matters that at the end of synchronization all peers in the network agree on the same time now in this tutorial we'll be making use of the operating system time of the server as the clock to synchronize to as it is the most easily available to us and we don't have to build any custom functions to get it done to answer the question of when do we synchronize that's what i have right here on the screen i'm on the client project on the game server singleton script this connect to server function connects the client to the game server after a positive authentication result has been returned from the authentication server we connect up our signals and onconnections succeeded we hear on line number 39 start synchronizing the clocks you'll be right to point out that at this point in time we haven't completed the login sequence yet the network token verification process still has to take place however it's preferable that we start syncing up the clocks before the login sequence has been completed so that if after the login sequence a game frame has to be drawn we have a synchronized clock available to us to do the synchronization itself we call the server we run the fetch server time function and we pass along the timestamp of the current operating system time of the client that is not the clock we need to be synchronizing to but it's a timestamp we're going to be needing later to calculate latency switching to the game server project here we have that remote function receiving the client time we determine the requester and we send back to the requester return server time function call including the server time the operating system time of the server the clock we need to be synchronizing to and we pass back that client time that we originally received back to the client project now we have the remote function return server time we are first going to be calculating the latency now that latency is important because this server time here received by this function needed to travel the internet coming from the server and while it was traveling the internet while the packet with this two variables and this function call was traveling the internet the server clock has been ticking so if we were to set the client clock only to the server time we would be out of sync already equal to the amount of latency the amount of time that it took that packet to travel so we have to add the latency on top of the received server timestamp in order to set the client clock at the right starting time to calculate that latency we're going to making use of two timestamp the timestamp that we originally sent to the server when we made the clock synchronization process or we started the clock synchronization process i should say and again a timestamp here of the client clock when we have received that function callback so we have the client making a connection or a request to the server that's a one-time latency and then we have the server replying to the client that's another time latency we subtract those timestamps from each other and divide by two to calculate the average latency between this connection one way so right now we have a client clock however that client clock is nothing more than a timestamp that clock is not ticking and we need to make it tick in order to make sure that it stays synchronized so in order to do that we're going to be making use of the physics process engine scrolling up a little bit in the project we now have the function physics process that gives us the delta value the delta value represents the amount of time that has elapsed since the last physics step that delta value if you have left your project at 60 frames per second for the physics engine is going to be an expected 0.01667 or in other words is the amount of time represented in seconds inside a float type of variant however our client clock ticks in milliseconds instead of seconds and it's an integer and it needs to be an integer because this client clock value would be too big for a float to remain precise so what we're going to do is we're going to turn that delta value which is seconds float into an integer and multiply it by a thousand so we get milliseconds and we add that on top of the client clock the delta latency i'll get into in a moment and then we're going to go into the decimal collector the decimal collector basically collects the amount of decibels that are left after we have turned this float into an integer a integer or a float to an integer is going to leave out every decimal in the float in other words we are losing two thirds of a millisecond on every single frame of this physics process engine two thirds of a millisecond isn't much but 60 times per second over possibly several hours of a single gameplay session easily adds up to minutes so we need to include those decimals as well into the clock to make sure that the clock stays synchronized to do that we're going to turn the float from seconds into a float in milliseconds and we subtract the amount of milliseconds we've already added to the client clock normally under normal circumstances this on a single physics step would represent 0.667 now we're going to be keeping on collecting that into our decimal collector and as soon as our decimal collector reaches a value higher than one we add another millisecond to the client clock and remove that one millisecond from the decimal collector and that process can continuously go on so now we have a clock and it ticks however the latency that we have set when we return server time right here is constantly shifting that latency is not going to be stable among a single game session it doesn't even have to be stable from one millisecond to the other millisecond however we don't want to make these adjustments of latency on every single frame first of all we'll be taking too much network bandwidth it would take probably a little bit too much of our server processing power than we would ideally want to and if we change the or adjust the latency on every single frame we're going to get this client clock jumping around as sometimes a packet can get lost on the internet and suddenly the latency is represented as 200 milliseconds but that might not be true for the average connection maybe only being at 20 or 30 milliseconds so that's where i have this delta latency for and that delta latency comes out of a latency adjustment function that latency adjustment function starts at where we started off in this code on the on connection succeeded where we do the original synchronization here we have these extra lines of code which create a new timer set the wait time of this timer to half a second auto starts it as soon as the timer enters the scene tree and we connect the timeout signal to a new function on this script the determine latency function then we add this timer as a child to the singleton so now we have this determined latency function that is going to be firing every half a second the function calls the server runs the function determine latency on the server and passes along that timestamp going to our game server project we have that function receives the client timestamp determines the requester and sends back to that requester the client time that it was originally received then when we return that latency we again have that client time client time and again we can take the current timestamp divide by two the difference is that instead of setting the latency now we append it to a latency array and we're gonna wait until that latency array has received nine latency values at that point we're going to be calculating the average latency over the past four and a half seconds nine times half a second and then set the latency to that new averaged out value thereby we don't use too much surface power not too much bandwidth and our latency adjustment is not going to be jumping around the client clock all the time we do this by first sorting our latency from the smallest to the biggest value then we take the midpoint of the latency so that would be the middle value the fifth value in the array of nine values we need that midpoint because to average out the latency we want to discriminate any possible extreme values like i said a packet can get lost on the internet and get into an internet traffic jam and suddenly that packet might say okay latency was 200 milliseconds well all the other eight packets say no our latency was 20 milliseconds we don't want that 200 milliseconds to increase the average latency as that is an extreme value that we want to discriminate out of the calculation so we take the midpoint assuming that we're not going to have more than four extreme values and i mean having five extreme values probably means that the entire connection is currently timing out or hanging so then it will be okay if we make a big change because then the player is going to experience lag anyway so what we do is we iterate back through the loop from backwards iteration we're going to check if that value that we iterate over is twice the size of the midpoint and if it is then we'll remove it from the array so we'll just you know discriminate that out of the calculation however when i was testing this out with my own connection with my own vps which sits 15 kilometers from here in amsterdam i get average latency of sometimes as little as six seven eight milliseconds and if my midpoint will be eight milliseconds but i also have a latency in there that's 20 milliseconds then it would be twice as big but at the same time i do want to include a 20 in the average calculation so especially for our europeans among us or european players maybe you may be having we have also a check that the latency is only deleted if it's indeed twice the midpoint but also bigger than 20 and that's for the super fast connections we can sometimes see in europe so it's removed if the value is extreme and if it's not extreme we add the value of that particular latency to a total latency now we can calculate the delta latency so the difference in latency from the latest time that we have calculated this we do that by taking the total latency divided by the amount of latency values that are left and we subtract the old latency so in the first time that this function runs that will be the latency set here in this return server time function and of course every time after that it will be the latency that was calculated by this function itself now i'm printing the new latency and the delta latency here so we can have a quick test with this and at the end of the function we of course clear the array of nine values so that we can then again start re-appending new values as they keep coming in and as soon as four and a half seconds later that array again has nine values it's going to be doing this calculation all over again so now if i had hit play to give you a quick demonstration of how this work we of course do have to wait until those latency values start coming in you should see that now yep new latency is 19 and apparently the first latency that was set was set at 30. so we have a delta of 11. and you'll see that delta usually being a little bit bigger at the at the very first time and as you can see now the delta values two milliseconds minus three milliseconds two milliseconds two milliseconds you can see my latency is only uh 20 milliseconds and i'm currently connected to my vps so you see that europe can have some very low that sometimes we really have to take some extra accounting for in in our functions so as you can see the latency is now very stable doesn't jump around in extreme values and this should make sure that our client time flows consistently now you might be wondering okay then why is this latency here so extreme sometimes or why is the first value sometimes so extreme well the first time we set latency we don't average it out over a couple of seconds so if you have some really bad luck this latency right here could be 200 milliseconds if that particular packet was lost but that is only at the beginning of the game if the player experienced a little bit of weirdness at the beginning of the game i think that it's better to experience a little bit of weirdness instead of having to wait four and a half seconds every time he logs in to set the server time correctly or the clock time correctly so it's a it's a it's a balance between the two i would say so there's one little bit of piece of code that i still owe you here that's this one and a half sentence with the client clock we're also including that delta latency that we calculate in that latency adjustment function so every time delta latency is set we then include that within the client clock and on the next line we return that delta latency back to zero by subtracting the delta latency from each other and now that i look at this coke you know what we can just as well say delta late c is zero at this point wait until the next delta later c is calculated delta latency changes again we included in the clock and then we set it to zero that way we don't include the delta latency every single frame otherwise the clocks would start to be desynchronized so that's it with this code you got yourself a clock synchronized with your server time now lastly i have one extra demonstration for you i also here on my receive world state function that we have from tutorial number 10 or 11. i got an extra print we got the world state that has a timestamp so let's print that timestamp and let's print the client clock we'll keep these latencies being printed i'll make sure that we delete that delta value out there and now we can check if indeed our clocks stay within 25 milliseconds of each other we'll have to add the latency that's constantly being recalculated to the timestamp of the world state as that is the average latency that that will say packet would have experienced itself and then we can match that to our client clock now there's going to be a lot of prints here because that's printing 20 times per second so we'll have to find well the first value new latency is 18 let's just take a random value here this is 1238 so we would expect that clock on the client to be 1256 and it is 68 so that's 12 millisecond deviation within our 25 maximum deviation that we set as a requirement let's take another one 756 plus 18 is 74 and we're at 85. and if we check maybe in a different section let's see here we have an average latency of 19. let's take this one 61 71 and our latest here at 69. so that's a minus 2 millisecond deviation still within the 25 that would have as a as a maximum at which point i would say the code doesn't work the way i wanted to work that was it for today guys hope you like it if you did smash that like button hit subscribe don't forget that little bell icon to make sure you don't miss out on the next tutorial in this golden multiplayer series i know this tutorial was a pretty tough one the concept of clock synchronization is is not that easy so you know pause that code whenever you need to and have a look at it again make sure you test it with these sort of prints to ensure that your clock is properly synchronized and if you have any problem leave a comment down in the comment section below or join the discord server and ask your question there i hope to see you on the next tutorial and until then keep on gaming keep on coding see you later guys
Info
Channel: Game Development Center
Views: 3,648
Rating: undefined out of 5
Keywords: Godot Multiplayer, Godot Multiplayer Tutorial, Godot Clocks, Godot Time, Godot Clock Synchronization, Godot Time Synchronization, Godot Multiplayer Syncing, Godot Network, Godot Networking, Godot Dedicated Server, Godot Multiplayer Server, How to make a multiplayer game, Godot MMO, Godot Beginner Tutorial, Godot 2d Tutorial, Godot Tutorial, Godot
Id: TwVT3Qx9xEM
Channel Id: undefined
Length: 19min 24sec (1164 seconds)
Published: Thu Dec 24 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.