This repository has been archived on 2023-09-13. You can view files and clone it, but cannot push or open issues or pull requests.
station_obscurum_unity/Assets/FishNet/CodeGenerating/Processing/Rpc/Attributes.cs

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