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;
- }
- }
- }
|