1. We've introduced thread tags, search within a thread and similar thread search. Read more here.
    Dismiss Notice
  2. Learn how you'll soon be able to publish your games to China in four simple steps with Xiaomi. Sign up now for early access.
    Dismiss Notice
  3. Get further faster with the Unity Plus Accelerator Pack, free for new Unity Plus subscribers for a limited time. Click here for more details.
    Dismiss Notice
  4. We've released our first Timeline Experimental Preview, our new tool for creating cutscenes and more! To check it out click here.
    Dismiss Notice
  5. Unity 5.5 is now released.
    Dismiss Notice
  6. Check out all the fixes for 5.5 in patch releases 1 & 2.
    Dismiss Notice
  7. Unity 5.6 beta is now available for download.
    Dismiss Notice

Unity Multiplayer Unet server-authoritative movement with client-side prediction and reconciliation

Discussion in 'Multiplayer Networking' started by GenaSG, Aug 24, 2015.

  1. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Hello everyone,
    So I've tried several times to find some information or examples on this topic but with no luck.
    Found some legacy implementation but it looks like that there is no open implementation/tutorial how to do such functionality using brand new UNET.
    So I've decided to write it myself. Thankfully I've found this awesome post. And it helped me a lot.
    I decided to share my code with the community so hopefully no one is going to feel the pain of writing it from ground up ;)
    Here is the link to github repo.
    I'm open for suggestions and pull requests.
    Cheers
     
  2. noise256

    noise256

    Joined:
    Apr 7, 2013
    Posts:
    18
    Thanks for this, I've been struggling with client side prediction and it looks like this will be a big help. I'll try and report back once I've got into the code a bit more.

    Just a quick suggestion but would it not be more efficient to separate position and rotation as the entire struct is marked as dirty if only one of them changes?

    edit:

    Lines 105 - 114. That if statement will never return true surely?

    Code (CSharp):
    1.                 //Sending results to other clients(state sync)
    2.                 Vector3 lastPosition = transform.position;
    3.                 Quaternion lastRotation = transform.rotation;
    4.                 if(Vector3.Distance(transform.position,lastPosition) > 0 || Quaternion.Angle(transform.rotation,lastRotation) > 0){
    5.                     Results results;
    6.                     results.rotation = transform.rotation;
    7.                     results.position = transform.position;
    8.                     results.timeStamp = inputs.timeStamp;
    9.                     _results = results;
    10.                 }
     
    Last edited: Aug 25, 2015
    GenaSG likes this.
  3. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    628
    Thanks very much for this, I implemented my own solution for this (for a prototype) and it was my first time doing so, so it will be greatly appreciated having another frame of reference.
     
  4. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Hello.
    I hope it will help you.
    About if statement. Correct, that's a dump mistake :). I'll upload fixed version shortly.
    And about separation of rotation and position.
    I've tried to have two different scripts for that. But there are few problems with that. For example. There would be two [Command] call in one frame and from what I've discovered calling [Command] 50 times per second is approximately 1 KBs of traffic. So it would be at least 2.5-3KBs of traffic if using this approach. And I'm trying to keep traffic down. Another problem is a bit more complicated. Rotation must be updated before position. Because you know it's moving related transform.forward :) . And there is no guaranty that rotation would be applied before position. It's internet after all.
    I hope those answers are helpful.
    Cheers
     
  5. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    You're welcome. I hope it would be helpful.
     
  6. FuzzyShan

    FuzzyShan

    Joined:
    Jul 30, 2012
    Posts:
    90
    you are a life saver, better than unity team with their docs.
     
    philwinkel likes this.
  7. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Thanks mate ;)
    I hope it would be helpful.
     
  8. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    I've added possibility to override Move/Rotate and GetInputs functions. So custom movements mechanics can be easily implemented in an inherited class. It would be possible to use UE-like approach with pawns and player controllers. Also I've created two new functions for position and rotation update on non-owner clients.
    Cheers
     
  9. Firoball

    Firoball

    Joined:
    Aug 6, 2015
    Posts:
    60
    Thanks for the code.

    I refactored my own Client Prediction/Reconciliation/Lag Compensation system from old Networking to UNET, but it is not fully complete yet.
     
  10. Firoball

    Firoball

    Joined:
    Aug 6, 2015
    Posts:
    60
    May I ask how you measured that? I remember with old networking I ended up at 1kB/s with ~ 20 RPC calls a second, but I haven't found a proper method yet to meaure in UNET.
     
  11. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Sure. I'm on OS X. I use nettop utility to monitor my network traffic.
     
  12. Firoball

    Firoball

    Joined:
    Aug 6, 2015
    Posts:
    60
    Ok, external tool. That was also what I was planning for now (Windows user, though).
    Thanks for reply.
     
  13. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    You're welcome
     
  14. moco2k

    moco2k

    Joined:
    Apr 29, 2015
    Posts:
    220
    Thanks a lot for sharing your code GenaSG!

    BTW, I think that UNET should support server-authoritative movement with client-side prediction and reconciliation out of the box as this seems to be a fundamental requirement for so many multiplayer games. I hope that they improve on this and bring in some nice docs soon.
     
    Brechtos and hippocoder like this.
  15. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    That's why I've shared this code. It's a must have functionality for realtime multiplayer games. I hope it would be useful.
    P.S. I've found a bug in interpolation functionality. So wait for update))
     
  16. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Crouch and sprint related functionality added.
    Add "Sprint" and "Crouch" bindings to project settings.
    Cheers
     
    moco2k likes this.
  17. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Playing with jumping mechanics. This one is a bit tricky. I've successfully synced falling through network but jumping itself is a bit jerky at the moment.
     
    moco2k likes this.
  18. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Hm, I have a bug with interpolation. List isn't cleared as it suppose to. And still can't add jumping.
     
  19. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Hello everyone,
    I've pushed new update to the repo.
    There is a fix for the interpolation bug. Also I've changed arguments types for virtual functions.
    If you'd find any bugs please give me a shout.
    Cheers
     
    moco2k likes this.
  20. moco2k

    moco2k

    Joined:
    Apr 29, 2015
    Posts:
    220
    Last edited: Sep 1, 2015
    GenaSG likes this.
  21. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
  22. Firoball

    Firoball

    Joined:
    Aug 6, 2015
    Posts:
    60
    Last edited: Sep 1, 2015
    GenaSG likes this.
  23. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Last edited: Sep 2, 2015
    Firoball likes this.
  24. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Added hacky gravity and jump functionality.
     
  25. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    I have a bug with frame dependant mouse look. :(
     
  26. Firoball

    Firoball

    Joined:
    Aug 6, 2015
    Posts:
    60
    Just saw I overlooked the very same link in your opening post... sorry :p
     
  27. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Anyone has any experience with mixing Update and FixedUpdate? To be precise read inputs in Update and apply them in FixedUpdate?
     
  28. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    No problem:)
    Cheers
     
  29. Torigas

    Torigas

    Joined:
    Jan 1, 2014
    Posts:
    60
    We're goin to look at your code and see if we can use it for our multiplayer shooter. Thanks so much for sharing!
     
  30. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    You're welcome.
    But be aware that there is a bug with inputs. At this moment inputs are frame rate dependent. Because they're being read in FixedUpdate.
     
  31. Firoball

    Firoball

    Joined:
    Aug 6, 2015
    Posts:
    60
    Not yet. But I've been at that point.

    At the moment I'm capturing inputs in FixedUpdate, do all movement stuff in Fixed Update and interpolate in Update.
    It works good, but shortly pressed buttons like attack/jump might get lost in Fixed Update. I will add a simple buffer which priorizes "pressed" over "not pressed" to make sure short button presses are not lost.
    Capturing inputs in Fixed Update may cause problems with the time corrected acceleration feature, though. I disabled it, as I'm fine with very simple movement.
     
  32. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    The problem would be with mouse look. You'd get different sensitivity on different FPSs.
    You can try it by adding a lot of grass to your scene. Start in small window and then resize it.
     
  33. Firoball

    Firoball

    Joined:
    Aug 6, 2015
    Posts:
    60
    I don't have mouse look, only transform rotation bound to mouse position relative to player. So I got around that problem ;)
     
  34. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Hm, nice.
    That should work)))
     
  35. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    I've uploaded fix for floating mouse sensitivity.
    Enjoy.
    Cheers
     
  36. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Next thing would be blocking inputs if no updates from server received.
    And that would be probably all for this project.
     
  37. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Hello,
    NetworkPawn is more like a practical use example. You should have CharacterController attached to your player. And to avoid falling through the ground you should just add rigidbody component to your player. Bu remember to disable gravity and enable all constraints.
    Cheers
     
  38. moco2k

    moco2k

    Joined:
    Apr 29, 2015
    Posts:
    220
    One question: Considering that we have very frequent updates here, I was wondering if this needs to use the reliable channel or if it could be changed to unreliable channel as well (I guess I remember some reading suggesting unreliable communication for movement syncing).
     
  39. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    It works well on unreliable channel too. There is a packet time stamp check. So out of order packets would be ignored. But as I remember there isn't much difference in traffic between reliable and unreliable. At least when I last tested it.
     
  40. Firoball

    Firoball

    Joined:
    Aug 6, 2015
    Posts:
    60
    Unreliable data may get corrupted or lost, and this is not checked, while reliable data is guaranteed to be transferred.
    The main difference is that unreliable data does not have handshake stuff to find out the data has been transferred. It's just sent and either arrives or not. The big plus is that all the overhead for securing the transfer, resending, etc. is not there. The data is transferred faster, the package size is smaller. Put otherwise: You can send more data faster, without putting too much stress on your bandwidth.

    The rule is pretty simple:
    Reliable:
    - Single Events (e.g. trigger switches, player shot, item collection)
    - "Rarely" changing data (e.g. player level, monster hit points, inventory stuff)
    - Keyboard/Controller inputs for player movement on client side (unreliable does not work here very well - you wouldn't want to lose a game because you fired a shot which wsa not registered due to unreliable data gone lost)
    - Chat messages

    Unreliable:
    - constant data stream like position data. If something is lost here, a new data snapshot will arrive soon. Interpolation normally can compensate the data loss good enough. If the connection is poor, this will end up in jerky interpolation or depending on your setup also "endless" extrapolation at some point. But at that point, reliable transfer will bog down as well.
     
    Last edited: Sep 4, 2015
  41. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Does any one have any experience in optimisation of network traffic?
    Maybe compression or variable types tricks?
     
  42. Firoball

    Firoball

    Joined:
    Aug 6, 2015
    Posts:
    60
    Not many...

    - collect bools and create a bitmask in uint32
    - transfer rotation always as euler, only the important rotations (usually y axis)
    - if range of a float is clearly limited, use fixpoint representation and a smaller datatype (uint16 or even byte). Best example: rotation - if accuracy of 0.01 is enough, you can always store it in uint16 as fixpoint with rounded (angle * 100) -> range of 0..36000 = 2 byte (0..65535) instead of 4 (float datatype)
    - on a higher level use static methods for compression provided by NetworkTransform, will keep you from writing compression routines on your own in several cases
    - use NetworkProximityChecker if you have large areas with many moving (= constantly updated) objects
    - for counters and ids use variable length encoding (supported by NetworkWriter/Reader out of the box).
     
    moco2k likes this.
  43. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Cool. Thanks.
    I believe that would be enough.
     
  44. Teedo145

    Teedo145

    Joined:
    Apr 14, 2014
    Posts:
    12
    Thanks for the script!
    I've attempted to add it to my game though unsure how to set the players spawning position on the server properly. Everything I try keeps moving the player back to position 0,0,0.
    I have attempted the following with no luck. Does anybody know what I am doing wrong?
    I will give it another try tomorrow when I have more time, though I'm not really sure what else to try.
    Thanks

    Code (CSharp):
    1.     public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId){
    2.         GameObject player = (GameObject)GameObject.Instantiate(playerPrefab, new Vector3(700,100,700), Quaternion.identity);
    3.         NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
    4.         //player.GetComponent<NetworkPawn>().UpdatePosition(new Vector3(700,100,700));
    5.     }
     
  45. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    Hello,
    I think I know the root of your problem. _results.position is Vector3.zero when player spawned. So it always will set it self in to default position and rotation. I'll add two new public functions so it would be possible to set spawn position and rotation.
    BTW. I can see you're using custom spawn function. Do you have some kind of complete functionality or you just experimenting ?
    P.S. UpdatePosition, UpdateRotation and UpdateCrouch functions are used on dummy clients for updating their states accordingly. Those functions wouldn't set values on authoritative side for you.
     
  46. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    I've added two new public function SetStartPosition and SetStartRotation. So it would be possible to set spawn position and rotation.
    Cheers
     
  47. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    I'll try to expand this project to include sample project with spawning,moving and , potentially, shooting.
    Be prepared that folder structure eventually will change.
    Cheers
     
  48. Teedo145

    Teedo145

    Joined:
    Apr 14, 2014
    Posts:
    12
    Thanks for the update :)
    The custom spawn function was just an attempt at setting the spawn position, I am just testing and playing around while trying to understand the whole client and authoritative server thing.
     
  49. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    I've pushed big update.
    Player spawn, first person view with full body awareness, some animations.
    But I'm still having problems with client-side interpolation. Sometimes there can be huge delays in updates from server. So buffer would be depleted to 0.
    Enjoy
    Cheers
     
  50. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    87
    I've just pushed small bug fix.
    It looks like that network send interval should be bigger than just fixedDeltaTim * n. There should be additional delay.
    Haven't tried it with different intervals though.