Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

NetCode Simpler Sample.

Discussion in 'NetCode for ECS' started by Opeth001, Aug 21, 2019.

  1. Opeth001

    Opeth001

    Joined:
    Jan 28, 2017
    Posts:
    1,112
    hi everyone,

    im trying to understand how the Asteroids Sample works, and i personally found it harder to understand than the NetCode itself.

    Can anyone provide any Simple example for the new netcode features.

    1) how to establish a connection between client & server.
    2) how to send & Handle Received RPCs.( reliable messages ).
    3) how to send & Handle Received Streams ( unreliable messages ).

    Thank you.
     
    Deleted User likes this.
  2. pal_trefall

    pal_trefall

    Joined:
    Feb 5, 2019
    Posts:
    78
    I've dabbled a bit with the Netcode the past couple of weeks, and I'm starting to get some results. I'll try to provide some insight I've gathered through the process, but I'm sure we'll have a lot more information come Unite in a month.

    1) This is fairly straight forward. You use the ClientServerBootstrap to get at the server world and client worlds. How many client worlds are present is determined by your settings in the Netcode's settings menu. You need to get the NetworkStreamReceiveSystem and Listen on the server world, and Connect on each client world. It's handy to deal with this in a component system, because on update you also want to catch any entity with NetworkStreamConnection, that does not have a NetworkStreamInGame, and add that component. These are the steps to establish a connection. I've yet to look into handling disconnect detection, but the Asteroids example checks the existence of the client's player entity on the server (which among other things has CommandTargetComponent, and probably the existence of NetworkStreamDisconnected component).

    From here its a good idea to look at the InputSystem in the Asteroids example, to see how they spawn a "player" and a "ship" that the player controls.

    2) I started from the InputSystem to get on top of this. First you need to write a struct that implements IRpcCommand. On Execute you get the request entity from the command buffer (using the provided jobIndex), and add a special Request component to it. This is basically the payload you want to go through the RPC command. I typically add an Entity entry I call Connection, and add the connection entity parameter from the Execute method in there. If you need more parameters you add those into this request component too, but you need to then also have them in the IRpcCommand struct you're writing, as a public variable/property, and write the Serialize and Deserialize for it (and inject it into the request component on Execute.

    When this is done you're ready to generate the RpcCollection. This will generate everything Netcode needs to deal with the logic of sending your new Rpc command over the wire, and on the opposite side (whether its a server or a client), will get an entity made (the request entity), with your request component on it. To catch this entity as it is instantiated, you need to write a system that handles it, and then deletes the request entity. Look at SpawnSystem in Asteroids example.

    To create this new Rpc Command, you need an RpcQueue (look at InputSystem again). When you hit RpcCollection generation, that generated a new RpcSystem for you that uses the name of your project. So MyProject would generate a MyProjectRpcSystem. It doesn't trim whitespaces yet, so you might need to fix this manually after generation. From this system you can GetRpcQueue on the new RpcCommand you defined. When you have this, you're ready to start sending rpc commands.

    You need to find all entities that has a CommandTargetComponent, but has not a NetworkStreamDisconnected. I "believe" there is only one per client. From this entity, get the OutgoingRpcDataStreamBufferComponent buffer, use this as input to the RpcQueue Schedule and instantiate your Rpc Command into it, and BAM! you've sent it.

    3) This topic is slightly more complex, but I think the prefab convertion pipeline is probably the most straight forward approach to do this. In the asteroids example they have a lot of these "Authoring" monobehaviors, that convert to a given ECS component. I suggest you embrace that to start with. Add the Ghost Authroing Component to your prefab, and hit its Update Component List button (the Netcode Quickstart readme examplain this fairly well). Once you hit the generate button, after tuning the settings, it generates a SnapshotData struct/file that implements ISnapshotData. I looked at the way the ship prefab was set up in the Asteroids example to get to grips with this. E.g. notice how its PlayerId component adds a PlayerId field to the Ghost Authoring Component, and how that adds an entry into the generated snapshot data. Notice how you also generated a GhostSpawnSystem, GhostUpdateSystem and GhostSerializer, and that GhostSpawnSystem is a partial class. This system is meant for you to extend, and you just have to look at the Asteroids sample how they extended the spawn systems. I'm sure there's someone else on here that can explain this process better than me, though.

    Now you're ready to generate the GhostCollection, and from here you should start getting snapshots. Note that you'll have to go through some of these generated files and add the using reference dependencies yourself manually, these generated files are semi-meant to be tweaked it looks like.

    I hope that at least can nudge you in the right direction, and the big disclaimer is that this is all my reasoning on Netcode and some of my reasoning might not be 100% correct. I'm sure we'll see improved documentation and usage examples soon. Feels like something is brewing for Unite.
     
    Last edited: Aug 22, 2019
    Sarkahn, PhilSA, Singtaa and 4 others like this.
  3. Opeth001

    Opeth001

    Joined:
    Jan 28, 2017
    Posts:
    1,112
    Thank you Very much !!!
    it's enought clear now for me to start.
     
    pal_trefall likes this.
  4. adammpolak

    adammpolak

    Joined:
    Sep 9, 2018
    Posts:
    450
    @pal_trefall thank you for your terrific explanation! Not sure if you could also help with explaining something else in the Asteroids sample...

    I have been working through the Asteroids sample to understand how Unity thinks about multiplayer architecture in the new DOTS stack. I understand the server being authoritative is key, but that clients still have to run certain systems to provide the immediate responses users would expect (can't wait for the server to respond for every single thing or the lag delay will be unacceptable). I am trying to develop a better sense of how to split systems to run on either client, or server, or both. This answer is probably super obvious to anyone that knows the pitfalls of multiplayer video game development but is lost on me.

    In Asteroids the systems are held in 3 folders:
    /client (for systems run only on the client, like the InputSystem because only clients have input)
    /server (for systems run only on the server, like the CollisionSystem because server is authoritative on collision)
    /mixed (for systems that run on both client and server, like SteeringSystem because client needs instant feedback but server ultimately decides positions)

    I am trying to understand why the BulletSystem is run on both client/server (/mixed) but the AsteroidSystem is run only on the server (/server).

    They both seem to do the same thing: make the bullet/asteroid continue traveling the direction it was traveling by updating its position using its velocity. And they both seem to have the same level of importance for the user, knowing exactly where they are. If I shoot a bullet I would expect it to travel in a way that makes sense regardless of server behavior. I would expect Asteroids to behave the same way, in a consistent pattern because I am trying to avoid them/shoot them.

    I understand the system to spawn the bullet needs to be both on the client and server (which it is, SteeringSystem is in /mixed) but once it has been created and traveling, it seems to be of the same importance of an Asteroid, no? Either both AsteroidSystem and BulletSystem should be in /mixed, or both of them should be in /server (i am using the folder location as a proxy for meaning wether the client/server should be running it).

    BulletSystem
    Code (CSharp):
    1.             public void Execute([ReadOnly] ref Velocity velocity, ref Translation position, [ReadOnly] ref PredictedGhostComponent prediction)
    2.             {
    3.                 if (!GhostPredictionSystemGroup.ShouldPredict(tick, prediction))
    4.                     return;
    5.                 position.Value.xy += velocity.Value * deltaTime;
    6.             }
    AsteroidSystem
    Code (CSharp):
    1.             public void Execute([ReadOnly] ref Velocity velocity, ref Translation position, ref Rotation rotation)
    2.             {
    3.                 position.Value.xy += velocity.Value * deltaTime;
    4.                 rotation.Value = math.mul(rotation.Value, quaternion.RotateZ(math.radians(100 * deltaTime)));
    5.             }
    As with all Ghosts the server provides SnapShot updates to all clients of both the bullet and the asteroid. Why would receiving snapshot updates from the server for Asteroids be acceptable but not Bullets? I am sure there is something very obvious I am missing.

    (@timjohansson if you are out there please save me from this mental torture!)
     
    pal_trefall likes this.
  5. pal_trefall

    pal_trefall

    Joined:
    Feb 5, 2019
    Posts:
    78
    I'm glad you liked it adammpolak.It's been a while since I looked at the sample now, but a wild guess would be that since the player shoot bullets, the bullets has to sit in mixed to be able to offer a prediction on the client, while asteroids are server-simulated, and the client simply interpolates the position of the asteroid rather than predict it.