forked from cgvr/DeltaVR
		
	
		
			
				
	
	
		
			166 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			166 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using FishNet.CodeGenerating.Helping;
 | 
						|
using FishNet.CodeGenerating.Helping.Extension;
 | 
						|
using FishNet.Connection;
 | 
						|
using FishNet.Object.Helping;
 | 
						|
using MonoFN.Cecil;
 | 
						|
using System.Collections.Generic;
 | 
						|
using System.Runtime.CompilerServices;
 | 
						|
 | 
						|
namespace FishNet.CodeGenerating.Processing.Rpc
 | 
						|
{
 | 
						|
    internal class Attributes : CodegenBase
 | 
						|
    {
 | 
						|
        
 | 
						|
        /// <summary>
 | 
						|
        /// Returns if methodDef has any Rpc attribute.
 | 
						|
        /// </summary>
 | 
						|
        public bool HasRpcAttributes(MethodDefinition methodDef)
 | 
						|
        {
 | 
						|
            foreach (CustomAttribute customAttribute in methodDef.CustomAttributes)
 | 
						|
            {
 | 
						|
                RpcType rt = base.Session.GetClass<AttributeHelper>().GetRpcAttributeType(customAttribute);
 | 
						|
                if (rt != RpcType.None)
 | 
						|
                    return true;
 | 
						|
            }
 | 
						|
 | 
						|
            //Fall through, nothing found.
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Returns a collection of RpcAttribute for methodDef.
 | 
						|
        /// </summary>
 | 
						|
        public List<AttributeData> GetRpcAttributes(MethodDefinition methodDef)
 | 
						|
        {
 | 
						|
            List<AttributeData> results = new List<AttributeData>();
 | 
						|
            string asyncAttributeFullName = typeof(AsyncStateMachineAttribute).FullName;
 | 
						|
            bool isAsync = false;
 | 
						|
 | 
						|
            foreach (CustomAttribute customAttribute in methodDef.CustomAttributes)
 | 
						|
            {
 | 
						|
                RpcType rt = base.Session.GetClass<AttributeHelper>().GetRpcAttributeType(customAttribute);
 | 
						|
                if (rt != RpcType.None)
 | 
						|
                {
 | 
						|
                    results.Add(new AttributeData(customAttribute, rt));
 | 
						|
                }
 | 
						|
                //Not a rpc attribute.
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    //Check if async.
 | 
						|
                    if (customAttribute.Is(asyncAttributeFullName))
 | 
						|
                        isAsync = true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            //Nothing found, exit early.
 | 
						|
            if (results.Count == 0)
 | 
						|
            {
 | 
						|
                return results;
 | 
						|
            }
 | 
						|
            //If has at least one RPC attrivbute and is an async method.
 | 
						|
            else if (isAsync)
 | 
						|
            {
 | 
						|
                base.Session.LogError($"{methodDef.Name} is an async RPC. This feature is not currently supported. You may instead run an async method from this RPC.");
 | 
						|
                return new List<AttributeData>();
 | 
						|
            }
 | 
						|
            //If more than one attribute make sure the combination is allowed.
 | 
						|
            else if (results.Count >= 2)
 | 
						|
            {
 | 
						|
                RpcType allRpcTypes = results.GetCombinedRpcType();
 | 
						|
                if (allRpcTypes != (RpcType.Observers | RpcType.Target))
 | 
						|
                {
 | 
						|
                    base.Session.LogError($"{methodDef.Name} contains multiple RPC attributes. Only ObserversRpc and TargetRpc attributes may be combined.");
 | 
						|
                    return new List<AttributeData>();
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            //Next validate that the method is setup properly for each rpcType.
 | 
						|
            foreach (AttributeData ad in results)
 | 
						|
            {
 | 
						|
                //If not valid then return empty list.
 | 
						|
                if (!IsRpcMethodValid(methodDef, ad.RpcType))
 | 
						|
                    return new List<AttributeData>();
 | 
						|
            }
 | 
						|
 | 
						|
            return results;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Returns if a RpcMethod can be serialized and has a proper signature.
 | 
						|
        /// </summary>
 | 
						|
        private bool IsRpcMethodValid(MethodDefinition methodDef, RpcType rpcType)
 | 
						|
        {
 | 
						|
            //Static method.
 | 
						|
            if (methodDef.IsStatic)
 | 
						|
            {
 | 
						|
                base.Session.LogError($"{methodDef.Name} RPC method cannot be static.");
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
            //Is generic type.
 | 
						|
            else if (methodDef.HasGenericParameters)
 | 
						|
            {
 | 
						|
                base.Session.LogError($"{methodDef.Name} RPC method cannot contain generic parameters.");
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
            //Abstract method.
 | 
						|
            else if (methodDef.IsAbstract)
 | 
						|
            {
 | 
						|
                base.Session.LogError($"{methodDef.Name} RPC method cannot be abstract.");
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
            //Non void return.
 | 
						|
            else if (methodDef.ReturnType != methodDef.Module.TypeSystem.Void)
 | 
						|
            {
 | 
						|
                base.Session.LogError($"{methodDef.Name} RPC method must return void.");
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
            //Misc failing conditions.
 | 
						|
            else
 | 
						|
            {
 | 
						|
                //Check for async attribute.
 | 
						|
                foreach (CustomAttribute ca in methodDef.CustomAttributes)
 | 
						|
                {
 | 
						|
 | 
						|
                }
 | 
						|
            }
 | 
						|
            //TargetRpc but missing correct parameters.
 | 
						|
            if (rpcType == RpcType.Target)
 | 
						|
            {
 | 
						|
                if (methodDef.Parameters.Count == 0 || !methodDef.Parameters[0].Is(typeof(NetworkConnection)))
 | 
						|
                {
 | 
						|
                    base.Session.LogError($"Target RPC {methodDef.Name} must have a NetworkConnection as the first parameter.");
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            //Make sure all parameters can be serialized.
 | 
						|
            for (int i = 0; i < methodDef.Parameters.Count; i++)
 | 
						|
            {
 | 
						|
                ParameterDefinition parameterDef = methodDef.Parameters[i];
 | 
						|
 | 
						|
                //If NetworkConnection, TargetRpc, and first parameter.
 | 
						|
                if ((i == 0) && (rpcType == RpcType.Target) && parameterDef.Is(typeof(NetworkConnection)))
 | 
						|
                    continue;
 | 
						|
 | 
						|
                if (parameterDef.ParameterType.IsGenericParameter)
 | 
						|
                {
 | 
						|
                    base.Session.LogError($"RPC method{methodDef.Name} contains a generic parameter. This is currently not supported.");
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
 | 
						|
                //Can be serialized/deserialized.
 | 
						|
                bool canSerialize = base.GetClass<GeneralHelper>().HasSerializerAndDeserializer(parameterDef.ParameterType, true);
 | 
						|
                if (!canSerialize)
 | 
						|
                {
 | 
						|
                    base.Session.LogError($"RPC method {methodDef.Name} parameter type {parameterDef.ParameterType.FullName} does not support serialization. Use a supported type or create a custom serializer.");
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
 | 
						|
            }
 | 
						|
 | 
						|
            //Fall through, success.
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
} |