Search Unity

Question Network Variable

Discussion in 'Unity Transport' started by Xa456789, Feb 6, 2024.

  1. Xa456789

    Xa456789

    Joined:
    Aug 5, 2022
    Posts:
    23
    I'm trying to make a Network Variable like in Netcode For GameObjects, so far I have this:
    NetVar.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using Unity.Collections;
    3.  
    4. public class NetVar : NetPacket
    5. {
    6.     public NetVarType netVarType { set; get; }
    7.     public SendMode sendMode { get; set; }
    8.     public int netVarId { get; set; }
    9.  
    10.     public float lastSend { set; get; }
    11.     public float sendInterval = 1.0f;
    12.  
    13.     public NetVar()
    14.     {
    15.         packetType = PacketType.NET_VAR;
    16.         sendMode = SendMode.ALWAYS_SEND;
    17.     }
    18.  
    19.     public NetVar(DataStreamReader reader)
    20.     {
    21.         packetType = PacketType.NET_VAR;
    22.         sendMode = SendMode.ALWAYS_SEND;
    23.         Deserialize(reader);
    24.     }
    25.  
    26.     /*public NetVar()
    27.     {
    28.         packetType = PacketType.NET_VAR;
    29.     }*/
    30.  
    31.     public override void Serialize(ref DataStreamWriter writer)
    32.     {
    33.         /*NEVER CHANGE */
    34.         writer.WriteByte((byte)packetType);
    35.         /*CAN BE CHANGED*/
    36.     }
    37.  
    38.     public override void Deserialize(DataStreamReader reader)
    39.     {
    40.         /*PACKET TYPE ALREADY READ*/
    41.     }
    42.  
    43.     public override void ReceivedOnServer(BaseServer server)
    44.     {
    45.         server.BroadcastPacket(this);
    46.     }
    47.  
    48.     public override void ReceivedOnClient()
    49.     {
    50.        
    51.     }
    52.  
    53.     public virtual void Synchronize(BaseClient client)
    54.     {
    55.          
    56.     }
    57.  
    58.     public void SetupId()
    59.     {
    60.         if (NetUtilities.Instance.GetNetVarList().Contains(this))
    61.             Debug.Log("J");
    62.             return;
    63.         NetUtilities.Instance.AddNetVarToList(this);
    64.         netVarId = NetUtilities.Instance.GetNetVarList().Count - 1;
    65.     }
    66.  
    67.     public virtual void SetValueToConvertedBytes(byte value)
    68.     {
    69.  
    70.     }
    71. }
    72.  
    Here is a Example of a NetVar:
    Code (CSharp):
    1. using UnityEngine;
    2. using Unity.Collections;
    3.  
    4. public class NetBool : NetVar
    5. {
    6.     public enum NetBoolState
    7.     {
    8.         TRUE = 1,
    9.         FALSE = 0
    10.     }
    11.  
    12.     private bool value;
    13.     public bool lastValue { set; get; }
    14.  
    15.     public NetBool()
    16.     {
    17.         packetType = PacketType.NET_VAR;
    18.         netVarType = NetVarType.BOOL;
    19.         sendMode = SendMode.ALWAYS_SEND;
    20.         value = false;
    21.         Debug.Log(value);
    22.     }
    23.  
    24.     public NetBool(SendMode sendMode)
    25.     {
    26.         packetType = PacketType.NET_VAR;
    27.         netVarType = NetVarType.BOOL;
    28.         this.sendMode = sendMode;
    29.         value = false;
    30.     }
    31.  
    32.     public NetBool(DataStreamReader reader)
    33.     {
    34.         packetType = PacketType.NET_VAR;
    35.         netVarType = NetVarType.BOOL;
    36.         Deserialize(reader);
    37.     }
    38.  
    39.     public NetBool(bool value, SendMode sendMode)
    40.     {
    41.         packetType = PacketType.NET_VAR;
    42.         netVarType = NetVarType.BOOL;
    43.         this.sendMode = sendMode;
    44.         this.value = value;
    45.     }
    46.  
    47.     public override void Serialize(ref DataStreamWriter writer)
    48.     {
    49.         SetupId();
    50.         /*NEVER CHANGE */
    51.         writer.WriteByte((byte)packetType);
    52.         /*CAN BE CHANGED*/
    53.         writer.WriteByte((byte)netVarType);
    54.         writer.WriteByte((byte)netVarId);
    55.  
    56.         if (value)
    57.             writer.WriteByte((byte)NetBoolState.TRUE);
    58.         else
    59.             writer.WriteByte((byte)NetBoolState.FALSE);
    60.     }
    61.  
    62.     public override void Deserialize(DataStreamReader reader)
    63.     {
    64.         /*PACKET AND NETVAR TYPE ALREADY READ*/
    65.         netVarId = reader.ReadByte();
    66.         NetBoolState netVarBool = (NetBoolState)reader.ReadByte();
    67.  
    68.         switch (netVarBool)
    69.         {
    70.             case NetBoolState.TRUE: value = true; break;
    71.             case NetBoolState.FALSE: value = false; break;
    72.             default:
    73.                 Debug.Log("Could not get NetBoolState");
    74.                 value = false;
    75.                 break;
    76.         }
    77.     }
    78.  
    79.     public override void ReceivedOnServer(BaseServer server)
    80.     {
    81.         server.BroadcastPacket(this);
    82.     }
    83.  
    84.     public override void ReceivedOnClient()
    85.     {
    86.         byte value;
    87.         if (this.value)
    88.             value = (byte)NetBoolState.TRUE;
    89.         else
    90.             value = (byte)NetBoolState.FALSE;
    91.  
    92.         Debug.Log(netVarId);
    93.         NetUtilities.Instance.GetNetVarById(netVarId).SetValueToConvertedBytes(value);
    94.     }
    95.  
    96.     public override void Synchronize(BaseClient client)
    97.     {
    98.         if (Time.time - lastSend > sendInterval)
    99.         {
    100.             switch (sendMode)
    101.             {
    102.                 case SendMode.ALWAYS_SEND: break;
    103.                 case SendMode.ONLY_NEW:
    104.                     if (value != lastValue)
    105.                     { break; }
    106.                     else { return; };
    107.             }
    108.  
    109.             lastValue = value;
    110.  
    111.             client.SendPacketToServer(this);
    112.  
    113.             lastSend = Time.time;
    114.         }
    115.     }
    116.  
    117.     public bool GetValue()
    118.     {
    119.         SetupId();
    120.         return value;
    121.     }
    122.  
    123.     public void SetValue(bool value)
    124.     {
    125.         SetupId();
    126.         this.value = value;
    127.     }
    128.  
    129.     public override void SetValueToConvertedBytes(byte value)
    130.     {
    131.         switch ((NetBoolState)value)
    132.         {
    133.             case NetBoolState.TRUE: this.value = true; break;
    134.             case NetBoolState.FALSE: this.value = false; break;
    135.             default:
    136.                 Debug.Log("Could not get NetBoolState");
    137.                 this.value = false;
    138.                 break;
    139.         }
    140.  
    141.         switch ((NetBoolState)value)
    142.         {
    143.             case NetBoolState.TRUE: Debug.Log(true.ToString()); break;
    144.             case NetBoolState.FALSE: Debug.Log(false.ToString()); break;
    145.             default:
    146.                 Debug.Log("Could not get NetBoolState");
    147.                 Debug.Log(false.ToString());
    148.                 break;
    149.         }
    150.     }
    151. }
    152.  

    BaseClient.cs
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Networking.Transport;
    5. using Unity.Collections;
    6.  
    7. public class BaseClient : MonoBehaviour
    8. {
    9.     public NetworkDriver driver;
    10.     protected NetworkConnection serverConnection;
    11.  
    12.     private string ipAddr = "127.0.0.1";
    13.     private ushort port = 44444;
    14.  
    15.     public ushort GetPort() { return port; }
    16.  
    17.     public void SetPort(ushort port) { this.port = port; }
    18.  
    19.     public string GetIpAddr() { return ipAddr; }
    20.     public void SetIpAddr(string IpAddr) { this.ipAddr = IpAddr; }
    21.  
    22.     private void Start() { Init(); }
    23.     private void Update() { UpdateServer(); }
    24.     private void OnDestroy() { Shutdown(); }
    25.  
    26.     public virtual void Init()
    27.     {
    28.         driver = NetworkDriver.Create();
    29.         serverConnection = default;
    30.  
    31.         NetworkEndpoint endPoint = NetworkEndpoint.LoopbackIpv4.WithPort(44444);
    32.  
    33.         serverConnection = driver.Connect(endPoint);
    34.     }
    35.  
    36.     public virtual void Shutdown()
    37.     {
    38.         driver.Dispose();
    39.     }
    40.  
    41.     public virtual void UpdateServer()
    42.     {
    43.         driver.ScheduleUpdate().Complete();
    44.         UpdateMessagePump();
    45.         IsConnectionActive();
    46.     }
    47.  
    48.     private void IsConnectionActive()
    49.     {
    50.         if (!serverConnection.IsCreated)
    51.         {
    52.             Debug.Log("Lost connection");
    53.         }
    54.     }
    55.  
    56.     protected virtual void UpdateMessagePump()
    57.     {
    58.         DataStreamReader stream;
    59.        
    60.         NetworkEvent.Type cmd;
    61.  
    62.         while ((cmd = serverConnection.PopEvent(driver, out stream)) != NetworkEvent.Type.Empty)
    63.         {
    64.             if (cmd == NetworkEvent.Type.Connect)
    65.             {
    66.                 Debug.Log("Client connected to server");
    67.             }
    68.             else if (cmd == NetworkEvent.Type.Data)
    69.             {
    70.                 OnDataReceived(stream);
    71.             }
    72.             else if (cmd == NetworkEvent.Type.Disconnect)
    73.             {
    74.                 serverConnection = default(NetworkConnection);
    75.                 Debug.Log("Client disconnected");
    76.             }
    77.         }
    78.     }
    79.  
    80.     public virtual void OnDataReceived(DataStreamReader stream)
    81.     {
    82.         NetPacket packet = null;
    83.         var packetType = (PacketType)stream.ReadByte();
    84.  
    85.         packet = NetUtilities.Instance.CreatePacket(stream, packetType);
    86.  
    87.         packet.ReceivedOnClient();
    88.     }
    89.  
    90.     public virtual void SendPacketToServer(NetPacket packet)
    91.     {
    92.         DataStreamWriter writer;
    93.         driver.BeginSend(serverConnection, out writer);
    94.         packet.Serialize(ref writer);
    95.         driver.EndSend(writer);
    96.     }
    97. }
    98.  
    BaseServer.cs
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Networking.Transport;
    5. using Unity.Collections;
    6.  
    7. public class BaseServer : MonoBehaviour
    8. {
    9.     public NetworkDriver driver;
    10.     protected NativeList<NetworkConnection> connections;
    11.  
    12.     [SerializeField] private bool isOpenForNewPlayers = true;
    13.  
    14.     public bool IsOpenForNewPlayers() { return isOpenForNewPlayers; }
    15.  
    16.     public void SetIsOpenForNewPlayers(bool isOpen) { isOpenForNewPlayers = isOpen; }
    17.  
    18.     private void Start() { Init(); }
    19.     private void Update() { UpdateServer(); }
    20.     private void OnDestroy() { Shutdown(); }
    21.  
    22.     public virtual void Init()
    23.     {
    24.         driver = NetworkDriver.Create();
    25.  
    26.         connections = new NativeList<NetworkConnection>(4, Allocator.Persistent);
    27.  
    28.         NetworkEndpoint endPoint = NetworkEndpoint.AnyIpv4.WithPort(44444);
    29.  
    30.         if (driver.Bind(endPoint) != 0)
    31.             Debug.Log("Could not bind to Port " + endPoint.Port);
    32.         else
    33.             driver.Listen();
    34.     }
    35.  
    36.     public virtual void Shutdown()
    37.     {
    38.         driver.Dispose();
    39.         connections.Dispose();
    40.     }
    41.  
    42.     public virtual void UpdateServer()
    43.     {
    44.         driver.ScheduleUpdate().Complete();
    45.         CleanUpConnection();
    46.         //if (IsOpenForNewPlayers())
    47.         AcceptNewConnections();
    48.         UpdateMessagePump();
    49.     }
    50.  
    51.     private void CleanUpConnection()
    52.     {
    53.         for (int i = 0; i < connections.Length; i++)
    54.         {
    55.             if (!connections[i].IsCreated)
    56.             {
    57.                 connections.RemoveAtSwapBack(i);
    58.                 i--;
    59.             }
    60.         }
    61.     }
    62.  
    63.     private void AcceptNewConnections()
    64.     {
    65.         NetworkConnection connection;
    66.  
    67.         while((connection = driver.Accept()) != default)
    68.         {
    69.             Debug.Log("Added new connection");
    70.             connections.Add(connection);
    71.         }
    72.     }
    73.  
    74.     protected virtual void UpdateMessagePump()
    75.     {
    76.         DataStreamReader stream;
    77.  
    78.         for(int i = 0; i < connections.Length; i++)
    79.         {
    80.             NetworkEvent.Type cmd;
    81.  
    82.             while ((cmd = driver.PopEventForConnection(connections[i], out stream)) != NetworkEvent.Type.Empty)
    83.             {
    84.                 if (cmd == NetworkEvent.Type.Data)
    85.                 {
    86.                     OnDataReceived(stream);
    87.                 }
    88.                 else if (cmd == NetworkEvent.Type.Disconnect)
    89.                 {
    90.                     connections[i] = default(NetworkConnection);
    91.                     Debug.Log("Client disconnect");
    92.                 }
    93.             }
    94.         }
    95.     }
    96.  
    97.     public virtual void OnDataReceived(DataStreamReader stream)
    98.     {
    99.         NetPacket packet = null;
    100.         var packetType = (PacketType)stream.ReadByte();
    101.  
    102.         packet = NetUtilities.Instance.CreatePacket(stream, packetType);
    103.  
    104.         packet.ReceivedOnServer(this);
    105.     }
    106.  
    107.     public virtual void BroadcastPacket(NetPacket packet)
    108.     {
    109.         for (int i = 0; i < connections.Length; i++)
    110.         {
    111.             if (connections[i].IsCreated)
    112.                 SendPacketToClient(connections[i], packet);
    113.         }
    114.     }
    115.  
    116.     public virtual void SendPacketToClient(NetworkConnection connection, NetPacket packet)
    117.     {
    118.         DataStreamWriter writer;
    119.         driver.BeginSend(connection, out writer);
    120.         packet.Serialize(ref writer);
    121.         driver.EndSend(writer);
    122.     }
    123. }

    As you can see, I've thought of using an ID for each NetVar. When a NetVar gets changed it would get broadcasted to every client there a NetVar with the same ID would get overriden with the new Value. But due to how C# handles code excution order there could be some issues.
    EXAMPLE:
    ClassA (on each client):

    Code (CSharp):
    1. NetBool bool = new NetBool();
    2. public BaseClient client;
    3.  
    4. void Start()
    5. {
    6.     bool.SetValue(true);
    7. }
    8.  
    9. void Update()
    10. {
    11.     bool.Synchronize(client);
    12. }
    ClassB (on each client):
    Code (CSharp):
    1. NetBool bool2 = new NetBool();
    2. public BaseClient client;
    3.  
    4. void Start()
    5. {
    6.     bool2.SetValue(true);
    7. }
    8.  
    9. void Update()
    10. {
    11.     bool2.Synchronize(client);
    12. }

    Unity would randomly chose between the Start Method of Class A and B.
    So if I just assign the ID based on how many already exist, it would sometimes be different for every client.

    Problem is, that I can't check how many already exist when creating a new NetVar, and here's my question:

    Is there a way to use somekind of solution for assigning IDs
    (maybe Time or something) but it would need to be the same for every client.


    OR

    Is there a way to acces a Class with that a List of NetVars on creation of the NetVar, I'm already doing that but I cannot acces it through FindObjectOftype<>() because NetVar doesnt use Monobehavior instead I created a Instance Variable for the Class with the List (NetUtilities - Class). The Instance can only get assigned to the class as soon as Awake() gets called wich is after NetVar creation.
     
    Last edited: Feb 6, 2024
  2. simon-lemay-unity

    simon-lemay-unity

    Unity Technologies

    Joined:
    Jul 19, 2021
    Posts:
    441
    Is there any reason you couldn't just have an object that's responsible for registering network variables, make it a singleton, and store it in a public static variable? This way it would be accessible from anywhere and your ID-generation would be centrally-controlled. Your network variables could access this object at creation time to register themselves and get a unique ID.

    Now the question becomes: how can a network variable register itself in a unique way that's not dependent on execution order? One way is for the network variable to provide something unique to your registration singleton. That will depend on how your game is structured, but one idea would be a string containing the game object's name, component name, and variable name. Your registration object can then keep track of the mapping between that string and the variable instance. If you need something short to send on the wire, you can simply hash that string.

    If you're interested in how Netcode for GameObjects is doing it, the code is public. It's fairly involved however, using code generation under the hood to automatically register network variables. Also since network variables there are necessarily attached to network behaviours, it makes it somewhat simpler to uniquely identify instances.
     
  3. Xa456789

    Xa456789

    Joined:
    Aug 5, 2022
    Posts:
    23
    Sorry, but how would a singleton implementation work? (Never heard of Singleton)
     
  4. simon-lemay-unity

    simon-lemay-unity

    Unity Technologies

    Joined:
    Jul 19, 2021
    Posts:
    441
    This is a very common concept. I'm sure a Google search for "C# singleton" will yield a lot of useful results.
     
  5. Xa456789

    Xa456789

    Joined:
    Aug 5, 2022
    Posts:
    23
    Here is my code for the NetUtilities:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Collections;
    5.  
    6. public class NetUtilities : MonoBehaviour
    7. {
    8.     public static NetUtilities Instance { get; private set; }
    9.  
    10.     public List<NetVar> netVarList = new List<NetVar>();
    11.  
    12.     private void Awake()
    13.     {
    14.         if (Instance != null)
    15.         {
    16.             Debug.Log("More than one NetUtilitie! " + transform + " - " + Instance);
    17.             Destroy(gameObject);
    18.             return;
    19.         }
    20.         Instance = this;
    21.     }
    22.  
    23.     public NetVar GetNetVarById(int id)
    24.     {
    25.         return netVarList[id];
    26.     }
    27.  
    28.     public List<NetVar> GetNetVarList()
    29.     {
    30.         return netVarList;
    31.     }
    32.  
    33.     public void AddNetVarToList(NetVar netVar)
    34.     {
    35.         Debug.Log(netVarList.Count);
    36.         netVarList.Add(netVar);
    37.     }
    38.  
    39.     public NetPacket CreatePacket(DataStreamReader stream, PacketType packetType)
    40.     {
    41.         NetPacket packet = null;
    42.         switch (packetType)
    43.         {
    44.             case PacketType.PLAYER_POS: packet = new NetPlayerPos(stream); break;
    45.             case PacketType.NET_VAR:
    46.                 packet = new NetVar(stream);
    47.                 var netVarType = (NetVarType)stream.ReadByte();
    48.  
    49.                 switch (netVarType)
    50.                 {
    51.                     case NetVarType.BOOL: packet = new NetBool(stream); break;
    52.                     case NetVarType.INT: packet = new NetInt(stream); break;
    53.                     default:
    54.                         Debug.Log("Could not get NetVarType");
    55.                         break;
    56.                 }
    57.                 break;
    58.  
    59.             default:
    60.                 Debug.Log("Could not get PacketType");
    61.                 break;
    62.         }
    63.  
    64.         return packet;
    65.     }
    66. }
    67.  
    68. public enum PacketType
    69. {
    70.     PLAYER_POS = 1,
    71.     NET_VAR = 2
    72. }
    73.  
    74. public enum NetVarType
    75. {
    76.     BOOL = 1,
    77.     INT = 2
    78. }
    79.  
    80. public enum SendMode
    81. {
    82.     ALWAYS_SEND = 0,
    83.     ONLY_NEW = 1
    84. }
    85.  
    And as I just noticed I was using a Singleton all along, but the problem lies within it getting created on Awake() wich is after the creation of NetVars, wich means no NetVar can acces the Instance when being created.
     
  6. simon-lemay-unity

    simon-lemay-unity

    Unity Technologies

    Joined:
    Jul 19, 2021
    Posts:
    441
    Right, in this context you shouldn't use
    Awake
    to initialize your instance, and instead simply use a getter to check if a (private) instance is null or not and instantiate the singleton if it is. I'd also suggest not to make singletons out of
    MonoBehaviour
    s (it doesn't look like your class needs to be one here).
     
  7. Xa456789

    Xa456789

    Joined:
    Aug 5, 2022
    Posts:
    23
    I'm trying to implement NetFloat but I cannot convert bytes to float:
    NetVar.cs
    Code (CSharp):
    1.     public virtual void SetValueToConvertedBytes(byte value)
    2.     {
    3.  
    4.     }
    NetFloat.cs
    Code (CSharp):
    1.     public override void SetValueToConvertedBytes(byte value)
    2.     {
    3.         this.value = (float)value;
    4.     }

    The float arrives how I want it to be, but when using this SetValueToConvertedBytes() Method it just gives me an INT,
    Example
    0.8 => 0;
    1.8 => 1;
    9.4 => 9;

    I suppose the conversion between bytes and float doesn't work with floats but just with Floats
    that are also Ints?

    Is there any other universal Type (like bytes). I need a Type, that allows to get converted into INTs, FLOATs and BOOLs (and Strings).

    OR

    A solution for the wrong conversion between floats and bytes.

    OR

    I need some way of getting the value from the Subclass (NetFloat, NetBool ...) by giving the BaseClass:
    I have a list of NetVars, wich is used for writting the new value for the correct NetVar on the other client. But I cannot get the value from (for example) NetBool, because of this I HAVE to use a universal method that can be called from the Baseclass to assign the value to the new one.
     
  8. simon-lemay-unity

    simon-lemay-unity

    Unity Technologies

    Joined:
    Jul 19, 2021
    Posts:
    441
    You likely want byte arrays as your universal type, not a single byte like your example suggests. And for type conversions,
    System.BitConverter
    contains a lot of methods to convert between primitive types and byte arrays.
     
  9. Xa456789

    Xa456789

    Joined:
    Aug 5, 2022
    Posts:
    23
    I see there is no Method for getting float with BitConverter.
    Is there any other option?
     
  10. simon-lemay-unity

    simon-lemay-unity

    Unity Technologies

    Joined:
    Jul 19, 2021
    Posts:
    441
  11. Xa456789

    Xa456789

    Joined:
    Aug 5, 2022
    Posts:
    23
    Hi, I have finally done everything else with higher priority, I even looked up string hashing, but I noticed a simple String like „Hello World“ gets converted into a really long hashed String.
    Are there multiple Hashing methods? Or did I look up something different?
    Thanks in advance!
     
  12. simon-lemay-unity

    simon-lemay-unity

    Unity Technologies

    Joined:
    Jul 19, 2021
    Posts:
    441
    Hashing functions turn a buffer of arbitrary length (which could be a string) into a fixed-size byte buffer. Different hashing functions end up with fixed-size buffers of different lengths. MD5 for example has a hash length of 128 bits. If you want something smaller you can either pick an algorithm which results in smaller buffers, or just take a standard algorithm like MD5 and only pick the first few bytes (here's an example).
     
  13. Xa456789

    Xa456789

    Joined:
    Aug 5, 2022
    Posts:
    23
    Ok, but what if I have the same script attached to a prefab, if I would duplicate it, the Id would be the same
     
  14. Xa456789

    Xa456789

    Joined:
    Aug 5, 2022
    Posts:
    23
    As an Example:
    A forest with many trees that all share TreeClass when a Player chops down a tree, the tree should disappear for every player. But if I use the class name (Tree) and the Variable name (IsDestroyed) the Id would always be TreeIsDestroyed or something different, but it would still be the same for every prefab.
    How do I go around this problem?
     
  15. Xa456789

    Xa456789

    Joined:
    Aug 5, 2022
    Posts:
    23
    Could you explain me further how NGO handles this, I mean I could also implement a special class that, when used allows Net Variables, but I don‘t think that that could change anything (it reduces the search amount by a bit but still)
     
  16. simon-lemay-unity

    simon-lemay-unity

    Unity Technologies

    Joined:
    Jul 19, 2021
    Posts:
    441
    That's a problem because you're using names based on the type of your variable. You need something that refers to the instance. As I indicated above, something that includes the game object's name, the component name and the field name could be one way to go.

    In NGO all network variables are necessarily attached to a
    NetworkBehaviour
    . Every network behaviour knows its network variable fields (identified by an attribute and populated in the behaviour using code generation). Furthermore, network behaviours are always associated at runtime with a
    NetworkObject
    . Network objects are uniquely identified by an ID, which is just an incrementing counter (there's no risk of conflicts because in NGO only the server can spawn network objects).

    Combine the network object ID, the network behaviour (as an index in the list of child components), and the field and you get a unique identifier for a particular network variable of a particular instance of an object. (In reality NGO doesn't even bother using the field in the identifier, and just sends all network variables for a particular network object and behaviour at the same time. There's some optimizations in there to avoid sending too much data, but that's not really relevant here.)
     
  17. Xa456789

    Xa456789

    Joined:
    Aug 5, 2022
    Posts:
    23
    But why is NGO using Code gen? I looked it up online and found a few videos on it, but what does Code gen do that regular code can‘t do (Yes it generates code, but why is it used) ? And could I replicate this, or does Unity use some stuff that cannot be accessed by me? Or does it use C++ for this or any other kind of programming language?
     
  18. simon-lemay-unity

    simon-lemay-unity

    Unity Technologies

    Joined:
    Jul 19, 2021
    Posts:
    441
    Code generation is used to make user code easier to read and write. For example it's what enables RPCs to work "magically" just by adding an attribute to a method (the code required to actually send the appropriate packet on the network is auto-generated). It also avoids using costly runtime reflection to answer questions like "what are all the network variable fields of this network behaviour?".

    Using code generation can be a bit difficult (it's not a beginner's topic) but there's nothing stopping users from leveraging it. NGO is just a normal package and all the APIs and technologies it uses are available to everyone.
     
  19. Xa456789

    Xa456789

    Joined:
    Aug 5, 2022
    Posts:
    23
    I‘ve just looked into the Code, but I couldn’t find any NetworkVariables Script. Could you link me to it?
    Thanks!
     
  20. simon-lemay-unity

    simon-lemay-unity

    Unity Technologies

    Joined:
    Jul 19, 2021
    Posts:
    441
    A lot of the basic code regarding network variables is here. Their updates over the network are done by sending a
    NetworkVariableDeltaMessage
    (message code is here and the code sending it is here). Code generation for network variables happens in this file, but do be aware that the code is not for the faint-hearted. You don't need to understand the code generation to get an idea of how network variables work though. The previous links I posted should be sufficient to understand what's going on.