Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Resolved Handling Connection Approval with Just Unity Transport

Discussion in 'Unity Transport' started by Phaaze1, Mar 18, 2023.

  1. Phaaze1

    Phaaze1

    Joined:
    Jun 26, 2013
    Posts:
    15
    Let's say I want to limit the number of connections to a server. In Netcode for Gameobjects, one would use the Connection Approval feature of the Network Manager. In Unity Transport, when following the example code, it only shows how to accept an arbitrary number of connections but not how to refuse a connection for a reason and send that reason to the client.

    My thought is that in the Accept code, you send the client a message and then call Disconnect() on the NetworkConnection object returned by Accept(). The problem I get here is that a null reference exception occurs in the Connect event on the client when it tries to write something to the server. I assume this is because the server has already disconnected and so the writer is never assigned by the begin send.

    What is the best way to handle this?

    Server Code
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Assertions;
    3. using Unity.Collections;
    4. using Unity.Networking.Transport;
    5. using Unity.Networking.Transport.Utilities;
    6.  
    7. public class ServerBehaviour : MonoBehaviour
    8. {
    9.     public int maxConnections = 2;
    10.     public NetworkDriver m_Driver;
    11.     public NetworkPipeline m_ReliablePipeline;
    12.     private NativeList<NetworkConnection> m_Connections;
    13.  
    14.     private void Start()
    15.     {
    16.         NetworkSettings settings = new NetworkSettings();
    17.         settings.WithReliableStageParameters(windowSize: 32);
    18.         m_Driver = NetworkDriver.Create(settings);
    19.         m_ReliablePipeline = m_Driver.CreatePipeline(typeof(ReliableSequencedPipelineStage));
    20.  
    21.         NetworkEndPoint endpoint = NetworkEndPoint.AnyIpv4;
    22.         endpoint.Port = 9000;
    23.  
    24.         if (m_Driver.Bind(endpoint) != 0)
    25.             Debug.Log("Failed to bind to port 9000");
    26.         else
    27.             m_Driver.Listen();
    28.  
    29.         m_Connections = new NativeList<NetworkConnection>(Allocator.Persistent);
    30.     }
    31.  
    32.     private void OnDestroy()
    33.     {
    34.         if (m_Driver.IsCreated)
    35.         {
    36.             m_Driver.Dispose();
    37.             m_Connections.Dispose();
    38.         }
    39.     }
    40.  
    41.     private void Update()
    42.     {
    43.         m_Driver.ScheduleUpdate().Complete();
    44.  
    45.         // Clean Up Connections
    46.         for (int i = 0; i < m_Connections.Length; i++)
    47.         {
    48.             if (!m_Connections[i].IsCreated)
    49.             {
    50.                 m_Connections.RemoveAtSwapBack(i);
    51.                 --i;
    52.             }
    53.         }
    54.  
    55.         // Accept or Reject New Connections
    56.         NetworkConnection c;
    57.         while ((c = m_Driver.Accept()) != default(NetworkConnection))
    58.         {
    59.             if (m_Connections.Length >= maxConnections)
    60.             {
    61.                 Debug.Log($"Rejected client {c.InternalId}");
    62.                 // Create some kind of encoding for the functionality.
    63.                 // In this case, if first byte is > 0, there was an error and don't read any more of the data.
    64.                 // A value of 1 could mean connection error due to full lobby
    65.                 byte errCode = 1;
    66.  
    67.                 m_Driver.BeginSend(m_ReliablePipeline, c, out var writer);
    68.                 writer.WriteByte(errCode);
    69.                 m_Driver.EndSend(writer);
    70.  
    71.                 c.Disconnect(m_Driver);
    72.             }
    73.             else
    74.             {
    75.                 m_Connections.Add(c);
    76.                 //Debug.Log("Accepted a connection");
    77.                 Debug.Log($"Connected to client {c.InternalId}");
    78.                 m_Driver.BeginSend(m_ReliablePipeline, c, out var writer);
    79.                 writer.WriteByte(0);
    80.                 writer.WriteUInt(0);
    81.                 m_Driver.EndSend(writer);
    82.             }
    83.         }
    84.  
    85.         // Handle Events
    86.         DataStreamReader stream;
    87.         for (int i = 0; i < m_Connections.Length; i++)
    88.         {
    89.             NetworkEvent.Type cmd;
    90.             while ((cmd = m_Driver.PopEventForConnection(m_Connections[i], out stream)) != NetworkEvent.Type.Empty)
    91.             {
    92.                 if (cmd == NetworkEvent.Type.Data)
    93.                 {
    94.                     uint number = stream.ReadUInt();
    95.  
    96.                     Debug.Log($"Got {number} from Client {m_Connections[i].InternalId}, adding + 2 to it.");
    97.                     number += 2;
    98.  
    99.                     m_Driver.BeginSend(m_ReliablePipeline, m_Connections[i], out var writer);
    100.                     writer.WriteByte(0);
    101.                     writer.WriteUInt(number);
    102.                     m_Driver.EndSend(writer);
    103.                 }
    104.                 else if (cmd == NetworkEvent.Type.Disconnect)
    105.                 {
    106.                     Debug.Log($"Client {m_Connections[i].InternalId} disconnected from server");
    107.                     m_Connections[i] = default(NetworkConnection);
    108.                 }
    109.             }
    110.         }
    111.     }
    112. }
    Client Event Handling
    Code (CSharp):
    1. DataStreamReader stream;
    2.         NetworkEvent.Type cmd;
    3.         while ((cmd = m_Connection.PopEvent(m_Driver, out stream)) != NetworkEvent.Type.Empty)
    4.         {
    5.             if (cmd == NetworkEvent.Type.Connect)
    6.             {
    7.                 Debug.Log("We are now connected to the server");
    8.  
    9.                 uint value = 1;
    10.                 m_Driver.BeginSend(m_ReliablePipeline, m_Connection, out var writer);
    11.                 writer.WriteUInt(value); // Null reference exception when disconnected in Accept() on server
    12.                 m_Driver.EndSend(writer);
    13.             }
    14.             else if (cmd == NetworkEvent.Type.Data)
    15.             {
    16.                 byte errCode = stream.ReadByte();
    17.                 Debug.Log(errCode);
    18.                 if (errCode > 0)
    19.                 {
    20.                     Debug.Log($"Got error code {errCode}");
    21.                 }
    22.                 else
    23.                 {
    24.                     uint value = stream.ReadUInt();
    25.                     Debug.Log("Got the value = " + value + " back from the server");
    26.                 }
    27.             }
    28.             else if (cmd == NetworkEvent.Type.Disconnect)
    29.             {
    30.                 Debug.Log("Client got disconnected from server");
    31.                 m_Connection = default(NetworkConnection);
    32.             }
    33.         }
     
  2. simon-lemay-unity

    simon-lemay-unity

    Unity Technologies

    Joined:
    Jul 19, 2021
    Posts:
    385
    Your basic approach is the correct one. To avoid the the exception you should check the return value of
    BeginSend
    on your client, and only write the value and make the
    EndSend
    call if it returns 0. Here's an example:
    Code (CSharp):
    1. Debug.Log("We are now connected to the server");
    2.  
    3. if (m_Driver.BeginSend(m_ReliablePipeline, m_Connection, out var writer) == 0)
    4. {
    5.     writer.WriteUInt(42);
    6.     m_Driver.EndSend(writer);
    7. }
    8. else
    9. {
    10.     Debug.Log("Connection was refused by the server.");
    11. }
    In general for production code I would recommend checking the return value of calls made to
    NetworkDriver
    . Most of them return error codes on failure (for example in your situation I'd expect it to return -3,
    NetworkStateMismatch
    ).