Hi everyone! This particular topic will span 2 videos, so if you have any comments or questions, I may be able to address them in Part 2! Don't forget to click the LIKE button when you find something useful in the video!
This has been an invaluable resource for getting my head around how to implement this. I have successfully used this to implement a variation of the whole concept in a very different game within Godot. Thank you for sharing.
I'm now right before doing reconciliation in the video but I tested my game and it is weird. All clients connected to the server/host have the same move speed but the host for some reason is faster than everybody else... In server mode, everybody is perfectly at the same speed. Any idea why?
Same here man also other physics objects on the server side move super fast , I figured it was because of calling simulate movement and process movement on the host so the host moved twice as fast as everyone what I did to fix it was : instead of stepping the physics by time.fixeddeltatime I used : float step = timer.mintimebetweenticks/((1/time.fixeddeltatime)
@git-amend I don't understand something about the logic in the ReconcileState function. Within the while loop, physics isn't actually happening right? So adding force and adding torque each tick within the loop doesnt actually achieve movement tick by tick does it?
I'm having a weird problem where the host kart that first joins is working fine, but any karts that join afterwards are having their Rigidbody's isKinematic setting enabled. I can fix this by just making sure to check its off once i call handleMovement, but I'm wondering if you would have an idea why this is happening, and how I can prevent it from spawning each kart this way? I've also tried setting the kinematic property to false in the awake method and spawn method, but i always have to fallback to the handleMovement method to fix it.
Thanks SO much for sharing your knowledge! I wanna ask an off top question: how did you manage to pull "Create field" and "Create Unity serialized field" higher in the Rider Context Action menu? I couldn't find any information about it. May be you should consider to create a video about Rider tricks that you use?
Interesting question - and actually that was not my doing. It's probably because at the time of recording I was using the Early Access version of Rider which probably just shifted them around. That version of Rider is now the latest stable version. In regards to a video about Rider tricks, that's a good idea actually, maybe on the topic of refactoring.
Hey man! Amazing video. I was able to follow it without any errors about a month back but now I have come back in need of help lmao. Is there anyway make an object teleport, such as when respawning the player? Whenever I try to modify the players position with ServerRPC's the server reconciliation kicks in and recalls me back to its expected position. Thank you!
@@adamgrey268 I think i'm still a bit confused. The server reconciliation is also server authoritative, so when I use a ServerRpc to move the player they just fight for controll, and because the reconcilation is called every frame, I dont end up teleporting anywhere. Ive checked this is the case by calling the tp every frame I press a certian button, and that works, but as soon as i let go of the button the player reconciles back to the old, non teleported position.
oh and I just thought I might add that clearing the input buffer teleports me to world position 0,0,0, but I cant get it to teleport me to anywhere else
Hi there! Just curious if you were ever able to resolve this. I was able to in at least in a hosted environment (not dedicated server), but should work there too. At a high level, at the time of respawning I'm resetting the state by clearing all the buffers and setting states to to default. Additionally, I'm preventing Reconciliation code from running if IsHost (because it shouldn't need it). Hopefully this helps someone, or someone can teach me a better way!
@@DrewofDonuts Hey man, Thank you for the reply! I decided to move on to a new project that is more within my skill range at the moment, but incase I ever revisit server reconciliation I am sure that your comment will serve useful. Thank you!
This video was so good! Hope you keep growing and more people can see it. I have a question regarding the ClientNetworkTransform. If the transform is controlled by the clients themselves for the client-prediction, then if he somehow changes the position of his kart outside the KartController (for example, I tried in the editor itself), the position would be changed by the ClientNetworkTransform, meaning the host/server would accept the new transform. Would that make the server not Authoritative allowing the players to cheat?
Yes. Whenever the client is setting its own state, it is client authoritative. This is not a true server authoritative solution - the server would have to do the re-sync by rolling back the client. The client should never be trusted with rolling back itself, i.e. setting it's own state.
if client disables handleServerReconciliation, his position can be out of sync, so if your enemy is on (x, y) (0,0) and cheater on his client on (-1, 1) but on server cheater is on (0, 1) and cheater does RPC call with tickNumber and rotation to shoot his enemy, then server will do a raycast with rotation that cheater passed in RPC cheater passed diagonal rotation (left and up), but on server his enemy is to the left, so he won't hit his enemy
yeah after figuring that out I was able to piece together my own approach to this (it's fundamentally the same but a little different in execution) The reason I still think reconcilliation should be handled by the server is that that is also where you may include some anticheat enforcements on the server side, which you would not want the client to be able to disable. Of course, anti cheat goes deeper than just that code
@@HalfBlind05 but handleServerReconciliation is meant for getting out of sync, client can't interfere server simulation, so in this case movement is cheat free
yeah I agree if the only thing you're worried about is movement, but for more complicated scenarios with, for example, attacking, you would likely go with an approach that favors the attacker, which means when it comes time to verify that the player's position was in the correct place to execute an attack (based upon the position the client sends to the server, which as you mention is effectively their "real position"), we would want that check and any possible reprecussions to be handled server side. You could also just split the functions, have the client handle its own reconcilliation, and have the server handle anti-cheat, but the code where it is done would look similar.
Great tutorials! However, I'm a bit confused about this one thing. If you have a client network transform constantly updating it's position on the server, does that mean that when we check for differences between the client and server we are only checking for differences that occur on a one tick timeframe? Because in my mind, after that one tick, the client position, even if incorrect, would become the server position due to the Client network transform. Thanks again for the tutorial.
Your understanding is correct, and it's why we run an independent timer that ticks much faster than the normal game update loop so that the reconciliation is applying it's own corrections before Unity is doing it's thing.
@@git-amendoh okay that makes sense, but when you say our timer ticks much faster than the normal game update, are you referring to fixed update? Wouldn't that only be a difference of 10 per second? (60 ticks per second vs 50 fixed updates per second)?
@@HalfBlind05 I'm referring to the NetworkTimer class. This object helps define the source of truth for where the Player should be at any given time. If during any of those ticks the Player becomes out of sync, a correction is applied. There are many executions of the NetworkTimer.ShouldTick() method every FixedUpdate which is storing state in the buffer on both the client and the server.
AMAZING video, i've been able to implement client prediction in my 2D multiplayer game and make it work properly thanks to this tutorial! This whole system might be a bit ambitious for the scope of my project, but i think it's better to do things properly from the start... I only have one question, which is how to use this on multiple mecanics, for example projectiles with physics, interactions between players and networked objects in the scene, ect... Should i recreate all the functions in every script i need prediction in ? or perhaps make some kind of manager out of it ?
Thanks! Glad it helped you out! I think you could recreate this with most mechanics, but I would do it judiciously because it's going to start eating up your bandwidth the more you need to do it. For most objects, the interpolation that is built into the Network Manager will be enough - I would only worry about it on core mechanics where you know there will be an issue with fast moving objects, and let Unity handle the rest.
@@git-amend I see! So probably only the movment of the players, and maybe the projectiles since it's a core mecanic... I'll have to test that. Either way, thanks a lot for your quick anwser and cool video, i'll be sure to check out your other tutorials :D
Thank you thank you for this project series! Super helpful. I have a question regarding the purpose of the ClientNetworkTransform. I’m assuming it’s there to handle relaying its transform to other clients (since the server is already processing movement). Couldn’t we also locally sync all non-owned clients’ transforms to their latest known server state (instead of using ClientNetworkTransform)? Is the ClientNetworkTransform then mostly being used for its interpolation capability?
Yes, you could sync all the clients transforms manually. But this is really an unnecessary amount of work since the NetworkTransform component already handles some of the trickier aspects of transform synchronization. So, if you want to benefit from the NetworkTransform and still be client authoritative, you need to override the base component. Additionally, in the next video we get into flipping the authoritative mode on the NetworkTransform as well, so that a client that is lagging really badly is forced to be server authoritative until corrected.
@@git-amend Got it! And I've seen the next video (also very helpful!) A follow-up thought: I guess I'm still a little confused about the fact that the server player is both processing movement to determine its state (including transform), and is having its transform updated by the client player's ClientNetworkTransform. For some reason I thought this would've caused some sort of conflict or visual artifact on the server side. Maybe I need to look more into how NetworkTransform works o.o
Hey, I've implemented this with unity's relay system and the clients' movement is always correct, but the hosts movement does not get transmitted to the clients and just ends up standing there. Any idea why this might be happening?
I've checked both handle server tick and handle client tick a few times and they are the same as yours, same as the ServerRpc functions if that helps at all.
@@shiiroitori5403 Could be any number of things. My only suggestion is to watch the next 2 videos because there are further changes, and make sure to compare your code with what's in the repository. I know Netcode bugs can be hard to diagnose, but it's usually something simple.
@@git-amend Could this be caused because I am using unity's basic input system rather than the new input system? If not then I think this an issue thats outside of my abilities lol.
I'm using this for a space ship controller. It always has a forwad movement vector and rotates based on the player's input. I'm facing a lot of stutter rotating left/right because of it being in FixedUpdate, rather than Update itself. Could I duplicate the HandleClient and HandleServer Tick into Update on top of having it in FixedUpdate? That feels like the wrong approach. Does anyone have any ideas?
Also, for me if I use a dedicated server, which i will, if on my client I raise the moveSpeed, it still syncs with the server and no reconciliation happens for some reason... I'm not sure where this is from... Even after following the entire video and code, it's like it is still the client who dictates the movement
Hi, can you please explain how does the client prediction works on host? As host is the server and the client at the same time, so when we run handleServerTick it is simulating the tick that was already simulated by the same entity. I got this problem when I was implementing client prediction in my game and my control system does work not on host because of this.
On the host, the server and client will always be on the same tick because of zero latency. However, in this project, the flow of logic is to handle input, handle the client (and check to see if we need to reconcile) and then handle the server. Because we want to validate our position based on the latest information the client has received from the server, only the previous server state is known to both the client and the server on the host at the point in the logic where we compare states. An alternative approach would be to flip the logic around so that the server runs its logic before the client moves. If done that way, you could use the currentTick when doing a reconciliation (on a host only) which isn't a bad idea either. The way it is setup in this project is handle Input > handle Client and reconcile previous tick > handle Server and send authoritative state. This way all the players are able to move one (or more) ticks ahead of the server all the time, including the host. I can't diagnose why your control system isn't working without seeing your code, but I suspect it might have something to do with which index in the buffer you are comparing. Compare your code against the code in the repository linked in the description. I hope that helps!
@@git-amendthanks for a quick answer. But it seems I phrased my question wrong: first we run the HandleClientTick where we simulate the current tick with ProcessMovement(Wich as I understand should change the position of the gameObject) then as there is no latency on the same frame we run HandleServerTick where in the queue there is only one item(the tick which has been done on the same frame) and according to the code we run another ProcessMovement. So as I see it we just ran two ProcessMovement on one frame on the same gameObject and such a problem will not occur on a regular client because there is two different gameObjects one on client side one on server(host).
@@КондрачукЯрославВолодимирович Apologies for the delay in responding to your question. In most networked game architectures where one player acts as both the host (server) and a client, there are generally two instances of the player's game object: one on the server side and one on the client side. These instances are synchronized to maintain game state consistency. Now, diving into what is going on under the hood in Unity's implementation is beyond the scope of what I have time for, though I'm sure it contains plenty of valuable insights. From blackbox testing, it seems clear that these are two different things, and even if you ProcessMovement on both the server and the client, the host does not move 2x faster than a player who is not the host. This is easiest to see at the end of the Lobby/Relay video, where I show the Host and then a regular player in the same game - you can see no noticeable difference in movement speed. I know the Netcode for GameObjects documentation is a little thin, but if you take a look at the advanced topics, you can see this described in the diagrams as the "client host version of obj" and "obj," which seems to make it clear that there are 2 versions of the networked object, including on a Host. That is still vague, but I don't see any reason to think it would be otherwise. See advanced topics here: docs-multiplayer.unity3d.com/netcode/current/advanced-topics/message-system/execution-table/
Hey man This was a wonderful video indeed. Thank you so much I have a question regarding the tick system. I think I'm missing something regarding its nature. How does this system ensure all players are and the server are on the same tick ? Or maybe it doesn't matter cause they will be simulated based on their tick number ? I'm confused. I don't know what I don't know, like what if a player joins the game 5 seconds later than the others, how does his tick match the server ? I'm sorry if I sound stupid. There is a lack of knowledge in my mind that has caused confusion and I don't know that it is
Try not to think of it as a synchronization system between all players. In a multiplayer game there will always be two versions of each player - a version on the client and a version on the server. This system is synchronizing these 2 versions for each player. This is happening with all players individually and not as a whole. There is no global tick for all players.
Many thanks for this tutorial. Great work! But I'm having some trouble. In my game, the host is always moving approx. twice as fast as the clients, while the clients move at the correct speed. Any Idea what I could have done wrong? I used 90% of your code and I'm not using rigidbodies, instead I use a character controller. Not calling ProcessMovement in HandleServerTick gives me normal host movement but of course no client movement at all. I also had to comment out the SwitchAuthorityMode method. I get really strange and jaggy behaviour for the client with this.
Netcode bugs are the worst. One suggestion is to watch at least the first part of the next video in this series because I made a few more changes to handling the differences between Client and Host. You may need to add a guard clause to your movement code so that you only run it once for the Client and once for the Server - that sounds like the main issue. If you continue to have problems, try only simulating physics on the server side. It's been a while since I made this video, but I hope that helps - all the code is still in the repository.
Thank you. I already use the code from the repository for comparison. I will continue my bug hunt this weekend. It really feels like the host does process its own movement twice. @@git-amend
I'm happy to report, that the problem is solved! It was indeed the ProcessMovement called in HandleServerTick. With the Kart project, you can use your regular movement method for simulating, since you set the character positions directly. I however move the character controller by a vector. So it really did move twice per tick. Thanks again :) @@git-amend
I have issues (and questions) about a lot of things. I'm wondeing what's the point of adding a network transform and network rigidbody since we implement the logic ourselves. I had to disable the transform to have a smooth execution server side because it was only using the transform instead of our logic that uses the inputs only. The network rigidbody caused even more trouble since it makes the object kinematic if it's not authoritative so the server couldn't basically run physics and was just relying on the network transform to do the movements. (because at the begining we set the object as "client authoritative") That said, i'm really grateful for this tutorial, I don't think i'd be able to code such a complex and clean implementation on my own.
The first 7 minutes will get you up and running with Netcode about as fast as possible, and it's the easy part. Past the 7-minute mark of this video is where the advanced part starts. See how the first section goes for you, experiment with Netcode a bit and take it from there! There are 2 videos before this one in the playlist about building a Kart Controller and an AI driver, you don't need to watch those necessarily; they are not specifically about multiplayer.
checking in here after 5 straight 14 hour days of learning and testing. its like im a different person now XD its a real different way of thinking but starting to get the hang of it, thanks for the vids!@@git-amend
The UI buttons added through your package don't seem to do anything when pressed, any idea why that could be? EDIT: Nevermind! It was because i didn't have an EventSystem anywhere
I just tested something and this is not really server authoritative because whenever i cheat values on the client, the server just accepts them for some reason... I'm not sure where i'm doing it wrong to be honest
Multiplayer games are complex, and there is no possible way that myself or anyone reading these comments is going to be able to help you diagnose issues you are having from these brief comments, or to be frank - any size of comment, because we cannot see your project. One mistake in the code, and things won't behave properly. The source code is available to help you, and that's the best guidance you can get without having a collaborator. It's most likely that you've made at least one major error in your code when differentiating between Client/Host/Server. I also recommend watching the other videos in this series because there are more changes to the codebase later on, and on top of that there have been updates to the source code since this video was released a year ago.
@@git-amend Well then one question regarding the client network transform. I feel like since it's on the player, the movement feels client sided after the first video which shouldn't really be the case right? Cause after video 1 I should have a server auth movement with client prediction? And also what if I'm using dedicated server and not host.. Is it still all the same? Cause what is happening for me right now is that at runtime if I modify my moveSpeed variable, then the client moves faster on his machine but also on the server which makes no sense...
You mention that you don't actually want to call a serverRpc every frame, and things should be consolidated. Can you elaborate on that? How could I reduce the calls to the server?
Great question. Every game is going to be different, but ideally you want to optimize the calls to the server in regard to frequency and size as much as possible. I would say a good starting point would be every 0.2 seconds. What a lot of games do is send frequent packages of only the bare minimum (only the thing that changed, when it changes) and then every so often send a larger package that contains enough data to do a full reconciliation. To optimize the network calls, the key is to aggregate game data over a certain number of ticks before triggering an RPC. You can package this accumulated data into a Data Transfer Object (DTO), a lightweight object designed to minimize overhead. The DTO could be a struct containing several InputPayloads or StatePayloads, and it could also include other essential information like a timestamp and NetworkObjectId. Then, your methods for sending and receiving have to be modified to handle several ticks worth of data each time they are called, instead of just one. For serialization of a DTO, Unity provides two high-performance classes you can leverage: FastBufferWriter and FastBufferReader. These classes are part of Unity's Netcode and are specifically designed for efficient network communication. There is an example in the documentation here: docs-multiplayer.unity3d.com/netcode/current/advanced-topics/fastbufferwriter-fastbufferreader/ Also, the tool I mention in the next video - SmoothSync - makes use of what I just described (and more), so if you own that tool, you can see a 'real' example there as well that is fully commented, and it is much more involved than something I can demonstrate in a YT video! Hope this answer helps!
@@git-amend Thanks for the response, this is very helpful! Can you also explain why the reconciliation threshold is so high? With a difference of 10 units, doesn't that mean someone could teleport 8 units forward and the server wouldn't reconcile them?
@@GaryHoliday13 For the threshold, it really depends on your own preferences and on the speed of the network. The more lag a player is experiencing, the higher the threshold should be, and the opposite is also true. With all things like this, try to find a happy medium where you aren't imposing server authority so often that it negates the responsiveness and snappy gameplay of client prediction while still enforcing the server authority after a certain point - there has to be a certain amount of leeway, or the game might suffer the same slow response times that games without client prediction have. Of course, the lower the value can be, the better. You could lower the threshold more as the players get close to finishing the race, and you could also change the threshold at runtime based on the ping between the server and the client, but that's beyond what I can type out in a comment! Just some food for thought.
Interesting, I guess I thought we would want every position that takes place on the client to be the exact same as the server. So instead of having a threshold it would directly compare positions at a given tick. If the client was acting in good faith, wouldn't that always be the case? Of course packets can be lost, but even that can be accounted for by monitoring what tick you are processing. Why are we giving any leeway?
@@GaryHoliday13 By all means keep it as low as possible, as I mentioned - the lower the better. I wouldn't put it at zero though, that's not realistic. It can probably be much smaller than 10 however, maybe even less than 1. Try it out with some latency and see how low you can get it without the Kart jumping around. In a future video we'll be working more on the braking and that will also make it more deterministic.
Hi How to change host ownership without disconnecting players in netcode and relay In this case when host player disconnect i want to change host owner without make all players rejoin Please help
I'd say 'yes' to that, especially with new features like the 2023 Multiplayer Play Mode and the Awaitable class, which has made it possible to build WebGL multiplayer games. However, it is still very complex, with too much boilerplate code needed. IMO it's a good idea to spend some extra time when you're working on your first or second Netcode game to start building your own library of code to act as a facade to manage connections with Lobby and Relay, help you easily create various data structures, provide a Player Settings entry for configuration, and handle the synchronization and serialization of data more efficiently. This is something I've been doing myself. After working on a few Netcode games, you start to notice how often you're re-writing the same code, and it can get pretty tedious. Having a library to handle the common tasks can really help cut down on repetition and make the process a lot more enjoyable - and repeatable.
@@hmbullen I think some work would be involved to make that switch, but not as much as you might think. A dedicated server, for the most part, is just a Host without a Client.
hello sir i am thoroughly goin through all ur videos yu re a godlike programmer just a question my client handle work fine after i integrate to different built project but when i use handleservertick then after queue items are used in server the server gets very jittery in y axis where yu used with y :4 since my car is always at 2.4 to 2.6 i even tried to set it to 2.4 its jittery coming front and back how can i fix this but im not using reconcile timer ?? please help sir
we call the ProcessMovement() method with the Move() method on the client and on the server, why is this? Shouldn't the calculation of the movement occur on the server only and send the calculated data to the client?
The intent of this video is to demonstrate how you can have the client control movement, but validate and potentially reconcile that movement afterwards with data from the server. Both the client and the server have to calculate movement to do this. The reason people use this technique in faster pace games is to avoid delays while receiving server data.
@@git-amend Got it, then one more question, is it possible to drag all the logic to the server and send data to the client in order to reduce calculations on the client? Is it possible to also enable multi-threading on the server, like a job system maybe? Because my client is WebGL, but server is linux platform, and i would deacrease fps on client
@@esteticachannel4604 Yes, you can of course do all the calculation on the server and just send position information to the client - as long as you are sending it fast enough, it would probably be fine. This is what most slower paced games do, as you probably know. As for specifics of your server/client and using multi-threading / jobs, I think that is too specific for me to comment on. It is possible of course, but it really depends what you are trying to achieve. In most games of this nature, the bottleneck isn't the processing - it's the network latency.
@@git-amend I understood, that is, it turns out that it is the network delay that affects the FPS more than the computing load, as I understand it? and it turns out that in your demonstration you are optimizing the network load? It’s just that I only have 30 player connections on the server and for some reason the server has a very heavy load on the CPU specifically.
Hi everyone! This particular topic will span 2 videos, so if you have any comments or questions, I may be able to address them in Part 2! Don't forget to click the LIKE button when you find something useful in the video!
This has been an invaluable resource for getting my head around how to implement this. I have successfully used this to implement a variation of the whole concept in a very different game within Godot. Thank you for sharing.
Excellent! Glad to hear that!
Man I have watched this video 4 or 5 times now.... thanks for putting this out there, your content is invaluable
I appreciate that!
This is a great video, probably the best one I've seen for beginning this subject. Thanks 🙂
Glad it was helpful!
Client transform script saved me! (As well as the rest) great video!
Awesome, I'm really glad to hear that! Thanks for the super!
This deserves more likes. High quality tutorial.
Thanks!
I'm now right before doing reconciliation in the video but I tested my game and it is weird. All clients connected to the server/host have the same move speed but the host for some reason is faster than everybody else... In server mode, everybody is perfectly at the same speed. Any idea why?
Same here man also other physics objects on the server side move super fast , I figured it was because of calling simulate movement and process movement on the host so the host moved twice as fast as everyone what I did to fix it was : instead of stepping the physics by time.fixeddeltatime I used : float step = timer.mintimebetweenticks/((1/time.fixeddeltatime)
@git-amend I don't understand something about the logic in the ReconcileState function. Within the while loop, physics isn't actually happening right? So adding force and adding torque each tick within the loop doesnt actually achieve movement tick by tick does it?
I'm having a weird problem where the host kart that first joins is working fine, but any karts that join afterwards are having their Rigidbody's isKinematic setting enabled. I can fix this by just making sure to check its off once i call handleMovement, but I'm wondering if you would have an idea why this is happening, and how I can prevent it from spawning each kart this way? I've also tried setting the kinematic property to false in the awake method and spawn method, but i always have to fallback to the handleMovement method to fix it.
Thanks SO much for sharing your knowledge! I wanna ask an off top question: how did you manage to pull "Create field" and "Create Unity serialized field" higher in the Rider Context Action menu? I couldn't find any information about it. May be you should consider to create a video about Rider tricks that you use?
Interesting question - and actually that was not my doing. It's probably because at the time of recording I was using the Early Access version of Rider which probably just shifted them around. That version of Rider is now the latest stable version. In regards to a video about Rider tricks, that's a good idea actually, maybe on the topic of refactoring.
God bless you sir 🙏 This helped save my project, very much appreciated!
You're welcome!
Let's gooooo!!! Awesome stuff.
Nice!!!! Thanks!
Server ticks, buffers, and reconciliation are the advanced topics I need and don't get from most UA-cam tutorials. Thanks, Git-amend!
Hey man! Amazing video. I was able to follow it without any errors about a month back but now I have come back in need of help lmao. Is there anyway make an object teleport, such as when respawning the player? Whenever I try to modify the players position with ServerRPC's the server reconciliation kicks in and recalls me back to its expected position. Thank you!
You need server side logic for teleporting the player too. Remember that the server is authoritative and has the last say in where the player is.
@@adamgrey268 I think i'm still a bit confused. The server reconciliation is also server authoritative, so when I use a ServerRpc to move the player they just fight for controll, and because the reconcilation is called every frame, I dont end up teleporting anywhere. Ive checked this is the case by calling the tp every frame I press a certian button, and that works, but as soon as i let go of the button the player reconciles back to the old, non teleported position.
oh and I just thought I might add that clearing the input buffer teleports me to world position 0,0,0, but I cant get it to teleport me to anywhere else
Hi there! Just curious if you were ever able to resolve this. I was able to in at least in a hosted environment (not dedicated server), but should work there too. At a high level, at the time of respawning I'm resetting the state by clearing all the buffers and setting states to to default. Additionally, I'm preventing Reconciliation code from running if IsHost (because it shouldn't need it). Hopefully this helps someone, or someone can teach me a better way!
@@DrewofDonuts Hey man, Thank you for the reply! I decided to move on to a new project that is more within my skill range at the moment, but incase I ever revisit server reconciliation I am sure that your comment will serve useful. Thank you!
This video was so good! Hope you keep growing and more people can see it.
I have a question regarding the ClientNetworkTransform.
If the transform is controlled by the clients themselves for the client-prediction, then if he somehow changes the position of his kart outside the KartController (for example, I tried in the editor itself), the position would be changed by the ClientNetworkTransform, meaning the host/server would accept the new transform. Would that make the server not Authoritative allowing the players to cheat?
Yes. Whenever the client is setting its own state, it is client authoritative. This is not a true server authoritative solution - the server would have to do the re-sync by rolling back the client. The client should never be trusted with rolling back itself, i.e. setting it's own state.
Thanks! That's what I thought
Also, what is preventing a cheater from preventing the call of handleServerReconciliation() ?, doesn't that need to be run on the server?
exactly i was wondering that too
if client disables handleServerReconciliation, his position can be out of sync, so if your enemy is on (x, y) (0,0) and cheater on his client on (-1, 1) but on server cheater is on (0, 1) and cheater does RPC call with tickNumber and rotation to shoot his enemy, then server will do a raycast with rotation that cheater passed in RPC
cheater passed diagonal rotation (left and up), but on server his enemy is to the left, so he won't hit his enemy
yeah after figuring that out I was able to piece together my own approach to this (it's fundamentally the same but a little different in execution)
The reason I still think reconcilliation should be handled by the server is that that is also where you may include some anticheat enforcements on the server side, which you would not want the client to be able to disable.
Of course, anti cheat goes deeper than just that code
@@HalfBlind05 but handleServerReconciliation is meant for getting out of sync, client can't interfere server simulation, so in this case movement is cheat free
yeah I agree if the only thing you're worried about is movement, but for more complicated scenarios with, for example, attacking, you would likely go with an approach that favors the attacker, which means when it comes time to verify that the player's position was in the correct place to execute an attack (based upon the position the client sends to the server, which as you mention is effectively their "real position"), we would want that check and any possible reprecussions to be handled server side.
You could also just split the functions, have the client handle its own reconcilliation, and have the server handle anti-cheat, but the code where it is done would look similar.
Great tutorials! However, I'm a bit confused about this one thing. If you have a client network transform constantly updating it's position on the server, does that mean that when we check for differences between the client and server we are only checking for differences that occur on a one tick timeframe? Because in my mind, after that one tick, the client position, even if incorrect, would become the server position due to the Client network transform. Thanks again for the tutorial.
Your understanding is correct, and it's why we run an independent timer that ticks much faster than the normal game update loop so that the reconciliation is applying it's own corrections before Unity is doing it's thing.
@@git-amendoh okay that makes sense, but when you say our timer ticks much faster than the normal game update, are you referring to fixed update? Wouldn't that only be a difference of 10 per second? (60 ticks per second vs 50 fixed updates per second)?
@@HalfBlind05 I'm referring to the NetworkTimer class. This object helps define the source of truth for where the Player should be at any given time. If during any of those ticks the Player becomes out of sync, a correction is applied. There are many executions of the NetworkTimer.ShouldTick() method every FixedUpdate which is storing state in the buffer on both the client and the server.
@@git-amend Okay, thanks again!
AMAZING video, i've been able to implement client prediction in my 2D multiplayer game and make it work properly thanks to this tutorial! This whole system might be a bit ambitious for the scope of my project, but i think it's better to do things properly from the start... I only have one question, which is how to use this on multiple mecanics, for example projectiles with physics, interactions between players and networked objects in the scene, ect... Should i recreate all the functions in every script i need prediction in ? or perhaps make some kind of manager out of it ?
Thanks! Glad it helped you out! I think you could recreate this with most mechanics, but I would do it judiciously because it's going to start eating up your bandwidth the more you need to do it. For most objects, the interpolation that is built into the Network Manager will be enough - I would only worry about it on core mechanics where you know there will be an issue with fast moving objects, and let Unity handle the rest.
@@git-amend I see! So probably only the movment of the players, and maybe the projectiles since it's a core mecanic... I'll have to test that. Either way, thanks a lot for your quick anwser and cool video, i'll be sure to check out your other tutorials :D
Thank you thank you for this project series! Super helpful.
I have a question regarding the purpose of the ClientNetworkTransform. I’m assuming it’s there to handle relaying its transform to other clients (since the server is already processing movement). Couldn’t we also locally sync all non-owned clients’ transforms to their latest known server state (instead of using ClientNetworkTransform)? Is the ClientNetworkTransform then mostly being used for its interpolation capability?
Yes, you could sync all the clients transforms manually. But this is really an unnecessary amount of work since the NetworkTransform component already handles some of the trickier aspects of transform synchronization. So, if you want to benefit from the NetworkTransform and still be client authoritative, you need to override the base component.
Additionally, in the next video we get into flipping the authoritative mode on the NetworkTransform as well, so that a client that is lagging really badly is forced to be server authoritative until corrected.
@@git-amend Got it! And I've seen the next video (also very helpful!) A follow-up thought: I guess I'm still a little confused about the fact that the server player is both processing movement to determine its state (including transform), and is having its transform updated by the client player's ClientNetworkTransform. For some reason I thought this would've caused some sort of conflict or visual artifact on the server side. Maybe I need to look more into how NetworkTransform works o.o
Hey, I've implemented this with unity's relay system and the clients' movement is always correct, but the hosts movement does not get transmitted to the clients and just ends up standing there. Any idea why this might be happening?
I've checked both handle server tick and handle client tick a few times and they are the same as yours, same as the ServerRpc functions if that helps at all.
@@shiiroitori5403 Could be any number of things. My only suggestion is to watch the next 2 videos because there are further changes, and make sure to compare your code with what's in the repository. I know Netcode bugs can be hard to diagnose, but it's usually something simple.
@@git-amend Alright, I'll try to get it sorted today. I'll let you know what the bug was if I find it. Thank you!
@@git-amend Could this be caused because I am using unity's basic input system rather than the new input system? If not then I think this an issue thats outside of my abilities lol.
Really great advance stuff, tysm
You're very welcome!
I'm using this for a space ship controller. It always has a forwad movement vector and rotates based on the player's input. I'm facing a lot of stutter rotating left/right because of it being in FixedUpdate, rather than Update itself. Could I duplicate the HandleClient and HandleServer Tick into Update on top of having it in FixedUpdate? That feels like the wrong approach.
Does anyone have any ideas?
Also, for me if I use a dedicated server, which i will, if on my client I raise the moveSpeed, it still syncs with the server and no reconciliation happens for some reason... I'm not sure where this is from... Even after following the entire video and code, it's like it is still the client who dictates the movement
Hi, can you please explain how does the client prediction works on host? As host is the server and the client at the same time, so when we run handleServerTick it is simulating the tick that was already simulated by the same entity. I got this problem when I was implementing client prediction in my game and my control system does work not on host because of this.
On the host, the server and client will always be on the same tick because of zero latency. However, in this project, the flow of logic is to handle input, handle the client (and check to see if we need to reconcile) and then handle the server. Because we want to validate our position based on the latest information the client has received from the server, only the previous server state is known to both the client and the server on the host at the point in the logic where we compare states. An alternative approach would be to flip the logic around so that the server runs its logic before the client moves. If done that way, you could use the currentTick when doing a reconciliation (on a host only) which isn't a bad idea either. The way it is setup in this project is handle Input > handle Client and reconcile previous tick > handle Server and send authoritative state.
This way all the players are able to move one (or more) ticks ahead of the server all the time, including the host.
I can't diagnose why your control system isn't working without seeing your code, but I suspect it might have something to do with which index in the buffer you are comparing. Compare your code against the code in the repository linked in the description. I hope that helps!
@@git-amendthanks for a quick answer. But it seems I phrased my question wrong: first we run the HandleClientTick where we simulate the current tick with ProcessMovement(Wich as I understand should change the position of the gameObject) then as there is no latency on the same frame we run HandleServerTick where in the queue there is only one item(the tick which has been done on the same frame) and according to the code we run another ProcessMovement. So as I see it we just ran two ProcessMovement on one frame on the same gameObject and such a problem will not occur on a regular client because there is two different gameObjects one on client side one on server(host).
@@КондрачукЯрославВолодимирович Apologies for the delay in responding to your question. In most networked game architectures where one player acts as both the host (server) and a client, there are generally two instances of the player's game object: one on the server side and one on the client side. These instances are synchronized to maintain game state consistency. Now, diving into what is going on under the hood in Unity's implementation is beyond the scope of what I have time for, though I'm sure it contains plenty of valuable insights. From blackbox testing, it seems clear that these are two different things, and even if you ProcessMovement on both the server and the client, the host does not move 2x faster than a player who is not the host. This is easiest to see at the end of the Lobby/Relay video, where I show the Host and then a regular player in the same game - you can see no noticeable difference in movement speed.
I know the Netcode for GameObjects documentation is a little thin, but if you take a look at the advanced topics, you can see this described in the diagrams as the "client host version of obj" and "obj," which seems to make it clear that there are 2 versions of the networked object, including on a Host. That is still vague, but I don't see any reason to think it would be otherwise. See advanced topics here:
docs-multiplayer.unity3d.com/netcode/current/advanced-topics/message-system/execution-table/
Couldn't the client just send more rpc calls than usual with this? and fill the input queue?
Hey man
This was a wonderful video indeed. Thank you so much
I have a question regarding the tick system. I think I'm missing something regarding its nature. How does this system ensure all players are and the server are on the same tick ? Or maybe it doesn't matter cause they will be simulated based on their tick number ? I'm confused. I don't know what I don't know, like what if a player joins the game 5 seconds later than the others, how does his tick match the server ? I'm sorry if I sound stupid. There is a lack of knowledge in my mind that has caused confusion and I don't know that it is
Try not to think of it as a synchronization system between all players. In a multiplayer game there will always be two versions of each player - a version on the client and a version on the server. This system is synchronizing these 2 versions for each player. This is happening with all players individually and not as a whole. There is no global tick for all players.
@@git-amend Thank you so much !
It totally makes sense now. You're a saviour.
Many thanks for this tutorial. Great work!
But I'm having some trouble. In my game, the host is always moving approx. twice as fast as the clients, while the clients move at the correct speed. Any Idea what I could have done wrong?
I used 90% of your code and I'm not using rigidbodies, instead I use a character controller. Not calling ProcessMovement in HandleServerTick gives me normal host movement but of course no client movement at all.
I also had to comment out the SwitchAuthorityMode method. I get really strange and jaggy behaviour for the client with this.
Netcode bugs are the worst. One suggestion is to watch at least the first part of the next video in this series because I made a few more changes to handling the differences between Client and Host. You may need to add a guard clause to your movement code so that you only run it once for the Client and once for the Server - that sounds like the main issue. If you continue to have problems, try only simulating physics on the server side. It's been a while since I made this video, but I hope that helps - all the code is still in the repository.
Thank you. I already use the code from the repository for comparison. I will continue my bug hunt this weekend. It really feels like the host does process its own movement twice. @@git-amend
I'm happy to report, that the problem is solved! It was indeed the ProcessMovement called in HandleServerTick. With the Kart project, you can use your regular movement method for simulating, since you set the character positions directly. I however move the character controller by a vector. So it really did move twice per tick. Thanks again :) @@git-amend
@@wabugi85Fantastic! I'm really glad you figured that out... tricky problem to diagnose and resolve!
I followed this video and the client moves way slower than the server. Why is that?
Me too! Did you figure this one out?
amazing tutorial!
Thank you!
I have issues (and questions) about a lot of things. I'm wondeing what's the point of adding a network transform and network rigidbody since we implement the logic ourselves.
I had to disable the transform to have a smooth execution server side because it was only using the transform instead of our logic that uses the inputs only.
The network rigidbody caused even more trouble since it makes the object kinematic if it's not authoritative so the server couldn't basically run physics and was just relying on the network transform to do the movements. (because at the begining we set the object as "client authoritative")
That said, i'm really grateful for this tutorial, I don't think i'd be able to code such a complex and clean implementation on my own.
there is also the isOwner thing, but the solution was in the following video.
about to start this video, Do you recommend this tutorial for first time net coders or come back to this after a little experience?
The first 7 minutes will get you up and running with Netcode about as fast as possible, and it's the easy part. Past the 7-minute mark of this video is where the advanced part starts. See how the first section goes for you, experiment with Netcode a bit and take it from there!
There are 2 videos before this one in the playlist about building a Kart Controller and an AI driver, you don't need to watch those necessarily; they are not specifically about multiplayer.
checking in here after 5 straight 14 hour days of learning and testing. its like im a different person now XD its a real different way of thinking but starting to get the hang of it, thanks for the vids!@@git-amend
@@minima_studios I'm glad to hear you are making your way through this one, it is probably the most technical subject on the channel. Nice work!
The UI buttons added through your package don't seem to do anything when pressed, any idea why that could be?
EDIT: Nevermind! It was because i didn't have an EventSystem anywhere
Great, glad you figured it out!
Thank you so much.
You're welcome!
amazing Stuff
Thank you!
I just tested something and this is not really server authoritative because whenever i cheat values on the client, the server just accepts them for some reason... I'm not sure where i'm doing it wrong to be honest
Multiplayer games are complex, and there is no possible way that myself or anyone reading these comments is going to be able to help you diagnose issues you are having from these brief comments, or to be frank - any size of comment, because we cannot see your project. One mistake in the code, and things won't behave properly. The source code is available to help you, and that's the best guidance you can get without having a collaborator. It's most likely that you've made at least one major error in your code when differentiating between Client/Host/Server. I also recommend watching the other videos in this series because there are more changes to the codebase later on, and on top of that there have been updates to the source code since this video was released a year ago.
@@git-amend Alright I makes sense thanks!
@@git-amend Well then one question regarding the client network transform. I feel like since it's on the player, the movement feels client sided after the first video which shouldn't really be the case right? Cause after video 1 I should have a server auth movement with client prediction? And also what if I'm using dedicated server and not host.. Is it still all the same?
Cause what is happening for me right now is that at runtime if I modify my moveSpeed variable, then the client moves faster on his machine but also on the server which makes no sense...
You mention that you don't actually want to call a serverRpc every frame, and things should be consolidated. Can you elaborate on that? How could I reduce the calls to the server?
Great question. Every game is going to be different, but ideally you want to optimize the calls to the server in regard to frequency and size as much as possible. I would say a good starting point would be every 0.2 seconds. What a lot of games do is send frequent packages of only the bare minimum (only the thing that changed, when it changes) and then every so often send a larger package that contains enough data to do a full reconciliation.
To optimize the network calls, the key is to aggregate game data over a certain number of ticks before triggering an RPC. You can package this accumulated data into a Data Transfer Object (DTO), a lightweight object designed to minimize overhead. The DTO could be a struct containing several InputPayloads or StatePayloads, and it could also include other essential information like a timestamp and NetworkObjectId. Then, your methods for sending and receiving have to be modified to handle several ticks worth of data each time they are called, instead of just one.
For serialization of a DTO, Unity provides two high-performance classes you can leverage: FastBufferWriter and FastBufferReader. These classes are part of Unity's Netcode and are specifically designed for efficient network communication. There is an example in the documentation here:
docs-multiplayer.unity3d.com/netcode/current/advanced-topics/fastbufferwriter-fastbufferreader/
Also, the tool I mention in the next video - SmoothSync - makes use of what I just described (and more), so if you own that tool, you can see a 'real' example there as well that is fully commented, and it is much more involved than something I can demonstrate in a YT video! Hope this answer helps!
@@git-amend Thanks for the response, this is very helpful!
Can you also explain why the reconciliation threshold is so high? With a difference of 10 units, doesn't that mean someone could teleport 8 units forward and the server wouldn't reconcile them?
@@GaryHoliday13 For the threshold, it really depends on your own preferences and on the speed of the network. The more lag a player is experiencing, the higher the threshold should be, and the opposite is also true. With all things like this, try to find a happy medium where you aren't imposing server authority so often that it negates the responsiveness and snappy gameplay of client prediction while still enforcing the server authority after a certain point - there has to be a certain amount of leeway, or the game might suffer the same slow response times that games without client prediction have. Of course, the lower the value can be, the better.
You could lower the threshold more as the players get close to finishing the race, and you could also change the threshold at runtime based on the ping between the server and the client, but that's beyond what I can type out in a comment! Just some food for thought.
Interesting, I guess I thought we would want every position that takes place on the client to be the exact same as the server. So instead of having a threshold it would directly compare positions at a given tick. If the client was acting in good faith, wouldn't that always be the case? Of course packets can be lost, but even that can be accounted for by monitoring what tick you are processing. Why are we giving any leeway?
@@GaryHoliday13 By all means keep it as low as possible, as I mentioned - the lower the better. I wouldn't put it at zero though, that's not realistic. It can probably be much smaller than 10 however, maybe even less than 1. Try it out with some latency and see how low you can get it without the Kart jumping around. In a future video we'll be working more on the braking and that will also make it more deterministic.
Hi
How to change host ownership without disconnecting players in netcode and relay
In this case when host player disconnect i want to change host owner without make all players rejoin
Please help
Do you think netcode for game objects makes multiplayer more accessible to indies?
I'd say 'yes' to that, especially with new features like the 2023 Multiplayer Play Mode and the Awaitable class, which has made it possible to build WebGL multiplayer games.
However, it is still very complex, with too much boilerplate code needed. IMO it's a good idea to spend some extra time when you're working on your first or second Netcode game to start building your own library of code to act as a facade to manage connections with Lobby and Relay, help you easily create various data structures, provide a Player Settings entry for configuration, and handle the synchronization and serialization of data more efficiently. This is something I've been doing myself. After working on a few Netcode games, you start to notice how often you're re-writing the same code, and it can get pretty tedious. Having a library to handle the common tasks can really help cut down on repetition and make the process a lot more enjoyable - and repeatable.
Thanks for the reply! With a game like this, if it were to blow up and you needed to move to dedicated servers, would that be a nightmare?@@git-amend
@@hmbullen I think some work would be involved to make that switch, but not as much as you might think. A dedicated server, for the most part, is just a Host without a Client.
Awesome. That's good to know!@@git-amend
might have to kiss you bro
Too kind lol
hello sir i am thoroughly goin through all ur videos yu re a godlike programmer just a question my client handle work fine after i integrate to different built project but when i use handleservertick then after queue items are used in server the server gets very jittery in y axis where yu used with y :4 since my car is always at 2.4 to 2.6 i even tried to set it to 2.4 its jittery coming front and back how can i fix this but im not using reconcile timer ?? please help sir
If only Nintendo understood this code lol
Haha!
we call the ProcessMovement() method with the Move() method on the client and on the server, why is this? Shouldn't the calculation of the movement occur on the server only and send the calculated data to the client?
The intent of this video is to demonstrate how you can have the client control movement, but validate and potentially reconcile that movement afterwards with data from the server. Both the client and the server have to calculate movement to do this. The reason people use this technique in faster pace games is to avoid delays while receiving server data.
@@git-amend Got it, then one more question, is it possible to drag all the logic to the server and send data to the client in order to reduce calculations on the client? Is it possible to also enable multi-threading on the server, like a job system maybe? Because my client is WebGL, but server is linux platform, and i would deacrease fps on client
@@esteticachannel4604 Yes, you can of course do all the calculation on the server and just send position information to the client - as long as you are sending it fast enough, it would probably be fine. This is what most slower paced games do, as you probably know. As for specifics of your server/client and using multi-threading / jobs, I think that is too specific for me to comment on. It is possible of course, but it really depends what you are trying to achieve. In most games of this nature, the bottleneck isn't the processing - it's the network latency.
@@git-amend
I understood, that is, it turns out that it is the network delay that affects the FPS more than the computing load, as I understand it? and it turns out that in your demonstration you are optimizing the network load? It’s just that I only have 30 player connections on the server and for some reason the server has a very heavy load on the CPU specifically.