Store the reference to a method without losing classes changes C# Unity

41 Views Asked by At

Basically I have achieved storing the methods but when I call them its like they are calling in a new Instance of an object instead of the object itself.

When I call the method it just prints 0

This is where I use it. It doesn't matter how many times I execute SumanNumero. When I call RPC_MetodoEjemplo I just get 0 (The variable is initialized with 5)

public class Something: NetworkBehaviour
    int x = 5;
    // Esto es un ejemplo de un rpc sin parámetros
    [ClientRPC]
    public void RPC_MetodoEjemplo()
    {
        Debug.Log(x);
    }
    
    public void SumanNumero()
    {
        x += 10;
    }
}

This is my approach:

private List<RPCMethodInfo> clientRPCMethods = new List<RPCMethodInfo>();

public RPCManager()
{
    // We obtain all the classes that inherits from NetworkBehaviour
    Type[] networkBehaviours = AppDomain.CurrentDomain.GetAssemblies()
        .SelectMany(x => x.GetTypes())
        .Where(x => typeof(NetworkBehaviour).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
        .ToArray();
    
    // We obtain all the methods that have the ClientRPC attribute
    foreach (Type networkBehaviour in networkBehaviours)
    {
        MethodInfo[] methods = networkBehaviour.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
            .Where(x => x.GetCustomAttributes(typeof(ClientRPCAttribute), false).Length > 0)
            .ToArray();

        foreach (MethodInfo method in methods)
        {
            clientRPCMethods.Add(new RPCMethodInfo(method, networkBehaviour));
        }
    }
}

// Llama a este método desde el cliente para ejecutar un RPC
public void CallRPC(string methodName, object[] parameters)
{
    foreach (var method in clientRPCMethods)
    {
        if (method.Method.Name == methodName)
        {
            method.Method.Invoke(method.Target, parameters);
            return;
        }
    }
}


public class RPCMethodInfo
{
    public MethodInfo Method { get; }
    public object Target { get; }

    public RPCMethodInfo(MethodInfo method, object target)
    {
        Method = method;
        Target = target;
    }
}
1

There are 1 best solutions below

0
7FULL On

Thanks to @derHugo I did something.

Its not so effective but It is only called 1 time at the begining of the game so it is not that bad.(Could be better, probably when I finish the proyect i will refactor it)

This is it:

using UnityEngine;
using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;

public class RPCManager
{
    private Dictionary<GameObject, List<RPCInfo>> methods = new Dictionary<GameObject, List<RPCInfo>>();
    public RPCManager()
    {
        GameObject[] objetosEnEscena = GameObject.FindObjectsOfType<GameObject>();
        
        foreach (GameObject objeto in objetosEnEscena)
        {
            NetworkBehaviour[] scripts = objeto.GetComponents<NetworkBehaviour>();
            
            foreach (NetworkBehaviour script in scripts)
            {
                MethodInfo[] metodos = script.GetType().GetMethods();
                
                List<RPCInfo> metodosConRPC = new List<RPCInfo>();
                
                foreach (MethodInfo metodo in metodos)
                {
                    if (metodo.GetCustomAttributes(typeof(ClientRPCAttribute), true).Length > 0)
                    {
                        metodosConRPC.Add(new RPCInfo(metodo, script));
                    }
                }

                methods.Add(objeto, metodosConRPC.ToList());
            }
        }
    }
    
    // This is to add a new RPC method to the list
    public void AddRPC(GameObject gameObject, MethodInfo methodInfo)
    {
        if (methods.ContainsKey(gameObject))
        {
            methods[gameObject].Add(new RPCInfo(methodInfo, gameObject.GetComponent<NetworkBehaviour>()));
        }
        else
        {
            methods.Add(gameObject, new List<RPCInfo> {new RPCInfo(methodInfo, gameObject.GetComponent<NetworkBehaviour>())});
        }
    }
    
    // Call RPC
    public void CallRPC(string methodName, object[] parameters)
    {
        foreach (KeyValuePair<GameObject, List<RPCInfo>> pair in methods)
        {
            foreach (RPCInfo rpcInfo in pair.Value)
            {
                //Debug.Log(rpcInfo.toTExt());
                if (rpcInfo.methodInfo.Name == methodName)
                {
                    rpcInfo.methodInfo.Invoke(rpcInfo.target, parameters);
                }
            }
        }
    }
    
    public List<Type> FindNetworkBehaviourTypes()
    {
        Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

        List<Type> networkBehaviourTypes = new List<Type>();

        foreach (Assembly assembly in assemblies)
        {
            Type[] types = assembly.GetTypes();
            networkBehaviourTypes.AddRange(types.Where(t => t.IsSubclassOf(typeof(NetworkBehaviour))));
        }

        return networkBehaviourTypes;
    }
    
    public List<GameObject> FindGameObjectsWithNetworkBehaviour()
    {
        List<Type> networkBehaviourTypes = FindNetworkBehaviourTypes();
        List<GameObject> gameObjectsWithNetworkBehaviour = new List<GameObject>();

        foreach (GameObject go in GameObject.FindObjectsOfType<GameObject>())
        {
            NetworkBehaviour[] networkBehaviours = go.GetComponents<NetworkBehaviour>();

            foreach (NetworkBehaviour nb in networkBehaviours)
            {
                if (networkBehaviourTypes.Contains(nb.GetType()))
                {
                    gameObjectsWithNetworkBehaviour.Add(go);
                    break; // No need to check this GameObject further
                }
            }
        }

        return gameObjectsWithNetworkBehaviour;
    }

    public class RPCInfo
    {
        public MethodInfo methodInfo;
        public object target;
        
        public RPCInfo(MethodInfo methodInfo, object target)
        {
            this.methodInfo = methodInfo;
            this.target = target;
        }
        
        // We use this method instead of ToString() because ToString() can only be called from the main thread
        // and this is executing in a background thread
        public string toTExt()
        {
            return "RPCInfo: " + methodInfo.Name + " " + target;
        }
    }
}
`