Connection Approval callback not firing

Hey everyone!
I have added a connection approval callback according to Connection Approval documentation, but when a client tries to join, the connection is immediately approved and the callback is never fired. Not sure if it could be a bug or if I’m overlooking something.

My setup:

  • This callback assigned after the NetworkManager SetSingleton is called but before start connecting to a session
  • The NetworkManager Connection Approval is set to enabled
  • I am using Distributed Authority
  • Tested in Netcode for GameObjects v2.4.4 and v2.5.0
  • Bellow is the code I am using for the approval check but right now rejecting all connections:
using Unity.Netcode;
using UnityEngine;

namespace GameSession
{
    public class GameSessionManager
    {
        public GameSessionManager()
        {
            NetworkManager.Singleton.ConnectionApprovalCallback = ApprovalCheck;
        }

        ~GameSessionManager()
        {
            NetworkManager.Singleton.ConnectionApprovalCallback = null;
        }
        
        private void ApprovalCheck(NetworkManager.ConnectionApprovalRequest request, NetworkManager.ConnectionApprovalResponse response)
        {
            response.Approved = false;
            response.Reason = "Always fails";
            
            Debug.Log($"ApprovalCheck- Approved: {response.Approved} , Reason if failed: {response.Reason}");
            response.Pending = false;
        }
    }
}

When and where do you instantiate your GameSessionManager?

You should not, actually NEVER write code in the finalizer of a C# class. It’s not comparable to how a C++ destructor works, which runs immediately when you destroy the object. But the C# finalizer is going to be delayed until garbage collection picks up the object, and unlike C++ there’s not even a guarantee that it will run at all.

In C# use the IDisposable pattern to cleanup anything before an instance goes out of scope or is set to null.

Unfortunately, at this time we don’t support ConnectionApproval with the DistributedAuthority topology. I see that’s a BIG oversight in our documentation. We’ll make sure we update that.

If you don’t mind sharing, what’s your use-case for ConnectionApproval in a Distributed authority context? It’s useful to know real-world scenarios for this sort of feature.

Yes, I intend to change it to IDisposible soon. I just don’t have a great way of managing its live cycle right now so I went with it for now.

I didn’t know it wasn’t guaranteed that it would run! Not gonna use it even in throwaway code. Thanks for the info!

Oh, I see… Thanks!
I would like to only accept connections in the preparation/setup of the game, where players select their loadout and mark as ready to start.

@Erethan

So, if you are keeping track of the session created like this:

            m_CurrentSession = await MultiplayerService.Instance.CreateOrJoinSessionAsync(m_SessionName, options);

Then you should be able to do something like the following on the session owner client (the client who started the session) at a later time in the session when you don’t want any more clients to join:

// You could lock the session
m_CurrentSession.AsHost().IsLocked = true;

Theoretically that functionality should still exist in a distributed authority session. You just need to make sure it is the session owner that is invoking the script.

Follow up on the above:
After further investigation it would appear IsLocked gets reset back to false.

(will continue looking into this and post here on any change in the above)

@NoelStephens_Unity ,
Thank you! I appreciate the effort :flexed_biceps:

Looking forward for the outcome

@Erethan

Ok, finally have the “low-down” on this. :+1:

IHostSession.MaxPlayers needs a “setter” and is not being updated in the most current version of the Multiplayer SDK (but they have a ticket to fix that and the MaxPlayers missing “setter”).

However, IHostSession.IsLocked does work… I just was missing one final step in this.

So your connection script to a distributed authority session should be wrapped in a try catch like such:

    private async Task<ISession> ConnectThroughLiveService()
    {
        try
        {
            var options = new SessionOptions()
            {
                Name = m_SessionName,
                MaxPlayers = 32
            }.WithDistributedAuthorityNetwork();

            m_CurrentSession = await MultiplayerService.Instance.CreateOrJoinSessionAsync(m_SessionName, options);
            return m_CurrentSession;
        }
        catch (Exception e)
        {
            LogMessage($"{e.Message}");
            // Handle resetting the user's UI back to finding a session to join in SessionStopped
            SessionStopped();
            Debug.LogException(e);
        }
        return null;
    }

To verify it is working here is a simple script to toggle the locked status of a session:

    public async void ToggleSessionLock()
    {
        var hostSession = m_CurrentSession.AsHost();        
        hostSession.IsLocked = !hostSession.IsLocked;
        // Once you modify the host session properties, 
        // you must save them. (heh..the part I was missing)
        await hostSession.SavePropertiesAsync();
        var msg = hostSession.IsLocked ? "is locked" : "is unlocked";
        Debug.Log($"[Host-{hostSession.Host}][Is Host: {hostSession.IsHost}] The session {msg}.");
    }

So, when it is locked any client attempting to connect to it will be denied. On the client side that was denied, MultiplayerService.Instance.CreateOrJoinSessionAsync currently throws an exception stating it is locked (thus why you need to wrap it in a try catch). This will be handled more gracefully in future updates.

The exception message is: “SessionException: lobby is locked”.

I think you can also filter out sessions that are locked when looking for a session to join… but the above is how you can (currently) close out a session so no other clients can join.

Awesome!
Will test it out likely over next neek

Thanks!