| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 | using System;using System.Collections.Generic;using UnityEngine;namespace Mirror.RemoteCalls{    // command function delegate    public delegate void CmdDelegate(NetworkBehaviour obj, NetworkReader reader, NetworkConnectionToClient senderConnection);    class Invoker    {        public Type invokeClass;        public MirrorInvokeType invokeType;        public CmdDelegate invokeFunction;        public bool cmdRequiresAuthority;        public bool AreEqual(Type invokeClass, MirrorInvokeType invokeType, CmdDelegate invokeFunction)        {            return (this.invokeClass == invokeClass &&                    this.invokeType == invokeType &&                    this.invokeFunction == invokeFunction);        }    }    public struct CommandInfo    {        public bool requiresAuthority;    }    /// <summary>Used to help manage remote calls for NetworkBehaviours</summary>    public static class RemoteCallHelper    {        static readonly Dictionary<int, Invoker> cmdHandlerDelegates = new Dictionary<int, Invoker>();        internal static int GetMethodHash(Type invokeClass, string methodName)        {            // (invokeClass + ":" + cmdName).GetStableHashCode() would cause allocations.            // so hash1 + hash2 is better.            unchecked            {                int hash = invokeClass.FullName.GetStableHashCode();                return hash * 503 + methodName.GetStableHashCode();            }        }        internal static int RegisterDelegate(Type invokeClass, string cmdName, MirrorInvokeType invokerType, CmdDelegate func, bool cmdRequiresAuthority = true)        {            // type+func so Inventory.RpcUse != Equipment.RpcUse            int cmdHash = GetMethodHash(invokeClass, cmdName);            if (CheckIfDeligateExists(invokeClass, invokerType, func, cmdHash))                return cmdHash;            Invoker invoker = new Invoker            {                invokeType = invokerType,                invokeClass = invokeClass,                invokeFunction = func,                cmdRequiresAuthority = cmdRequiresAuthority,            };            cmdHandlerDelegates[cmdHash] = invoker;            //string ingoreAuthorityMessage = invokerType == MirrorInvokeType.Command ? $" requiresAuthority:{cmdRequiresAuthority}" : "";            //Debug.Log($"RegisterDelegate hash: {cmdHash} invokerType: {invokerType} method: {func.GetMethodName()}{ingoreAuthorityMessage}");            return cmdHash;        }        static bool CheckIfDeligateExists(Type invokeClass, MirrorInvokeType invokerType, CmdDelegate func, int cmdHash)        {            if (cmdHandlerDelegates.ContainsKey(cmdHash))            {                // something already registered this hash                Invoker oldInvoker = cmdHandlerDelegates[cmdHash];                if (oldInvoker.AreEqual(invokeClass, invokerType, func))                {                    // it's all right,  it was the same function                    return true;                }                Debug.LogError($"Function {oldInvoker.invokeClass}.{oldInvoker.invokeFunction.GetMethodName()} and {invokeClass}.{func.GetMethodName()} have the same hash.  Please rename one of them");            }            return false;        }        public static void RegisterCommandDelegate(Type invokeClass, string cmdName, CmdDelegate func, bool requiresAuthority)        {            RegisterDelegate(invokeClass, cmdName, MirrorInvokeType.Command, func, requiresAuthority);        }        public static void RegisterRpcDelegate(Type invokeClass, string rpcName, CmdDelegate func)        {            RegisterDelegate(invokeClass, rpcName, MirrorInvokeType.ClientRpc, func);        }        //  We need this in order to clean up tests        internal static void RemoveDelegate(int hash)        {            cmdHandlerDelegates.Remove(hash);        }        static bool GetInvokerForHash(int cmdHash, MirrorInvokeType invokeType, out Invoker invoker)        {            if (cmdHandlerDelegates.TryGetValue(cmdHash, out invoker) && invoker != null && invoker.invokeType == invokeType)            {                return true;            }            // debug message if not found, or null, or mismatched type            // (no need to throw an error, an attacker might just be trying to            //  call an cmd with an rpc's hash)            // Debug.Log("GetInvokerForHash hash:" + cmdHash + " not found");            return false;        }        // InvokeCmd/Rpc Delegate can all use the same function here        internal static bool InvokeHandlerDelegate(int cmdHash, MirrorInvokeType invokeType, NetworkReader reader, NetworkBehaviour invokingType, NetworkConnectionToClient senderConnection = null)        {            if (GetInvokerForHash(cmdHash, invokeType, out Invoker invoker) && invoker.invokeClass.IsInstanceOfType(invokingType))            {                invoker.invokeFunction(invokingType, reader, senderConnection);                return true;            }            return false;        }        internal static CommandInfo GetCommandInfo(int cmdHash, NetworkBehaviour invokingType)        {            if (GetInvokerForHash(cmdHash, MirrorInvokeType.Command, out Invoker invoker) && invoker.invokeClass.IsInstanceOfType(invokingType))            {                return new CommandInfo                {                    requiresAuthority = invoker.cmdRequiresAuthority                };            }            return default;        }        /// <summary>Gets the handler function by hash. Useful for profilers and debuggers.</summary>        public static CmdDelegate GetDelegate(int cmdHash)        {            if (cmdHandlerDelegates.TryGetValue(cmdHash, out Invoker invoker))            {                return invoker.invokeFunction;            }            return null;        }    }}
 |