quantum-space-buddies/Mirror/Runtime/RemoteCallHelper.cs
2021-12-27 22:31:23 -08:00

153 lines
5.8 KiB
C#

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