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