Search code examples
c#unity-game-enginelambdadelegates

Store function for later use / delegate with undefined attributes


I am working on a network project in Unity. following code is currently working but its not optimal since I will have to write very similar code over and over again. I read about delegates, lambdas and/or command-patterns but I cant figure out how to do it. I would like to store a function to invoke it as soon as networkauthority is handed over to the client.

Following code spawns a GameObject "corner":

public void CornerSpawnRequest(Vector3 spawnPosition, GameObject joinToObject)
{
    NetworkIDRequest.instance.RequestAuthority(GetComponent<NetworkIdentity>());
    StartCoroutine(WaitForSeconds(spawnPosition, joinToObject));    
}

IEnumerator WaitForSeconds(Vector3 spawnPosition, GameObject joinToObject)
{
    yield return new WaitForSeconds(.1f);
    NetworkCornerSpawn(spawnPosition, joinToObject);
}

[Command]
private void NetworkCornerSpawn(Vector3 spawnPosition, GameObject joinToObject)
{
    CornerSpawnExecution(spawnPosition, joinToObject);
}

Following code is part of the player to conveniently allow handover of authority. The delegate section is not doing something at the moment but only shows what I tried:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
using System;

public class NetworkIDRequest : NetworkBehaviour
{
//Singleton pattern in void Start().
//I deleted it for Stackoverflow to optimize readeability

    private void Update()
    {
        //RunWithAuthority();
    }

    public delegate void taskDelegate();

    private taskDelegate clickOnGameObjectDelegate = delegate { };
    public taskDelegate command
    {
        get => clickOnGameObjectDelegate;
        set => clickOnGameObjectDelegate = value;
    }

    public void RequestAuthority(NetworkIdentity identity)
    {
        CmdRequestNetworkAuthority(identity);
    }

    [Command]
    private void CmdRequestNetworkAuthority(NetworkIdentity identity)
    {
        identity.AssignClientAuthority(base.connectionToClient);
    }
}

I would like to replace the two lines in "CornerSpawnRequest()" with some kind of delegate in class NetworkIDRequest to always hand over a network ID that I want to request authority for and a function that shall be called as soon as I have authority. The function should have undefined amounts of attributes. Thats why I couldnt figure out a solution. I only know how to create delegates that have defined attributes.

I hope someone can tell me how to solve this problem in a solid and less clunky way. Thanks allready

best Björn


Solution

  • Whenever you need a delegate with unknown signature simply wrap it inside of an Action (basically a parameter less void delegate)

    You could probably do something like

    // For storing the callback
    private Action _whenHaveAuthority;
    // For storing the NetworkIdentity that requested authority
    // In order to check back later if the event is for us
    private NetworkIdentity _identity;
    
    public void RequestAuthority(NetworkIdentity identity, Action whenHaveAuthority)
    {
        // Store identity and callback
        _whenHaveAuthority = whenHaveAuthority;
        _identity = identity;
        CmdRequestNetworkAuthority(identity);
    }
    
    [Command]
    private void CmdRequestNetworkAuthority(NetworkIdentity identity)
    {
        identity.AssignClientAuthority(base.connectionToClient);
    
        Rpc_InformClientHasAuthority(identity);
    }
    
    // Server informs all clients 
    [ClientRpc]
    private void Rpc_InformClientHasAuthority(NetworkIdentity identity)
    {
        // Check if we are the one that sent the request
        if(_identity && _identity == identity)
        {
            // Execute the stored callback
            _whenHaveAuthority?.Invoke();
    
            // and reset
            _identity = null;
            _whenHaveAuthority = null;
        }
    }
    

    And then you would pass in the callback like e.g.

    public void CornerSpawnRequest(Vector3 spawnPosition, GameObject joinToObject)
    {
        NetworkIDRequest.instance.RequestAuthority(GetComponent<NetworkIdentity>(), () =>
        {
            NetworkCornerSpawn(spawnPosition, joinToObject);
        });
    }
    
    [Command]
    private void NetworkCornerSpawn(Vector3 spawnPosition, GameObject joinToObject)
    {
        CornerSpawnExecution(spawnPosition, joinToObject);
    }
    

    Note: Typed on smartphone but I hope the idea gets clear