Search Unity

Authoritative all in one FPS network solution

Discussion in 'Multiplayer' started by AurimasBlazulionis, Jan 26, 2017.

  1. JosephHK

    JosephHK

    Joined:
    Jan 28, 2015
    Posts:
    40
    You can create a static dictionary to cache the Controller component and just forward the inputs received so that the private variables are not changed in the static function.

    The handler function registered should be static unless the instance is a persistent singleton.

    Actually, if you register a new message handler with the same msgType, the old one will be removed.
     
    Last edited: Apr 13, 2017
  2. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    Well it still works so... Yah. But now I see it as a "hack" and will probably move the handler to the GameManager which would then forward it to the correct controller.
    Edit: now I get that the previous client might lose control because the handler changes and if it does not then I get multiple unnecessary calls.
     
  3. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    I changed a few lines so that everything would work. Now I believe this thing is stable and usable. I added OnSendInputs to the GameManager where the message gets forwarded to the right client. Well I was quite lazy and all I did was I added a dictionary which's key is the connection ID, then I looked up the dictionary and called the same OnSendInputs function on the client (this time removed the check). Thanks to JosephHK for suggesting to move the thing to a static function.
     
  4. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    Anyone know what would be the best way to organize unknown data to the file and then retrieve it. I am just unsure about how everything would go if script execution order changed. Take a look at this code:

    Code (CSharp):
    1.  
    2.     public struct SmallResults
    3.     {
    4.         public Vector3 position;
    5.         public Quaternion rotation;
    6.         public List<float> valuesSingle;
    7.         public List<bool> valuesBool;
    8.         public List<uint> valuesUint;
    9.         public uint timestamp;
    10.     }
    11.  
    12.         private SmallResults data;
    13.  
    14.         private int uintIndexC = -1;
    15.         private int boolIndexC = -1;
    16.         private int singleIndexC = -1;
    17.  
    18.         public int RequestUintIndex () {
    19.             uintIndexC++;
    20.             if (data.valuesUint == null)
    21.                 data.valuesUint = new List<uint> ();
    22.             return uintIndexC;
    23.         }
    24.  
    25.         public void SetUint(int index, uint val) {
    26.             if (data.valuesUint.Count == index)
    27.                 data.valuesUint.Add (val);
    28.             else if (data.valuesUint.Count > index)
    29.                 data.valuesUint [index] = val;
    30.         }
    31.  
    32.         public int RequestBoolIndex () {
    33.             boolIndexC++;
    34.             if (data.valuesBool == null)
    35.                 data.valuesBool = new List<bool> ();
    36.             return boolIndexC;
    37.         }
    38.  
    39.         public void SetBool (int index, bool val) {
    40.             if (data.valuesBool.Count == index)
    41.                 data.valuesBool.Add (val);
    42.             else if (data.valuesBool.Count > index)
    43.                 data.valuesBool [index] = val;
    44.         }
    45.  
    46.         public int RequestSingleIndex () {
    47.             singleIndexC++;
    48.             if (data.valuesSingle == null)
    49.                 data.valuesSingle = new List<float> ();
    50.             return singleIndexC;
    51.         }
    52.  
    53.         public void SetSingle(int index, float val) {
    54.             if (data.valuesSingle.Count == index)
    55.                 data.valuesSingle.Add (val);
    56.             else if (data.valuesSingle.Count > index)
    57.                 data.valuesSingle [index] = val;
    58.         }
    I am thinking of going this way with universal object recording. But if the object changes so slightly or somehow the way scripts execute changes the whole recording gets screwed, because in the Awake/Start, some scripts get wrong index to read the data from. I am thinking about storing metadata for each index just before all the ticks are stored. Anyone have better ideas?
     
  5. ZhavShaw

    ZhavShaw

    Joined:
    Aug 12, 2013
    Posts:
    173
    Definitely an amazing thing you have posted here. I wish I was better so I can help you, but otherwise, I'm amazed you'd post this much work!
     
    AurimasBlazulionis likes this.
  6. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    Thanks! I was busy with other things and exams are coming so I couldn't do much in the past 2 weeks, so in the next week I should also make a fully working recording system that could record any object.

    Speaking of that, I am thinking of making a single class to store recorded data, then an object type unique interface would handle the data. So the same code could be reused for both players, props and other things that only your imagination knows what they are. After that comes the part I hate to even think about - climbing. I will probably make IK work before that thing.
     
    ZhavShaw likes this.
  7. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    Found the most simple and elegant solution to any object recording. Basically the recording manager calls the interface attached through the inspector and then the interface handles 2 actions: Tick, where it basically writes all the state information at that tick and SetData, which sets start and end information to be used while replaying. The interface simply writes and reads data using NetworkWriter and Reader. You can also inherit from the interface to build things on top of the base implementation where only position and rotation is saved. Now I will just find an easy way who should play back the data and a way to identify which object should be spawned when the demo file is replayed.

    I am thinking of pushing an update today.
     
    Player7 and TwoTen like this.
  8. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    Here we go! A new update. Of course, old demo files will be incompatible because of how the recording system has changed, but now there is only one thing that I might add to the system - static per-object properties and I even added a placeholder uint write/read to keep backwards compatibility.

    Speaking of recording things, the setup is not the easiest one. You must add the prefab of the recorded object to Network Settings scriptable object. And then you have to set the spawnIndex on the prefab itself in the RecordableObject script to write the correct value to the recorded file. I might think of something single-step, if you have any suggestions, please tell me.

    Now I don't know when the next update will be, because the recording part pretty much was the last one I needed for my game at this moment. I will be focusing on my game for now. But I still need proper foot IK so that one might come sometime. Climbing - I will need that definitely, but that is not something I care too much about because the current state once of the game once I switched to the UNet-Controller is pretty awful already (speaking of ragdoll transitions and stuff like that). So, if I need to change the controller for my game needs, I will do the updates but no real guarantees at least until the middle of June.
     
    TwoTen likes this.
  9. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    I have just fixed an issues with sliding of surfaces, strafing and made that unnecessary GC allocations wouldn't happen while not recording.
     
  10. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    Here is a quick showcase of foot IK which surprisingly I managed to implement in under 3 hours. It works rather well. What I am going to do is make IK weight go down while in air so the feet would be animated properly. Then I will add hand IK and push an update. After that I guess I will work on ragdoll transition because that is currently of a bit higher priority for me.
     
  11. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    A quick little update, I pushed the foot IK script to github.
     
  12. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    This is what is coming up soon:

    Was easier than I expected.
     
    TwoTen likes this.
  13. Jick87

    Jick87

    Joined:
    Oct 21, 2015
    Posts:
    124
    This looks really cool. Great work! :)

    I was wondering though if you had any more details about this item in the TODO list on the GitHub page:
    Does that mean the character will be able to be physics-based, like have a rigidbody and interact with other physics-based objects in a world? Or what? Just wondering...

    Thanks!
     
  14. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    No, it means the player will request something that requires a physics call, like raycast, for example, shooting bullets. The server would set up the scene to be exactly the same as the player sees it, and then shoot a raycast, because the player sees a few ticks behind and it would make nearly impossible to accurately aim if everything would be left the same as is.
     
    TwoTen likes this.
  15. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    Also, I will try to push an update changing the movement logic this week, because the current one is a bit flawed.
     
  16. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    Ragdoll is out! Also, some issues fixed.
     
  17. iCode

    iCode

    Joined:
    May 25, 2013
    Posts:
    33
    This would be AWESOME. keep up the good work!
     
  18. uacaman

    uacaman

    Joined:
    Mar 9, 2014
    Posts:
    4
    Nice work. This is very helpfull, it saves a lot of work. Do you have a patreon account?
     
  19. jessejarvis

    jessejarvis

    Joined:
    Aug 9, 2013
    Posts:
    303
    Wow! I am loving this so far!

    I got a few questions though as I am trying to make a WoW Movement System.

    1) I don't want to use Ragdoll/Root Motion, is there anything I should remove/know?

    2) I want to set it up so Q & E strafe, and A & D rotate the player. How would I go about this?

    3) I want to only rotate the camera when the player holds down right click or left click (Left click orbits around player, right click rotates player at all times).

    4) I can't seem to find a zoom in/out for this, did you make one?

    That's all for now. Thanks! :)

    Edit: I figured out #3 & #4 myself.

    I also am experiencing like stutter or snappy lag on client side when the player is moving. Watching the player move on server is fine though.
     
    Last edited: Nov 9, 2017
  20. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    I am not really sure about this since I haven't opened unity up in a couple of months, but to disable ragdoll you could just change the player settings to not make it go to the ragdoll state, also I think you could just remove the ragdoll controller and it should work fine.

    As of 2) well, you need to modify the input script.
     
  21. Vancete

    Vancete

    Joined:
    May 3, 2010
    Posts:
    198
    Really appreciate your work, I'll take a look at home.

    Thx
     
  22. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    This took a while. Now I got back to the project (and Unity in general) for at least quite a bit.

    So, I mainly have reworked the main loop where all the ticks occur, it is way simpler now. The current code got more like the one inside Source Engine (from the networking side of things), that's where I have spent my time close to in the past couple or not couple of 6 months. One new feature is ability to gather and run ticks inside Update loop. If the player is at low framerate, it will send some (almost) duplicate commands to the server and everything will run nicely.

    Another thing I added was delta compression of the commands themselves. There is a network property you can set that controls after how many commands a full snapshot of it is going to be sent (in case of desynchronization of some key the player is holding). During some tests, in a world with 2 players, about 20 kilobits are being passed over from the server side. While there are 4 players (3 clients, 1 host), it is something around 100 kilobits. While there is a player moving, bandwith goes up to around 150, which is due to position vectors updating. That is something I would not consider a lot.

    Also, there are a couple of breaking changes. One being that CLIENT_TRUST is no more. It is a little bit of a mess to maintain, at this point not that many prediction errors occur. The other is that there is one more argument for each tick callback.

    Last thing, there is lag compensation work in progress (though, currently a bit glitchy). Again, inspired by Source Engine, I simplified the way it's done by just doing StartLagCompensation, running code inside and calling EndLagCompensation after everything is done.

    Here is the full commit message adding a few things:
     
    Jick87 likes this.
  23. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    Here is a quick summary of what I am working on now. There are 2 things - lag compensation and extensibility. I have worked on the latter for quite a bit.

    My idea was to allow to extend the controller without the need of modifying anything inside of it. The first thing would be overridable virtual functions for the key parts of the controller. Another, more difficult part is variables (basically extension of Results). I came up with a custom variable synchronization system. It's a class PredVar<T>, which holds the value and a shadow network copy of it, which is needed for restoring it's state before predicting. Variable transmission is done in a delta compressed manner and using the same NetworkReader and Writer. It is type independant for the most part, but is not completely (yet). Due to C# not having real templates, you can not have per-type specifications, which would be needed for defining type specific Read and Write functions, but for now, every type has to be defined in a similar fasion:
    Code (CSharp):
    1.     [System.Serializable]
    2.     public class PredVar_Vector3 : PredVar<Vector3>
    3.     {
    4.         public PredVar_Vector3(Vector3 val) : base(val) { }
    5.  
    6.         protected override Vector3 ReadVal(NetworkReader reader) {
    7.             return reader.ReadVector3();
    8.         }
    9.  
    10.         protected override void WriteVal(NetworkWriter writer, Vector3 value) {
    11.             writer.Write(value);
    12.         }
    13.     }
    And if you were to include more input data, at the moment you could add more buttons, up to 30 (32 - existing 2 for jump and crouch) by accessing a bitfield keys meant for storing which buttons are pressed or not. The same goes for any booleans you want to keep inside the Results struct, there is a flags variable. I tried to make it very straightforward, writing a helper struct with helper functions for making things easier, all you have to do is keep track of the mask of the specific key/flag, which is easiest defined in this way:
    Code (CSharp):
    1. const uint keyAttack = (1 << 2);
    Where 2 should be replaced with the index of the flag.

    And as of the lag compensation, here is a video showing it working, the red dots on the ground are where every object is moved to when a player shoots and then there is an impact point, being red if hit a player. This basic shooting with synchronized/predicted ammo and time delay was implemented in just 50 lines of code.

    An update should be coming out soon, and afterwards I will focus more on documenting the whole project and exposing more stuff for overriding (especially the movement code).

    This would be the end of the restructuring of the controller and it would be ready for an actual game.
     
  24. Fortitude3D

    Fortitude3D

    Joined:
    Sep 7, 2017
    Posts:
    155
    Clean, nice ! :)
     
  25. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    Okay, the update is live. A couple of specifics about predicted variables. Either up to 32 or 64 such variables are supported on each controller, which is controlled by a compiler switch. The example of how everything works is in a new script I added.

    Another thing I forgot to mention is that I will also focus on minimizing the whole controller's dependable on UNet to a point where a custom framework could be very easily implemented with a couple of lines of code.

    I will also consider minimizing the results struct by clearing some variables that could easily be implemented using PredVars. After this the code specifics should stabilize and rarely change.

    Here is the full commit message:
     
  26. AurimasBlazulionis

    AurimasBlazulionis

    Joined:
    Aug 13, 2013
    Posts:
    209
    A new update making the code better, faster, nicer and more modular. Now I will focus on writing the documentation.

     
  27. Parobro

    Parobro

    Joined:
    Jan 13, 2019
    Posts:
    1
    hey, i wanna ask if there is anyone can help me with a 3d spaceship movement system, i never done it before but wen would need one for our spaceship mmo,so with serverside code and im so super confused :/