Add StickGame Assets
This commit is contained in:
8
phr/StickGame/Assets/FishNet/CodeGenerating.meta
Normal file
8
phr/StickGame/Assets/FishNet/CodeGenerating.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a03c3ea914b8ed649a14606e1430e404
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2e223f21744b544bbb7e93020199efb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Cil;
|
||||
|
||||
namespace FishNet.CodeGenerating.Extension
|
||||
{
|
||||
|
||||
|
||||
internal static class ILProcessorExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a variable type within the body and returns it's VariableDef.
|
||||
/// </summary>
|
||||
internal static VariableDefinition CreateVariable(this ILProcessor processor, CodegenSession session, System.Type variableType)
|
||||
{
|
||||
return processor.Body.Method.CreateVariable(session, variableType);
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a variable type within the body and returns it's VariableDef.
|
||||
/// </summary>
|
||||
internal static VariableDefinition CreateVariable(this ILProcessor processor, CodegenSession session, TypeReference variableTr)
|
||||
{
|
||||
return processor.Body.Method.CreateVariable(variableTr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 127d8312da53b3e49ba9e3e4c6348857
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,208 @@
|
||||
using FishNet.CodeGenerating.Helping;
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Cil;
|
||||
using MonoFN.Cecil.Rocks;
|
||||
using MonoFN.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FishNet.CodeGenerating.Extension
|
||||
{
|
||||
|
||||
|
||||
internal static class MethodDefinitionExtensions
|
||||
{
|
||||
public const MethodAttributes PUBLIC_VIRTUAL_ATTRIBUTES = (MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig);
|
||||
public const MethodAttributes PROTECTED_VIRTUAL_ATTRIBUTES = (MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a custom attribute.
|
||||
/// </summary>
|
||||
public static CustomAttribute GetCustomAttribute(this MethodDefinition md, string attributeFullName)
|
||||
{
|
||||
if (md == null)
|
||||
return null;
|
||||
|
||||
foreach (CustomAttribute item in md.CustomAttributes)
|
||||
{
|
||||
if (item.AttributeType.FullName == attributeFullName)
|
||||
return item;
|
||||
}
|
||||
|
||||
//Not found.
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the method content and returns ret.
|
||||
/// </summary>
|
||||
internal static void ClearMethodWithRet(this MethodDefinition md, CodegenSession session, ModuleDefinition importReturnModule = null)
|
||||
{
|
||||
md.Body.Instructions.Clear();
|
||||
ILProcessor processor = md.Body.GetILProcessor();
|
||||
processor.Add(session.GetClass<GeneralHelper>().CreateRetDefault(md, importReturnModule));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the ParameterDefinition index from end of parameters.
|
||||
/// </summary>
|
||||
/// <param name="md"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
internal static ParameterDefinition GetEndParameter(this MethodDefinition md, int index)
|
||||
{
|
||||
//Not enough parameters.
|
||||
if (md.Parameters.Count < (index + 1))
|
||||
return null;
|
||||
|
||||
return md.Parameters[md.Parameters.Count - (index + 1)];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a variable type within the body and returns it's VariableDef.
|
||||
/// </summary>
|
||||
internal static VariableDefinition CreateVariable(this MethodDefinition methodDef, TypeReference variableTypeRef)
|
||||
{
|
||||
VariableDefinition variableDef = new VariableDefinition(variableTypeRef);
|
||||
methodDef.Body.Variables.Add(variableDef);
|
||||
return variableDef;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a variable type within the body and returns it's VariableDef.
|
||||
/// </summary>
|
||||
internal static VariableDefinition CreateVariable(this MethodDefinition methodDef, CodegenSession session, System.Type variableType)
|
||||
{
|
||||
return CreateVariable(methodDef, session.GetClass<GeneralHelper>().GetTypeReference(variableType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the proper OpCode to use for call methods.
|
||||
/// </summary>
|
||||
public static MonoFN.Cecil.Cil.OpCode GetCallOpCode(this MethodDefinition md)
|
||||
{
|
||||
if (md.Attributes.HasFlag(MethodAttributes.Virtual))
|
||||
return MonoFN.Cecil.Cil.OpCodes.Callvirt;
|
||||
else
|
||||
return MonoFN.Cecil.Cil.OpCodes.Call;
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the proper OpCode to use for call methods.
|
||||
/// </summary>
|
||||
public static MonoFN.Cecil.Cil.OpCode GetCallOpCode(this MethodReference mr, CodegenSession session)
|
||||
{
|
||||
return mr.CachedResolve(session).GetCallOpCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds otherMd parameters to thisMR and returns added parameters.
|
||||
/// </summary>
|
||||
public static List<ParameterDefinition> CreateParameters(this MethodReference thisMr, CodegenSession session, MethodDefinition otherMd)
|
||||
{
|
||||
return thisMr.CachedResolve(session).CreateParameters(session, otherMd);
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds otherMr parameters to thisMR and returns added parameters.
|
||||
/// </summary>
|
||||
public static List<ParameterDefinition> CreateParameters(this MethodReference thisMr, CodegenSession session, MethodReference otherMr)
|
||||
{
|
||||
return thisMr.CachedResolve(session).CreateParameters(session, otherMr.CachedResolve(session));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds otherMd parameters to thisMd and returns added parameters.
|
||||
/// </summary>
|
||||
public static List<ParameterDefinition> CreateParameters(this MethodDefinition thisMd, CodegenSession session, MethodDefinition otherMd)
|
||||
{
|
||||
List<ParameterDefinition> results = new List<ParameterDefinition>();
|
||||
|
||||
foreach (ParameterDefinition pd in otherMd.Parameters)
|
||||
{
|
||||
session.ImportReference(pd.ParameterType);
|
||||
int currentCount = thisMd.Parameters.Count;
|
||||
string name = (pd.Name + currentCount);
|
||||
ParameterDefinition parameterDef = new ParameterDefinition(name, pd.Attributes, pd.ParameterType);
|
||||
//Set any default values.
|
||||
parameterDef.Constant = pd.Constant;
|
||||
parameterDef.IsReturnValue = pd.IsReturnValue;
|
||||
parameterDef.IsOut = pd.IsOut;
|
||||
foreach (CustomAttribute item in pd.CustomAttributes)
|
||||
parameterDef.CustomAttributes.Add(item);
|
||||
parameterDef.HasConstant = pd.HasConstant;
|
||||
parameterDef.HasDefault = pd.HasDefault;
|
||||
|
||||
thisMd.Parameters.Add(parameterDef);
|
||||
|
||||
results.Add(parameterDef);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a method reference while considering if declaring type is generic.
|
||||
/// </summary>
|
||||
public static MethodReference GetMethodReference(this MethodDefinition md, CodegenSession session)
|
||||
{
|
||||
MethodReference methodRef = session.ImportReference(md);
|
||||
|
||||
//Is generic.
|
||||
if (md.DeclaringType.HasGenericParameters)
|
||||
{
|
||||
GenericInstanceType git = methodRef.DeclaringType.MakeGenericInstanceType();
|
||||
MethodReference result = new MethodReference(md.Name, md.ReturnType)
|
||||
{
|
||||
HasThis = md.HasThis,
|
||||
ExplicitThis = md.ExplicitThis,
|
||||
DeclaringType = git,
|
||||
CallingConvention = md.CallingConvention,
|
||||
};
|
||||
foreach (ParameterDefinition pd in md.Parameters)
|
||||
{
|
||||
session.ImportReference(pd.ParameterType);
|
||||
result.Parameters.Add(pd);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return methodRef;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a method reference for a generic method.
|
||||
/// </summary>
|
||||
public static MethodReference GetMethodReference(this MethodDefinition md, CodegenSession session, TypeReference typeReference)
|
||||
{
|
||||
MethodReference methodRef = session.ImportReference(md);
|
||||
return methodRef.GetMethodReference(session, typeReference);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a method reference for a generic method.
|
||||
/// </summary>
|
||||
public static MethodReference GetMethodReference(this MethodDefinition md, CodegenSession session, TypeReference[] typeReferences)
|
||||
{
|
||||
MethodReference methodRef = session.ImportReference(md);
|
||||
return methodRef.GetMethodReference(session, typeReferences);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes a method definition public.
|
||||
/// </summary>
|
||||
public static void SetPublicAttributes(this MethodDefinition md)
|
||||
{
|
||||
md.Attributes = PUBLIC_VIRTUAL_ATTRIBUTES;
|
||||
}
|
||||
public static void SetProtectedAttributes(this MethodDefinition md)
|
||||
{
|
||||
md.Attributes = PROTECTED_VIRTUAL_ATTRIBUTES;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 866ed457fe28c3e4b9698d87b9abd709
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,273 @@
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using MonoFN.Cecil;
|
||||
|
||||
namespace FishNet.CodeGenerating.Extension
|
||||
{
|
||||
|
||||
|
||||
internal static class TypeDefinitionExtensions
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns if a TypeDefinition is nullable.
|
||||
/// </summary>
|
||||
public static bool IsNullable(this TypeDefinition td)
|
||||
{
|
||||
return (td.Name == typeof(System.Nullable<>).Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the first method by a given name.
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <param name="methodName"></param>
|
||||
/// <returns></returns>
|
||||
internal static MethodDefinition GetMethod(this TypeDefinition typeDef, string methodName)
|
||||
{
|
||||
foreach (MethodDefinition md in typeDef.Methods)
|
||||
{
|
||||
if (md.Name == methodName)
|
||||
return md;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static MethodReference GetMethodReferenceInBase(this TypeDefinition td, CodegenSession session, string methodName)
|
||||
{
|
||||
MethodDefinition baseMd = td.GetMethodDefinitionInBase(session, methodName);
|
||||
if (baseMd == null)
|
||||
return null;
|
||||
|
||||
MethodReference baseMr;
|
||||
TypeReference baseTr = td.BaseType;
|
||||
if (baseTr.CachedResolve(session).HasGenericParameters)
|
||||
{
|
||||
GenericInstanceType git = (GenericInstanceType)baseTr;
|
||||
baseMr = new MethodReference(baseMd.Name, baseMd.ReturnType, git)
|
||||
{
|
||||
HasThis = baseMd.HasThis,
|
||||
CallingConvention = baseMd.CallingConvention,
|
||||
ExplicitThis = baseMd.ExplicitThis,
|
||||
};
|
||||
foreach (ParameterDefinition pd in baseMd.Parameters)
|
||||
{
|
||||
session.ImportReference(pd.ParameterType);
|
||||
baseMr.Parameters.Add(pd);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
baseMr = session.ImportReference(baseMd);
|
||||
}
|
||||
|
||||
return baseMr;
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a method in the next base class.
|
||||
/// </summary>
|
||||
public static MethodDefinition GetMethodDefinitionInBase(this TypeDefinition td, CodegenSession session, string methodName)
|
||||
{
|
||||
if (td.BaseType == null)
|
||||
{
|
||||
session.LogError($"BaseType for {td.FullName} is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
TypeDefinition baseTd = td.BaseType.CachedResolve(session);
|
||||
return baseTd.GetMethod(methodName);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a method in the next base class.
|
||||
/// </summary>
|
||||
public static MethodReference GetMethodReference(this TypeDefinition td, CodegenSession session, string methodName)
|
||||
{
|
||||
MethodDefinition md = td.GetMethod(methodName);
|
||||
//Not found.
|
||||
if (md == null)
|
||||
return null;
|
||||
|
||||
return md.GetMethodReference(session);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a MethodReference or creates one if missing.
|
||||
/// </summary>
|
||||
public static MethodReference GetOrCreateMethodReference(this TypeDefinition td, CodegenSession session, string methodName, MethodAttributes attributes, TypeReference returnType, out bool created)
|
||||
{
|
||||
MethodDefinition md = td.GetMethod(methodName);
|
||||
//Not found.
|
||||
if (md == null)
|
||||
{
|
||||
md = new MethodDefinition(methodName, attributes, returnType);
|
||||
td.Methods.Add(md);
|
||||
created = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
created = false;
|
||||
}
|
||||
|
||||
return md.GetMethodReference(session);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets a MethodDefinition or creates one if missing.
|
||||
/// </summary>
|
||||
public static MethodDefinition GetOrCreateMethodDefinition(this TypeDefinition td, CodegenSession session, string methodName, MethodAttributes attributes, TypeReference returnType, out bool created)
|
||||
{
|
||||
MethodDefinition md = td.GetMethod(methodName);
|
||||
//Not found.
|
||||
if (md == null)
|
||||
{
|
||||
md = new MethodDefinition(methodName, attributes, returnType);
|
||||
td.Methods.Add(md);
|
||||
created = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
created = false;
|
||||
}
|
||||
|
||||
return md;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a MethodDefinition or creates one if missing.
|
||||
/// </summary>
|
||||
public static MethodDefinition GetOrCreateMethodDefinition(this TypeDefinition td, CodegenSession session, string methodName, MethodDefinition methodTemplate, bool copyParameters, out bool created)
|
||||
{
|
||||
MethodDefinition md = td.GetMethod(methodName);
|
||||
//Not found.
|
||||
if (md == null)
|
||||
{
|
||||
TypeReference returnType = session.ImportReference(methodTemplate.ReturnType);
|
||||
md = new MethodDefinition(methodName, methodTemplate.Attributes, returnType)
|
||||
{
|
||||
ExplicitThis = methodTemplate.ExplicitThis,
|
||||
AggressiveInlining = methodTemplate.AggressiveInlining,
|
||||
Attributes = methodTemplate.Attributes,
|
||||
CallingConvention = methodTemplate.CallingConvention,
|
||||
HasThis = methodTemplate.HasThis,
|
||||
};
|
||||
md.Body.InitLocals = methodTemplate.Body.InitLocals;
|
||||
|
||||
if (copyParameters)
|
||||
{
|
||||
foreach (ParameterDefinition pd in methodTemplate.Parameters)
|
||||
md.Parameters.Add(pd);
|
||||
}
|
||||
|
||||
foreach (var item in methodTemplate.GenericParameters)
|
||||
md.GenericParameters.Add(item);
|
||||
|
||||
td.Methods.Add(md);
|
||||
created = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
created = false;
|
||||
}
|
||||
|
||||
return md;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a method in any inherited classes. The first found method is returned.
|
||||
/// </summary>
|
||||
public static MethodDefinition GetMethodDefinitionInAnyBase(this TypeDefinition td, CodegenSession session, string methodName)
|
||||
{
|
||||
while (td != null)
|
||||
{
|
||||
foreach (MethodDefinition md in td.Methods)
|
||||
{
|
||||
if (md.Name == methodName)
|
||||
return md;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
td = td.GetNextBaseTypeDefinition(session);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the next base type.
|
||||
/// </summary>
|
||||
public static TypeDefinition GetNextBaseTypeDefinition(this TypeDefinition typeDef, CodegenSession session)
|
||||
{
|
||||
return (typeDef.BaseType == null) ? null : typeDef.BaseType.CachedResolve(session);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a FieldReference.
|
||||
/// </summary>
|
||||
public static FieldReference CreateFieldReference(this FieldDefinition fd, CodegenSession session)
|
||||
{
|
||||
FieldReference fr;
|
||||
TypeDefinition declaringType = fd.DeclaringType;
|
||||
//Is generic.
|
||||
if (declaringType.HasGenericParameters)
|
||||
{
|
||||
GenericInstanceType git = new GenericInstanceType(declaringType);
|
||||
foreach (GenericParameter item in declaringType.GenericParameters)
|
||||
git.GenericArguments.Add(item);
|
||||
fr = new FieldReference(fd.Name, fd.FieldType, git);
|
||||
return fr;
|
||||
}
|
||||
//Not generic.
|
||||
else
|
||||
{
|
||||
return session.ImportReference(fd);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a FieldReference or creates it if missing.
|
||||
/// </summary>
|
||||
public static FieldReference GetOrCreateFieldReference(this TypeDefinition td, CodegenSession session, string fieldName, FieldAttributes attributes, TypeReference fieldTypeRef, out bool created)
|
||||
{
|
||||
FieldReference fr = td.GetFieldReference(fieldName, session);
|
||||
if (fr == null)
|
||||
{
|
||||
fr = td.CreateFieldDefinition(session, fieldName, attributes, fieldTypeRef);
|
||||
created = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
created = false;
|
||||
}
|
||||
|
||||
return fr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a FieldReference.
|
||||
/// </summary>
|
||||
public static FieldReference CreateFieldDefinition(this TypeDefinition td, CodegenSession session, string fieldName, FieldAttributes attributes, TypeReference fieldTypeRef)
|
||||
{
|
||||
FieldDefinition fd = new FieldDefinition(fieldName, attributes, fieldTypeRef);
|
||||
td.Fields.Add(fd);
|
||||
return fd.CreateFieldReference(session);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9f00cf3dc8b90b469c3c9cb8b87fc88
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,51 @@
|
||||
|
||||
using FishNet.CodeGenerating.Helping;
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using MonoFN.Cecil;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.CodeGenerating.Extension
|
||||
{
|
||||
|
||||
|
||||
internal static class TypeReferenceExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Returns if a TypeReference is nullable.
|
||||
/// </summary>
|
||||
public static bool IsNullable(this TypeReference tr, CodegenSession session)
|
||||
{
|
||||
TypeDefinition td = tr.CachedResolve(session);
|
||||
return td.IsNullable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the fullname of a TypeReference without <>.
|
||||
/// </summary>
|
||||
/// <param name="tr"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetFullnameWithoutBrackets(this TypeReference tr)
|
||||
{
|
||||
string str = tr.FullName;
|
||||
str = str.Replace("<", "");
|
||||
str = str.Replace(">", "");
|
||||
return str;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes a GenericInstanceType.
|
||||
/// </summary>
|
||||
public static GenericInstanceType MakeGenericInstanceType(this TypeReference self)
|
||||
{
|
||||
GenericInstanceType instance = new GenericInstanceType(self);
|
||||
foreach (GenericParameter argument in self.GenericParameters)
|
||||
instance.GenericArguments.Add(argument);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 239b1b10db80c594d93b7ba4ee2c1ec5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
After updating a custom Cecil to fix conflict with Unity.Burst in 2021 perform the following:
|
||||
|
||||
- Open cecil in it's own project; eg: do not place directly in FN.
|
||||
- Rename namespace.Mono to namespace.MonoFN.
|
||||
- Current project rename strings, "Mono to "MonoFN
|
||||
- Replace current project #if INSIDE_ROCKS to #if UNITY_EDITOR
|
||||
- Comment out `[assembly: AssemblyTitle ("MonoFN.Cecil.Rocks")]` within rocks\Mono.Cecil.Rocks\AssemblyInfo.cs.
|
||||
- Delete obj/bin/tests folders.
|
||||
- Copy into FN project.
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9133eb285bd7f3c4f89f4d7a2a079c6b
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
phr/StickGame/Assets/FishNet/CodeGenerating/Helpers.meta
Normal file
8
phr/StickGame/Assets/FishNet/CodeGenerating/Helpers.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d27bb367daea26d46a8fa5f6ca02865e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,93 @@
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using FishNet.Object;
|
||||
using FishNet.Object.Helping;
|
||||
using FishNet.Object.Prediction;
|
||||
using FishNet.Object.Synchronizing;
|
||||
using MonoFN.Cecil;
|
||||
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
internal class AttributeHelper : CodegenBase
|
||||
{
|
||||
#region Reflection references.
|
||||
internal string ReplicateAttribute_FullName;
|
||||
internal string ReconcileAttribute_FullName;
|
||||
private string ServerAttribute_FullName;
|
||||
private string ClientAttribute_FullName;
|
||||
private string ServerRpcAttribute_FullName;
|
||||
private string ObserversRpcAttribute_FullName;
|
||||
private string TargetRpcAttribute_FullName;
|
||||
private string SyncVarAttribute_FullName;
|
||||
private string SyncObjectAttribute_FullName;
|
||||
#endregion
|
||||
|
||||
public override bool ImportReferences()
|
||||
{
|
||||
ServerAttribute_FullName = typeof(ServerAttribute).FullName;
|
||||
ClientAttribute_FullName = typeof(ClientAttribute).FullName;
|
||||
ServerRpcAttribute_FullName = typeof(ServerRpcAttribute).FullName;
|
||||
ObserversRpcAttribute_FullName = typeof(ObserversRpcAttribute).FullName;
|
||||
TargetRpcAttribute_FullName = typeof(TargetRpcAttribute).FullName;
|
||||
SyncVarAttribute_FullName = typeof(SyncVarAttribute).FullName;
|
||||
SyncObjectAttribute_FullName = typeof(SyncObjectAttribute).FullName;
|
||||
ReplicateAttribute_FullName = typeof(ReplicateAttribute).FullName;
|
||||
ReconcileAttribute_FullName = typeof(ReconcileAttribute).FullName;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns type of Rpc attributeFullName is for.
|
||||
/// </summary>
|
||||
/// <param name="attributeFullName"></param>
|
||||
/// <returns></returns>
|
||||
public RpcType GetRpcAttributeType(CustomAttribute ca)
|
||||
{
|
||||
if (ca.Is(ServerRpcAttribute_FullName))
|
||||
return RpcType.Server;
|
||||
else if (ca.Is(ObserversRpcAttribute_FullName))
|
||||
return RpcType.Observers;
|
||||
else if (ca.Is(TargetRpcAttribute_FullName))
|
||||
return RpcType.Target;
|
||||
else
|
||||
return RpcType.None;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns type of Rpc attributeFullName is for.
|
||||
/// </summary>
|
||||
/// <param name="attributeFullName"></param>
|
||||
/// <returns></returns>
|
||||
internal QolAttributeType GetQolAttributeType(string attributeFullName)
|
||||
{
|
||||
if (attributeFullName == ServerAttribute_FullName)
|
||||
return QolAttributeType.Server;
|
||||
else if (attributeFullName == ClientAttribute_FullName)
|
||||
return QolAttributeType.Client;
|
||||
else
|
||||
return QolAttributeType.None;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns if attribute if a SyncVarAttribute.
|
||||
/// </summary>
|
||||
/// <param name="attributeFullName"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsSyncVarAttribute(string attributeFullName)
|
||||
{
|
||||
return (attributeFullName == SyncVarAttribute_FullName);
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns if attribute if a SyncObjectAttribute.
|
||||
/// </summary>
|
||||
/// <param name="attributeFullName"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsSyncObjectAttribute(string attributeFullName)
|
||||
{
|
||||
return (attributeFullName == SyncObjectAttribute_FullName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d32f3dc23b55598429c5cfe6156e6243
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,194 @@
|
||||
using FishNet.CodeGenerating.Helping;
|
||||
using FishNet.CodeGenerating.ILCore;
|
||||
using FishNet.CodeGenerating.Processing;
|
||||
using FishNet.CodeGenerating.Processing.Rpc;
|
||||
using MonoFN.Cecil;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.CompilationPipeline.Common.Diagnostics;
|
||||
#if !UNITY_2020_1_OR_NEWER
|
||||
using UnityEngine;
|
||||
#endif
|
||||
using SR = System.Reflection;
|
||||
|
||||
|
||||
namespace FishNet.CodeGenerating
|
||||
{
|
||||
|
||||
internal class CodegenSession
|
||||
{
|
||||
/// <summary>
|
||||
/// Current module for this session.
|
||||
/// </summary>
|
||||
internal ModuleDefinition Module;
|
||||
/// <summary>
|
||||
/// Outputs errors when codegen fails.
|
||||
/// </summary>
|
||||
internal List<DiagnosticMessage> Diagnostics;
|
||||
/// <summary>
|
||||
/// SyncVars that are being accessed from an assembly other than the currently being processed one.
|
||||
/// </summary>
|
||||
internal List<FieldDefinition> DifferentAssemblySyncVars = new List<FieldDefinition>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// CodegenBase classes for processing a module.
|
||||
/// </summary>
|
||||
private List<CodegenBase> _bases;
|
||||
/// <summary>
|
||||
/// Quick lookup of base classes.
|
||||
/// </summary>
|
||||
private Dictionary<string, CodegenBase> _basesCache = new Dictionary<string, CodegenBase>();
|
||||
|
||||
/// <summary>
|
||||
/// Returns class of type if found within CodegenBase classes.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
internal T GetClass<T>() where T : CodegenBase
|
||||
{
|
||||
string tName = typeof(T).Name;
|
||||
return (T)_basesCache[tName];
|
||||
}
|
||||
/// <summary>
|
||||
/// Resets all helpers while importing any information needed by them.
|
||||
/// </summary>
|
||||
/// <param name="module"></param>
|
||||
/// <returns></returns>
|
||||
internal bool Initialize(ModuleDefinition module)
|
||||
{
|
||||
Module = module;
|
||||
Diagnostics = new List<DiagnosticMessage>();
|
||||
|
||||
_bases = new List<CodegenBase>()
|
||||
{
|
||||
new ReaderImports(), new ReaderProcessor()
|
||||
,new WriterImports(), new WriterProcessor()
|
||||
, new PhysicsHelper(), new TimeManagerHelper(), new AttributeHelper(), new GeneralHelper()
|
||||
, new ObjectHelper(), new NetworkBehaviourHelper()
|
||||
, new CreatedSyncVarGenerator(), new TransportHelper()
|
||||
, new NetworkConnectionImports(), new PredictedObjectHelper(), new GeneratorHelper()
|
||||
, new CustomSerializerProcessor()
|
||||
, new NetworkBehaviourProcessor()
|
||||
, new QolAttributeProcessor()
|
||||
, new RpcProcessor()
|
||||
, new NetworkBehaviourSyncProcessor()
|
||||
, new PredictionProcessor()
|
||||
};
|
||||
|
||||
//Add all to dictionary first, then import.
|
||||
foreach (CodegenBase item in _bases)
|
||||
{
|
||||
string tName = item.GetType().Name;
|
||||
_basesCache.Add(tName, item);
|
||||
}
|
||||
|
||||
//Initialize.
|
||||
foreach (CodegenBase item in _bases)
|
||||
{
|
||||
item.Initialize(this);
|
||||
if (!item.ImportReferences())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#region Logging.
|
||||
/// <summary>
|
||||
/// Logs a warning.
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
internal void LogWarning(string msg)
|
||||
{
|
||||
#if UNITY_2020_1_OR_NEWER
|
||||
Diagnostics.AddWarning(msg);
|
||||
#else
|
||||
Debug.LogWarning(msg);
|
||||
#endif
|
||||
}
|
||||
/// <summary>
|
||||
/// Logs an error.
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
internal void LogError(string msg)
|
||||
{
|
||||
#if UNITY_2020_1_OR_NEWER
|
||||
Diagnostics.AddError(msg);
|
||||
#else
|
||||
Debug.LogError(msg);
|
||||
#endif
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ImportReference.
|
||||
|
||||
public MethodReference ImportReference(SR.MethodBase method)
|
||||
{
|
||||
return Module.ImportReference(method);
|
||||
}
|
||||
|
||||
public MethodReference ImportReference(SR.MethodBase method, IGenericParameterProvider context)
|
||||
{
|
||||
return Module.ImportReference(method, context);
|
||||
}
|
||||
|
||||
public TypeReference ImportReference(TypeReference type)
|
||||
{
|
||||
return Module.ImportReference(type);
|
||||
}
|
||||
|
||||
public TypeReference ImportReference(TypeReference type, IGenericParameterProvider context)
|
||||
{
|
||||
return Module.ImportReference(type, context);
|
||||
}
|
||||
|
||||
public FieldReference ImportReference(FieldReference field)
|
||||
{
|
||||
return Module.ImportReference(field);
|
||||
}
|
||||
|
||||
public FieldReference ImportReference(FieldReference field, IGenericParameterProvider context)
|
||||
{
|
||||
return Module.ImportReference(field, context);
|
||||
}
|
||||
public MethodReference ImportReference(MethodReference method)
|
||||
{
|
||||
return Module.ImportReference(method);
|
||||
}
|
||||
|
||||
public MethodReference ImportReference(MethodReference method, IGenericParameterProvider context)
|
||||
{
|
||||
return Module.ImportReference(method, context);
|
||||
}
|
||||
public TypeReference ImportReference(System.Type type)
|
||||
{
|
||||
return ImportReference(type, null);
|
||||
}
|
||||
|
||||
|
||||
public TypeReference ImportReference(System.Type type, IGenericParameterProvider context)
|
||||
{
|
||||
return Module.ImportReference(type, context);
|
||||
}
|
||||
|
||||
|
||||
public FieldReference ImportReference(SR.FieldInfo field)
|
||||
{
|
||||
return Module.ImportReference(field);
|
||||
}
|
||||
|
||||
public FieldReference ImportReference(SR.FieldInfo field, IGenericParameterProvider context)
|
||||
{
|
||||
return Module.ImportReference(field, context);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e8416eee3308f54fa942003de975420
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,121 @@
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using FishNet.CodeGenerating.Processing;
|
||||
using FishNet.Object.Synchronizing;
|
||||
using FishNet.Object.Synchronizing.Internal;
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Rocks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
internal class CreatedSyncVarGenerator : CodegenBase
|
||||
{
|
||||
private readonly Dictionary<string, CreatedSyncVar> _createdSyncVars = new Dictionary<string, CreatedSyncVar>();
|
||||
|
||||
#region Relfection references.
|
||||
private TypeReference _syncBase_TypeRef;
|
||||
internal TypeReference SyncVar_TypeRef;
|
||||
private MethodReference _syncVar_Constructor_MethodRef;
|
||||
#endregion
|
||||
|
||||
#region Const.
|
||||
private const string GETVALUE_NAME = "GetValue";
|
||||
private const string SETVALUE_NAME = "SetValue";
|
||||
#endregion
|
||||
|
||||
/* //feature add and test the dirty boolean changes
|
||||
* eg... instead of base.Dirty()
|
||||
* do if (!base.Dirty()) return false;
|
||||
* See synclist for more info. */
|
||||
|
||||
/// <summary>
|
||||
/// Imports references needed by this helper.
|
||||
/// </summary>
|
||||
/// <param name="moduleDef"></param>
|
||||
/// <returns></returns>
|
||||
public override bool ImportReferences()
|
||||
{
|
||||
SyncVar_TypeRef = base.ImportReference(typeof(SyncVar<>));
|
||||
MethodDefinition svConstructor = SyncVar_TypeRef.GetFirstConstructor(base.Session, true);
|
||||
_syncVar_Constructor_MethodRef = base.ImportReference(svConstructor);
|
||||
|
||||
Type syncBaseType = typeof(SyncBase);
|
||||
_syncBase_TypeRef = base.ImportReference(syncBaseType);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and optionally creates data for SyncVar<typeOfField>
|
||||
/// </summary>
|
||||
/// <param name="dataTr"></param>
|
||||
/// <returns></returns>
|
||||
internal CreatedSyncVar GetCreatedSyncVar(FieldDefinition originalFd, bool createMissing)
|
||||
{
|
||||
TypeReference dataTr = originalFd.FieldType;
|
||||
TypeDefinition dataTd = dataTr.CachedResolve(base.Session);
|
||||
|
||||
string typeHash = dataTr.FullName + dataTr.IsArray.ToString();
|
||||
|
||||
if (_createdSyncVars.TryGetValue(typeHash, out CreatedSyncVar createdSyncVar))
|
||||
{
|
||||
return createdSyncVar;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!createMissing)
|
||||
return null;
|
||||
|
||||
base.ImportReference(dataTd);
|
||||
|
||||
GenericInstanceType syncVarGit = SyncVar_TypeRef.MakeGenericInstanceType(new TypeReference[] { dataTr });
|
||||
TypeReference genericDataTr = syncVarGit.GenericArguments[0];
|
||||
|
||||
//Make sure can serialize.
|
||||
bool canSerialize = base.GetClass<GeneralHelper>().HasSerializerAndDeserializer(genericDataTr, true);
|
||||
if (!canSerialize)
|
||||
{
|
||||
base.LogError($"SyncVar {originalFd.Name} data type {genericDataTr.FullName} does not support serialization. Use a supported type or create a custom serializer.");
|
||||
return null;
|
||||
}
|
||||
|
||||
//Set needed methods from syncbase.
|
||||
MethodReference setSyncIndexMr;
|
||||
MethodReference genericSyncVarCtor = _syncVar_Constructor_MethodRef.MakeHostInstanceGeneric(base.Session, syncVarGit);
|
||||
|
||||
if (!base.GetClass<NetworkBehaviourSyncProcessor>().SetSyncBaseMethods(_syncBase_TypeRef.CachedResolve(base.Session), out setSyncIndexMr, out _))
|
||||
return null;
|
||||
|
||||
MethodReference setValueMr = null;
|
||||
MethodReference getValueMr = null;
|
||||
foreach (MethodDefinition md in SyncVar_TypeRef.CachedResolve(base.Session).Methods)
|
||||
{
|
||||
//GetValue.
|
||||
if (md.Name == GETVALUE_NAME)
|
||||
{
|
||||
MethodReference mr = base.ImportReference(md);
|
||||
getValueMr = mr.MakeHostInstanceGeneric(base.Session, syncVarGit);
|
||||
}
|
||||
//SetValue.
|
||||
else if (md.Name == SETVALUE_NAME)
|
||||
{
|
||||
MethodReference mr = base.ImportReference(md);
|
||||
setValueMr = mr.MakeHostInstanceGeneric(base.Session, syncVarGit);
|
||||
}
|
||||
}
|
||||
|
||||
if (setValueMr == null || getValueMr == null)
|
||||
return null;
|
||||
|
||||
CreatedSyncVar csv = new CreatedSyncVar(syncVarGit, dataTd, getValueMr, setValueMr, setSyncIndexMr, null, genericSyncVarCtor);
|
||||
_createdSyncVars.Add(typeHash, csv);
|
||||
return csv;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 935ec97b96b35b94f8c6880c6908304c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6fee1744ec071184db72fc2443e77ece
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,54 @@
|
||||
using MonoFN.Cecil;
|
||||
using System.Linq;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping.Extension
|
||||
{
|
||||
|
||||
internal static class CustomAttributeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Finds a field within an attribute.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="customAttr"></param>
|
||||
/// <param name="field"></param>
|
||||
/// <param name="defaultValue"></param>
|
||||
/// <returns></returns>
|
||||
internal static T GetField<T>(this CustomAttribute customAttr, string field, T defaultValue)
|
||||
{
|
||||
foreach (CustomAttributeNamedArgument customField in customAttr.Fields)
|
||||
{
|
||||
if (customField.Name == field)
|
||||
{
|
||||
return (T)customField.Argument.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if any of the attributes match IAtrribute.
|
||||
/// </summary>
|
||||
/// <typeparam name="TAttribute"></typeparam>
|
||||
/// <param name="attributeProvider"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool HasCustomAttribute<TAttribute>(this ICustomAttributeProvider attributeProvider)
|
||||
{
|
||||
return attributeProvider.CustomAttributes.Any(attr => attr.AttributeType.Is<TAttribute>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if ca is of type target.
|
||||
/// </summary>
|
||||
/// <param name="ca"></param>
|
||||
/// <param name="targetFullName"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool Is(this CustomAttribute ca, string targetFullName)
|
||||
{
|
||||
return ca.AttributeType.FullName == targetFullName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a66d771ab331fae408142a5c04abd74e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,41 @@
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Cil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Unity.CompilationPipeline.Common.Diagnostics;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
internal static class Diagnostics
|
||||
{
|
||||
internal static void AddError(this List<DiagnosticMessage> diagnostics, string message)
|
||||
{
|
||||
diagnostics.AddMessage(DiagnosticType.Error, (SequencePoint)null, message);
|
||||
}
|
||||
|
||||
internal static void AddWarning(this List<DiagnosticMessage> diagnostics, string message)
|
||||
{
|
||||
diagnostics.AddMessage(DiagnosticType.Warning, (SequencePoint)null, message);
|
||||
}
|
||||
|
||||
internal static void AddError(this List<DiagnosticMessage> diagnostics, MethodDefinition methodDef, string message)
|
||||
{
|
||||
diagnostics.AddMessage(DiagnosticType.Error, methodDef.DebugInformation.SequencePoints.FirstOrDefault(), message);
|
||||
}
|
||||
|
||||
internal static void AddMessage(this List<DiagnosticMessage> diagnostics, DiagnosticType diagnosticType, SequencePoint sequencePoint, string message)
|
||||
{
|
||||
diagnostics.Add(new DiagnosticMessage
|
||||
{
|
||||
DiagnosticType = diagnosticType,
|
||||
File = sequencePoint?.Document.Url.Replace($"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}", ""),
|
||||
Line = sequencePoint?.StartLine ?? 0,
|
||||
Column = sequencePoint?.StartColumn ?? 0,
|
||||
MessageData = $" - {message}"
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb7b65b572b01444cbe3c9d830cd3587
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,38 @@
|
||||
using FishNet.CodeGenerating.Extension;
|
||||
using MonoFN.Cecil;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping.Extension
|
||||
{
|
||||
|
||||
internal static class FieldReferenceExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Resolve favoring cached results first.
|
||||
/// </summary>
|
||||
internal static FieldDefinition CachedResolve(this FieldReference fieldRef, CodegenSession session)
|
||||
{
|
||||
return session.GetClass<GeneralHelper>().GetFieldReferenceResolve(fieldRef);
|
||||
}
|
||||
|
||||
|
||||
public static FieldReference MakeHostGenericIfNeeded(this FieldDefinition fd, CodegenSession session)
|
||||
{
|
||||
TypeReference declaringTr = fd.DeclaringType;
|
||||
|
||||
if (declaringTr.HasGenericParameters)
|
||||
{
|
||||
GenericInstanceType git = declaringTr.MakeGenericInstanceType();
|
||||
FieldReference result = new FieldReference(fd.Name, fd.FieldType, git);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return session.ImportReference(fd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d983ebd0c9e1b745902030c2f7a8e99
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,191 @@
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using MonoFN.Cecil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
internal static class Constructors
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first constructor that optionally has, or doesn't have parameters.
|
||||
/// </summary>
|
||||
/// <param name="typeRef"></param>
|
||||
/// <returns></returns>
|
||||
public static MethodDefinition GetFirstConstructor(this TypeReference typeRef, CodegenSession session, bool requireParameters)
|
||||
{
|
||||
return typeRef.CachedResolve(session).GetFirstConstructor(requireParameters);
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the first constructor that optionally has, or doesn't have parameters.
|
||||
/// </summary>
|
||||
/// <param name="typeRef"></param>
|
||||
/// <returns></returns>
|
||||
public static MethodDefinition GetFirstConstructor(this TypeDefinition typeDef, bool requireParameters)
|
||||
{
|
||||
|
||||
foreach (MethodDefinition methodDef in typeDef.Methods)
|
||||
{
|
||||
if (methodDef.IsConstructor && methodDef.IsPublic)
|
||||
{
|
||||
if (requireParameters && methodDef.Parameters.Count > 0)
|
||||
return methodDef;
|
||||
else if (!requireParameters && methodDef.Parameters.Count == 0)
|
||||
return methodDef;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first public constructor with no parameters.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static MethodDefinition GetDefaultConstructor(this TypeReference typeRef, CodegenSession session)
|
||||
{
|
||||
return typeRef.CachedResolve(session).GetDefaultConstructor();
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the first public constructor with no parameters.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static MethodDefinition GetDefaultConstructor(this TypeDefinition typeDef)
|
||||
{
|
||||
foreach (MethodDefinition methodDef in typeDef.Methods)
|
||||
{
|
||||
if (methodDef.IsConstructor && methodDef.IsPublic && methodDef.Parameters.Count == 0)
|
||||
return methodDef;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all constructors on typeDef.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static List<MethodDefinition> GetConstructors(this TypeDefinition typeDef)
|
||||
{
|
||||
List<MethodDefinition> lst = new List<MethodDefinition>();
|
||||
foreach (MethodDefinition methodDef in typeDef.Methods)
|
||||
{
|
||||
if (methodDef.IsConstructor)
|
||||
lst.Add(methodDef);
|
||||
}
|
||||
|
||||
return lst;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets constructor which has arguments.
|
||||
/// </summary>
|
||||
/// <param name="typeRef"></param>
|
||||
/// <returns></returns>
|
||||
public static MethodDefinition GetConstructor(this TypeReference typeRef, CodegenSession session, Type[] arguments)
|
||||
{
|
||||
return typeRef.CachedResolve(session).GetConstructor(arguments);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets constructor which has arguments.
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <returns></returns>
|
||||
public static MethodDefinition GetConstructor(this TypeDefinition typeDef, Type[] arguments)
|
||||
{
|
||||
Type[] argsCopy = (arguments == null) ? new Type[0] : arguments;
|
||||
foreach (MethodDefinition methodDef in typeDef.Methods)
|
||||
{
|
||||
if (methodDef.IsConstructor && methodDef.IsPublic && methodDef.Parameters.Count == argsCopy.Length)
|
||||
{
|
||||
bool match = true;
|
||||
for (int i = 0; i < argsCopy.Length; i++)
|
||||
{
|
||||
if (methodDef.Parameters[0].ParameterType.FullName != argsCopy[i].FullName)
|
||||
{
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match)
|
||||
return methodDef;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets constructor which has arguments.
|
||||
/// </summary>
|
||||
/// <param name="typeRef"></param>
|
||||
/// <returns></returns>
|
||||
public static MethodDefinition GetConstructor(this TypeReference typeRef, CodegenSession session, TypeReference[] arguments)
|
||||
{
|
||||
return typeRef.CachedResolve(session).GetConstructor(arguments);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets constructor which has arguments.
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <returns></returns>
|
||||
public static MethodDefinition GetConstructor(this TypeDefinition typeDef, TypeReference[] arguments)
|
||||
{
|
||||
TypeReference[] argsCopy = (arguments == null) ? new TypeReference[0] : arguments;
|
||||
foreach (MethodDefinition methodDef in typeDef.Methods)
|
||||
{
|
||||
if (methodDef.IsConstructor && methodDef.IsPublic && methodDef.Parameters.Count == argsCopy.Length)
|
||||
{
|
||||
bool match = true;
|
||||
for (int i = 0; i < argsCopy.Length; i++)
|
||||
{
|
||||
if (methodDef.Parameters[0].ParameterType.FullName != argsCopy[i].FullName)
|
||||
{
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match)
|
||||
return methodDef;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the constructor with parameterCount for typeRef.
|
||||
/// </summary>
|
||||
/// <param name="typeRef"></param>
|
||||
/// <returns></returns>
|
||||
public static MethodDefinition GetConstructor(this TypeReference typeRef, CodegenSession session, int parameterCount)
|
||||
{
|
||||
return typeRef.CachedResolve(session).GetConstructor(parameterCount);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the constructor with parameterCount for typeRef.
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <returns></returns>
|
||||
public static MethodDefinition GetConstructor(this TypeDefinition typeDef, int parameterCount)
|
||||
{
|
||||
foreach (MethodDefinition methodDef in typeDef.Methods)
|
||||
{
|
||||
if (methodDef.IsConstructor && methodDef.IsPublic && methodDef.Parameters.Count == parameterCount)
|
||||
return methodDef;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a7e03137ca78704e999f3a3dc68b953
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,169 @@
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Cil;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping.Extension
|
||||
{
|
||||
|
||||
internal static class ILProcessorExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Creates a debug log for text without any conditions.
|
||||
/// </summary>
|
||||
public static void DebugLog(this ILProcessor processor, CodegenSession session, string txt)
|
||||
{
|
||||
processor.Emit(OpCodes.Ldstr, txt);
|
||||
processor.Emit(OpCodes.Call, session.GetClass<GeneralHelper>().Debug_LogCommon_MethodRef);
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a debug log for vd without any conditions.
|
||||
/// </summary>
|
||||
public static void DebugLog(this ILProcessor processor, CodegenSession session, VariableDefinition vd)
|
||||
{
|
||||
processor.Emit(OpCodes.Ldloc, vd);
|
||||
processor.Emit(OpCodes.Box, vd.VariableType);
|
||||
processor.Emit(OpCodes.Call, session.GetClass<GeneralHelper>().Debug_LogCommon_MethodRef);
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a debug log for vd without any conditions.
|
||||
/// </summary>
|
||||
public static void DebugLog(this ILProcessor processor, CodegenSession session, FieldDefinition fd, bool loadArg0)
|
||||
{
|
||||
if (loadArg0)
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(OpCodes.Ldfld, fd);
|
||||
processor.Emit(OpCodes.Box, fd.FieldType);
|
||||
processor.Emit(OpCodes.Call, session.GetClass<GeneralHelper>().Debug_LogCommon_MethodRef);
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a debug log for pd without any conditions.
|
||||
/// </summary>
|
||||
public static void DebugLog(this ILProcessor processor, CodegenSession session, ParameterDefinition pd)
|
||||
{
|
||||
processor.Emit(OpCodes.Ldloc, pd);
|
||||
processor.Emit(OpCodes.Box, pd.ParameterType);
|
||||
processor.Emit(OpCodes.Call, session.GetClass<GeneralHelper>().Debug_LogCommon_MethodRef);
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Creates a debug log for mr without any conditions.
|
||||
///// </summary>
|
||||
//public static void DebugLog(this ILProcessor processor, MethodReference mr)
|
||||
//{
|
||||
// processor.Emit(OpCodes.Call, mr);
|
||||
// processor.Emit(OpCodes.Box, mr.ReturnType);
|
||||
// processor.Emit(OpCodes.Call, base.GetClass<GeneralHelper>().Debug_LogCommon_MethodRef);
|
||||
//}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Inserts instructions at the beginning.
|
||||
/// </summary>
|
||||
/// <param name="processor"></param>
|
||||
/// <param name="instructions"></param>
|
||||
public static void InsertAt(this ILProcessor processor, int target, List<Instruction> instructions)
|
||||
{
|
||||
for (int i = 0; i < instructions.Count; i++)
|
||||
processor.Body.Instructions.Insert(i + target, instructions[i]);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Inserts instructions at the beginning.
|
||||
/// </summary>
|
||||
/// <param name="processor"></param>
|
||||
/// <param name="instructions"></param>
|
||||
public static void InsertFirst(this ILProcessor processor, List<Instruction> instructions)
|
||||
{
|
||||
for (int i = 0; i < instructions.Count; i++)
|
||||
processor.Body.Instructions.Insert(i, instructions[i]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts instructions at the end while also moving Ret down.
|
||||
/// </summary>
|
||||
/// <param name="processor"></param>
|
||||
/// <param name="instructions"></param>
|
||||
public static void InsertLast(this ILProcessor processor, List<Instruction> instructions)
|
||||
{
|
||||
bool retRemoved = false;
|
||||
int startingCount = processor.Body.Instructions.Count;
|
||||
//Remove ret if it exist and add it back in later.
|
||||
if (startingCount > 0)
|
||||
{
|
||||
if (processor.Body.Instructions[startingCount - 1].OpCode == OpCodes.Ret)
|
||||
{
|
||||
processor.Body.Instructions.RemoveAt(startingCount - 1);
|
||||
retRemoved = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Instruction inst in instructions)
|
||||
processor.Append(inst);
|
||||
|
||||
//Add ret back if it was removed.
|
||||
if (retRemoved)
|
||||
processor.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts instructions before target.
|
||||
/// </summary>
|
||||
/// <param name="processor"></param>
|
||||
/// <param name="instructions"></param>
|
||||
public static void InsertBefore(this ILProcessor processor, Instruction target, List<Instruction> instructions)
|
||||
{
|
||||
int index = processor.Body.Instructions.IndexOf(target);
|
||||
for (int i = 0; i < instructions.Count; i++)
|
||||
processor.Body.Instructions.Insert(index + i, instructions[i]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds instructions to the end of processor.
|
||||
/// </summary>
|
||||
/// <param name="processor"></param>
|
||||
/// <param name="instructions"></param>
|
||||
public static void Add(this ILProcessor processor, List<Instruction> instructions)
|
||||
{
|
||||
for (int i = 0; i < instructions.Count; i++)
|
||||
processor.Body.Instructions.Add(instructions[i]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts instructions before returns. Only works on void types.
|
||||
/// </summary>
|
||||
/// <param name="processor"></param>
|
||||
/// <param name="instructions"></param>
|
||||
public static void InsertBeforeReturns(this ILProcessor processor, CodegenSession session, List<Instruction> instructions)
|
||||
{
|
||||
if (processor.Body.Method.ReturnType.FullName != session.Module.TypeSystem.Void.FullName)
|
||||
{
|
||||
session.LogError($"Cannot insert instructions before returns on {processor.Body.Method.FullName} because it does not return void.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Insert at the end of the method
|
||||
* and get the first instruction that was inserted.
|
||||
* Any returns or breaks which would exit the method
|
||||
* will jump to this instruction instead. */
|
||||
processor.InsertLast(instructions);
|
||||
Instruction startInst = processor.Body.Instructions[processor.Body.Instructions.Count - instructions.Count];
|
||||
|
||||
//Look for anything that jumps to rets.
|
||||
for (int i = 0; i < processor.Body.Instructions.Count; i++)
|
||||
{
|
||||
Instruction inst = processor.Body.Instructions[i];
|
||||
if (inst.Operand is Instruction operInst)
|
||||
{
|
||||
if (operInst.OpCode == OpCodes.Ret)
|
||||
inst.Operand = startInst;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 820cf8401d4d71c4196dda444559ef8a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Cil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
public static class Instructions
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 360667149f16b6c4aba61fd05427cbfb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,166 @@
|
||||
using FishNet.CodeGenerating.Extension;
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Rocks;
|
||||
using System;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping.Extension
|
||||
{
|
||||
|
||||
internal static class MethodReferenceExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Returns a custom attribute.
|
||||
/// </summary>
|
||||
public static CustomAttribute GetCustomAttribute(this MethodReference mr, string attributeFullName)
|
||||
{
|
||||
MethodDefinition md = mr.Resolve();
|
||||
return MethodDefinitionExtensions.GetCustomAttribute(md, attributeFullName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes a generic method with specified arguments.
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="genericArguments"></param>
|
||||
/// <returns></returns>
|
||||
public static GenericInstanceMethod MakeGenericMethod(this MethodReference method, params TypeReference[] genericArguments)
|
||||
{
|
||||
GenericInstanceMethod result = new GenericInstanceMethod(method);
|
||||
foreach (TypeReference argument in genericArguments)
|
||||
result.GenericArguments.Add(argument);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes a generic method with the same arguments as the original.
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
/// <returns></returns>
|
||||
public static GenericInstanceMethod MakeGenericMethod(this MethodReference method)
|
||||
{
|
||||
GenericInstanceMethod result = new GenericInstanceMethod(method);
|
||||
foreach (ParameterDefinition pd in method.Parameters)
|
||||
result.GenericArguments.Add(pd.ParameterType);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a method reference for a generic method.
|
||||
/// </summary>
|
||||
public static MethodReference GetMethodReference(this MethodReference mr, CodegenSession session, TypeReference typeReference)
|
||||
{
|
||||
return mr.GetMethodReference(session, new TypeReference[] { typeReference });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a method reference for a generic method.
|
||||
/// </summary>
|
||||
public static MethodReference GetMethodReference(this MethodReference mr, CodegenSession session, TypeReference[] typeReferences)
|
||||
{
|
||||
if (mr.HasGenericParameters)
|
||||
{
|
||||
if (typeReferences == null || typeReferences.Length == 0)
|
||||
{
|
||||
session.LogError($"Method {mr.Name} has generic parameters but TypeReferences are null or 0 length.");
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
GenericInstanceMethod gim = mr.MakeGenericMethod(typeReferences);
|
||||
return gim;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return mr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Resolve favoring cached results first.
|
||||
/// </summary>
|
||||
internal static MethodDefinition CachedResolve(this MethodReference methodRef, CodegenSession session)
|
||||
{
|
||||
return session.GetClass<GeneralHelper>().GetMethodReferenceResolve(methodRef);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a method of a generic class such as ArraySegment`T.get_Count,
|
||||
/// and a generic instance such as ArraySegment`int
|
||||
/// Creates a reference to the specialized method ArraySegment`int`.get_Count
|
||||
/// <para> Note that calling ArraySegment`T.get_Count directly gives an invalid IL error </para>
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
/// <param name="instanceType"></param>
|
||||
/// <returns></returns>
|
||||
public static MethodReference MakeHostInstanceGeneric(this MethodReference self, CodegenSession session, GenericInstanceType instanceType)
|
||||
{
|
||||
MethodReference reference = new MethodReference(self.Name, self.ReturnType, instanceType)
|
||||
{
|
||||
CallingConvention = self.CallingConvention,
|
||||
HasThis = self.HasThis,
|
||||
ExplicitThis = self.ExplicitThis
|
||||
};
|
||||
|
||||
foreach (ParameterDefinition parameter in self.Parameters)
|
||||
reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
|
||||
|
||||
foreach (GenericParameter generic_parameter in self.GenericParameters)
|
||||
reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference));
|
||||
|
||||
return session.ImportReference(reference);
|
||||
}
|
||||
/// <summary>
|
||||
/// Given a method of a generic class such as ArraySegment`T.get_Count,
|
||||
/// and a generic instance such as ArraySegment`int
|
||||
/// Creates a reference to the specialized method ArraySegment`int`.get_Count
|
||||
/// <para> Note that calling ArraySegment`T.get_Count directly gives an invalid IL error </para>
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
/// <param name="instanceType"></param>
|
||||
/// <returns></returns>
|
||||
public static MethodReference MakeHostInstanceGeneric(this MethodReference self, TypeReference typeRef, params TypeReference[] args)
|
||||
{
|
||||
GenericInstanceType git = typeRef.MakeGenericInstanceType(args);
|
||||
MethodReference reference = new MethodReference(self.Name, self.ReturnType, git)
|
||||
{
|
||||
CallingConvention = self.CallingConvention,
|
||||
HasThis = self.HasThis,
|
||||
ExplicitThis = self.ExplicitThis
|
||||
};
|
||||
|
||||
foreach (ParameterDefinition parameter in self.Parameters)
|
||||
reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
|
||||
|
||||
foreach (GenericParameter generic_parameter in self.GenericParameters)
|
||||
reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference));
|
||||
|
||||
return reference;
|
||||
}
|
||||
public static bool Is<T>(this MethodReference method, string name)
|
||||
{
|
||||
return method.DeclaringType.Is<T>() && method.Name == name;
|
||||
}
|
||||
public static bool Is<T>(this TypeReference td)
|
||||
{
|
||||
return Is(td, typeof(T));
|
||||
}
|
||||
|
||||
public static bool Is(this TypeReference td, Type t)
|
||||
{
|
||||
if (t.IsGenericType)
|
||||
{
|
||||
return td.GetElementType().FullName == t.FullName;
|
||||
}
|
||||
return td.FullName == t.FullName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee1c15a06ab386e439ec5aa41e3496f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
using FishNet.CodeGenerating.ILCore;
|
||||
using MonoFN.Cecil;
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping.Extension
|
||||
{
|
||||
|
||||
public static class ModuleDefinitionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a class within a module.
|
||||
/// </summary>
|
||||
/// <param name="moduleDef"></param>
|
||||
/// <returns></returns>
|
||||
public static TypeDefinition GetClass(this ModuleDefinition moduleDef, string className, string namespaceName = "")
|
||||
{
|
||||
if (namespaceName.Length == 0)
|
||||
namespaceName = FishNetILPP.RUNTIME_ASSEMBLY_NAME;
|
||||
|
||||
return moduleDef.GetType(namespaceName, className);
|
||||
}
|
||||
|
||||
public static MethodReference ImportReference(this ModuleDefinition moduleDef, Expression<Action> expression)
|
||||
{
|
||||
return ImportReference(moduleDef, (LambdaExpression)expression);
|
||||
}
|
||||
public static MethodReference ImportReference<T>(this ModuleDefinition module, Expression<Action<T>> expression)
|
||||
{
|
||||
return ImportReference(module, (LambdaExpression)expression);
|
||||
}
|
||||
|
||||
public static MethodReference ImportReference(this ModuleDefinition module, LambdaExpression expression)
|
||||
{
|
||||
if (expression.Body is MethodCallExpression outermostExpression)
|
||||
{
|
||||
MethodInfo methodInfo = outermostExpression.Method;
|
||||
return module.ImportReference(methodInfo);
|
||||
}
|
||||
|
||||
if (expression.Body is NewExpression newExpression)
|
||||
{
|
||||
ConstructorInfo methodInfo = newExpression.Constructor;
|
||||
// constructor is null when creating an ArraySegment<object>
|
||||
methodInfo = methodInfo ?? newExpression.Type.GetConstructors()[0];
|
||||
return module.ImportReference(methodInfo);
|
||||
}
|
||||
|
||||
if (expression.Body is MemberExpression memberExpression)
|
||||
{
|
||||
var property = memberExpression.Member as PropertyInfo;
|
||||
return module.ImportReference(property.GetMethod);
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid Expression {expression.Body.GetType()}");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42648785493390646898f5fa13e1dfd6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
using MonoFN.Cecil;
|
||||
using System;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping.Extension
|
||||
{
|
||||
|
||||
internal static class ParameterDefinitionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns if parameterDef is Type.
|
||||
/// </summary>
|
||||
/// <param name="parameterDef"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static bool Is(this ParameterDefinition parameterDef, Type type)
|
||||
{
|
||||
return parameterDef.ParameterType.FullName == type.FullName;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 31071055a2e388141b8f11e1ba4e147e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,500 @@
|
||||
using FishNet.CodeGenerating.Extension;
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping.Extension
|
||||
{
|
||||
|
||||
|
||||
internal static class TypeDefinitionExtensionsOld
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Creates a GenericInstanceType and adds parameters.
|
||||
/// </summary>
|
||||
internal static GenericInstanceType CreateGenericInstanceType(this TypeDefinition type, Collection<GenericParameter> parameters)
|
||||
{
|
||||
GenericInstanceType git = new GenericInstanceType(type);
|
||||
foreach (GenericParameter gp in parameters)
|
||||
git.GenericArguments.Add(gp);
|
||||
|
||||
return git;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds public fields in type and base type
|
||||
/// </summary>
|
||||
/// <param name="variable"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<FieldDefinition> FindAllPublicFields(this TypeDefinition typeDef, CodegenSession session
|
||||
, System.Type[] excludedBaseTypes = null, string[] excludedAssemblyPrefixes = null)
|
||||
{
|
||||
|
||||
GeneralHelper gh = session.GetClass<GeneralHelper>();
|
||||
while (typeDef != null)
|
||||
{
|
||||
if (IsExcluded(typeDef, excludedBaseTypes, excludedAssemblyPrefixes))
|
||||
break;
|
||||
|
||||
foreach (FieldDefinition fd in typeDef.Fields)
|
||||
{
|
||||
if (fd.IsStatic)
|
||||
continue;
|
||||
if (fd.IsNotSerialized)
|
||||
continue;
|
||||
if (gh.CodegenExclude(fd))
|
||||
continue;
|
||||
if (fd.IsPrivate)
|
||||
continue;
|
||||
|
||||
yield return fd;
|
||||
}
|
||||
|
||||
try { typeDef = typeDef.BaseType?.CachedResolve(session); }
|
||||
catch { break; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds public properties on typeDef and all base types which have a public get/set accessor.
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<PropertyDefinition> FindAllPublicProperties(this TypeDefinition typeDef, CodegenSession session
|
||||
, System.Type[] excludedBaseTypes = null, string[] excludedAssemblyPrefixes = null)
|
||||
{
|
||||
GeneralHelper gh = session.GetClass<GeneralHelper>();
|
||||
while (typeDef != null)
|
||||
{
|
||||
if (IsExcluded(typeDef, excludedBaseTypes, excludedAssemblyPrefixes))
|
||||
break;
|
||||
|
||||
foreach (PropertyDefinition pd in typeDef.Properties)
|
||||
{
|
||||
//Missing get or set method.
|
||||
if (pd.GetMethod == null || pd.SetMethod == null)
|
||||
continue;
|
||||
if (gh.CodegenExclude(pd))
|
||||
continue;
|
||||
if (pd.GetMethod.IsPrivate)
|
||||
continue;
|
||||
if (pd.SetMethod.IsPrivate)
|
||||
continue;
|
||||
if (pd.GetMethod.ReturnType.IsGenericParameter)
|
||||
continue;
|
||||
|
||||
yield return pd;
|
||||
}
|
||||
|
||||
try { typeDef = typeDef.BaseType?.CachedResolve(session); }
|
||||
catch { break; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if typeDef is excluded.
|
||||
/// </summary>
|
||||
private static bool IsExcluded(TypeDefinition typeDef, System.Type[] excludedBaseTypes = null, string[] excludedAssemblyPrefixes = null)
|
||||
{
|
||||
if (excludedBaseTypes != null)
|
||||
{
|
||||
foreach (System.Type t in excludedBaseTypes)
|
||||
{
|
||||
if (typeDef.FullName == t.FullName)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (excludedAssemblyPrefixes != null)
|
||||
{
|
||||
foreach (string s in excludedAssemblyPrefixes)
|
||||
{
|
||||
int len = s.Length;
|
||||
string tdAsmName = typeDef.Module.Assembly.FullName;
|
||||
if (tdAsmName.Length >= len && tdAsmName.Substring(0, len).ToLower() == s.ToLower())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//Fall through, not excluded.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns if typeDef is excluded.
|
||||
/// </summary>
|
||||
public static bool IsExcluded(this TypeDefinition typeDef, string excludedAssemblyPrefix)
|
||||
{
|
||||
|
||||
int len = excludedAssemblyPrefix.Length;
|
||||
string tdAsmName = typeDef.Module.Assembly.FullName;
|
||||
if (tdAsmName.Length >= len && tdAsmName.Substring(0, len).ToLower() == excludedAssemblyPrefix.ToLower())
|
||||
return true;
|
||||
|
||||
//Fall through, not excluded.
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if typeDef or any of it's parents inherit from NetworkBehaviour.
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool InheritsNetworkBehaviour(this TypeDefinition typeDef, CodegenSession session)
|
||||
{
|
||||
string nbFullName = session.GetClass<NetworkBehaviourHelper>().FullName;
|
||||
|
||||
TypeDefinition copyTd = typeDef;
|
||||
while (copyTd != null)
|
||||
{
|
||||
if (copyTd.FullName == nbFullName)
|
||||
return true;
|
||||
|
||||
copyTd = copyTd.GetNextBaseTypeDefinition(session);
|
||||
}
|
||||
|
||||
//Fall through, network behaviour not found.
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nested TypeDefinition of name.
|
||||
/// </summary>
|
||||
internal static TypeDefinition GetNestedType(this TypeDefinition typeDef, string name)
|
||||
{
|
||||
foreach (TypeDefinition nestedTd in typeDef.NestedTypes)
|
||||
{
|
||||
if (nestedTd.Name == name)
|
||||
return nestedTd;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the BaseType for TypeDef exist and is not NetworkBehaviour,
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool CanProcessBaseType(this TypeDefinition typeDef, CodegenSession session)
|
||||
{
|
||||
return (typeDef != null && typeDef.BaseType != null && typeDef.BaseType.FullName != session.GetClass<NetworkBehaviourHelper>().FullName);
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns if the BaseType for TypeDef exist and is not NetworkBehaviour,
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <returns></returns>
|
||||
internal static TypeDefinition GetNextBaseClassToProcess(this TypeDefinition typeDef, CodegenSession session)
|
||||
{
|
||||
if (typeDef.BaseType != null && typeDef.BaseType.FullName != session.GetClass<NetworkBehaviourHelper>().FullName)
|
||||
return typeDef.BaseType.CachedResolve(session);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static TypeDefinition GetLastBaseClass(this TypeDefinition typeDef, CodegenSession session)
|
||||
{
|
||||
TypeDefinition copyTd = typeDef;
|
||||
while (copyTd.BaseType != null)
|
||||
copyTd = copyTd.BaseType.CachedResolve(session);
|
||||
|
||||
return copyTd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for a type in current and inherited types.
|
||||
/// </summary>
|
||||
internal static TypeDefinition GetClassInInheritance(this TypeDefinition typeDef, CodegenSession session, string typeFullName)
|
||||
{
|
||||
TypeDefinition copyTd = typeDef;
|
||||
do
|
||||
{
|
||||
if (copyTd.FullName == typeFullName)
|
||||
return copyTd;
|
||||
|
||||
if (copyTd.BaseType != null)
|
||||
copyTd = copyTd.BaseType.CachedResolve(session);
|
||||
else
|
||||
copyTd = null;
|
||||
|
||||
} while (copyTd != null);
|
||||
|
||||
//Not found.
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for a type in current and inherited types.
|
||||
/// </summary>
|
||||
internal static TypeDefinition GetClassInInheritance(this TypeDefinition typeDef, CodegenSession session, TypeDefinition targetTypeDef)
|
||||
{
|
||||
if (typeDef == null)
|
||||
return null;
|
||||
|
||||
TypeDefinition copyTd = typeDef;
|
||||
do
|
||||
{
|
||||
if (copyTd == targetTypeDef)
|
||||
return copyTd;
|
||||
|
||||
if (copyTd.BaseType != null)
|
||||
copyTd = copyTd.BaseType.CachedResolve(session);
|
||||
else
|
||||
copyTd = null;
|
||||
|
||||
} while (copyTd != null);
|
||||
|
||||
//Not found.
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns if typeDef is static (abstract, sealed).
|
||||
/// </summary>
|
||||
internal static bool IsStatic(this TypeDefinition typeDef)
|
||||
{
|
||||
//Combining flags in a single check some reason doesn't work right with HasFlag.
|
||||
return (typeDef.Attributes.HasFlag(TypeAttributes.Abstract) && typeDef.Attributes.HasFlag(TypeAttributes.Sealed));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enum underlying type for typeDef.
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <returns></returns>
|
||||
internal static TypeReference GetEnumUnderlyingTypeReference(this TypeDefinition typeDef)
|
||||
{
|
||||
foreach (FieldDefinition field in typeDef.Fields)
|
||||
{
|
||||
if (!field.IsStatic)
|
||||
return field.FieldType;
|
||||
}
|
||||
throw new ArgumentException($"Invalid enum {typeDef.FullName}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if typeDef is derived from type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool InheritsFrom<T>(this TypeDefinition typeDef, CodegenSession session)
|
||||
{
|
||||
return typeDef.InheritsFrom(session, typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if typeDef is derived from type.
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool InheritsFrom(this TypeDefinition typeDef, CodegenSession session, Type type)
|
||||
{
|
||||
if (!typeDef.IsClass)
|
||||
return false;
|
||||
|
||||
TypeDefinition copyTd = typeDef;
|
||||
while (copyTd.BaseType != null)
|
||||
{
|
||||
if (copyTd.BaseType.IsType(type))
|
||||
return true;
|
||||
|
||||
copyTd = copyTd.GetNextBaseTypeDefinition(session);
|
||||
}
|
||||
|
||||
//Fall through.
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a method to typeDef.
|
||||
/// </summary>
|
||||
/// <param name="typDef"></param>
|
||||
/// <param name="methodName"></param>
|
||||
/// <param name="attributes"></param>
|
||||
/// <returns></returns>
|
||||
internal static MethodDefinition AddMethod(this TypeDefinition typDef, string methodName, MethodAttributes attributes)
|
||||
{
|
||||
return AddMethod(typDef, methodName, attributes, typDef.Module.ImportReference(typeof(void)));
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds a method to typeDef.
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <param name="methodName"></param>
|
||||
/// <param name="attributes"></param>
|
||||
/// <param name="typeReference"></param>
|
||||
/// <returns></returns>
|
||||
internal static MethodDefinition AddMethod(this TypeDefinition typeDef, string methodName, MethodAttributes attributes, TypeReference typeReference)
|
||||
{
|
||||
var method = new MethodDefinition(methodName, attributes, typeReference);
|
||||
typeDef.Methods.Add(method);
|
||||
return method;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if a type is a subclass of another.
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <param name="ClassTypeFullName"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool IsSubclassOf(this TypeDefinition typeDef,CodegenSession session, string ClassTypeFullName)
|
||||
{
|
||||
if (!typeDef.IsClass) return false;
|
||||
|
||||
TypeReference baseTypeRef = typeDef.BaseType;
|
||||
while (baseTypeRef != null)
|
||||
{
|
||||
if (baseTypeRef.FullName == ClassTypeFullName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
baseTypeRef = baseTypeRef.CachedResolve(session).BaseType;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a field reference by name.
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <param name="fieldName"></param>
|
||||
/// <returns></returns>
|
||||
public static FieldReference GetFieldReference(this TypeDefinition typeDef, string fieldName, CodegenSession session)
|
||||
{
|
||||
if (typeDef.HasFields)
|
||||
{
|
||||
for (int i = 0; i < typeDef.Fields.Count; i++)
|
||||
{
|
||||
if (typeDef.Fields[i].Name == fieldName)
|
||||
return session.ImportReference(typeDef.Fields[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the TypeDefinition implements TInterface.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInterface"></typeparam>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <returns></returns>
|
||||
public static bool ImplementsInterface<TInterface>(this TypeDefinition typeDef)
|
||||
{
|
||||
if (typeDef.Interfaces == null)
|
||||
return false;
|
||||
for (int i = 0; i < typeDef.Interfaces.Count; i++)
|
||||
{
|
||||
if (typeDef.Interfaces[i].InterfaceType.Is<TInterface>())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the TypeDefinition implements TInterface.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInterface"></typeparam>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <returns></returns>
|
||||
public static bool ImplementsInterface(this TypeDefinition typeDef, string interfaceName)
|
||||
{
|
||||
for (int i = 0; i < typeDef.Interfaces.Count; i++)
|
||||
{
|
||||
if (typeDef.Interfaces[i].InterfaceType.FullName == interfaceName)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the TypeDefinition implements TInterface.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInterface"></typeparam>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <returns></returns>
|
||||
public static bool ImplementsInterfaceRecursive<T>(this TypeDefinition typeDef, CodegenSession session)
|
||||
{
|
||||
TypeDefinition climbTypeDef = typeDef;
|
||||
|
||||
while (climbTypeDef != null)
|
||||
{
|
||||
if (climbTypeDef.Interfaces.Any(i => i.InterfaceType.Is<T>()))
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
if (climbTypeDef.BaseType != null)
|
||||
climbTypeDef = climbTypeDef.BaseType.CachedResolve(session);
|
||||
else
|
||||
climbTypeDef = null;
|
||||
}
|
||||
//Could not resolve assembly; can happen for assemblies being checked outside FishNet/csharp.
|
||||
catch (AssemblyResolutionException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the TypeDefinition implements TInterface.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInterface"></typeparam>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <returns></returns>
|
||||
public static bool ImplementsInterfaceRecursive(this TypeDefinition typeDef, CodegenSession session, string interfaceName)
|
||||
{
|
||||
TypeDefinition climbTypeDef = typeDef;
|
||||
|
||||
while (climbTypeDef != null)
|
||||
{
|
||||
if (climbTypeDef.Interfaces.Any(i => i.InterfaceType.FullName == interfaceName))
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
if (climbTypeDef.BaseType != null)
|
||||
climbTypeDef = climbTypeDef.BaseType.CachedResolve(session);
|
||||
else
|
||||
climbTypeDef = null;
|
||||
}
|
||||
//Could not resolve assembly; can happen for assemblies being checked outside FishNet/csharp.
|
||||
catch (AssemblyResolutionException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 645e49fe7eeff3a4e9eb65d77fc6e2ca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,138 @@
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Rocks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping.Extension
|
||||
{
|
||||
|
||||
internal static class TypeReferenceExtensionsOld
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Resolve favoring cached results first.
|
||||
/// </summary>
|
||||
internal static TypeDefinition CachedResolve(this TypeReference typeRef, CodegenSession session)
|
||||
{
|
||||
return session.GetClass<GeneralHelper>().GetTypeReferenceResolve(typeRef);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if typeRef is a class or struct.
|
||||
/// </summary>
|
||||
internal static bool IsClassOrStruct(this TypeReference typeRef, CodegenSession session)
|
||||
{
|
||||
TypeDefinition typeDef = typeRef.CachedResolve(session);
|
||||
return (!typeDef.IsPrimitive && !typeDef.IsEnum && (typeDef.IsClass || typeDef.IsValueType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all properties on typeRef and all base types which have a public get/set accessor.
|
||||
/// </summary>
|
||||
/// <param name="typeRef"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<PropertyDefinition> FindAllSerializableProperties(this TypeReference typeRef, CodegenSession session, System.Type[] excludedBaseTypes = null, string[] excludedAssemblyPrefixes = null)
|
||||
{
|
||||
return typeRef.CachedResolve(session).FindAllPublicProperties(session, excludedBaseTypes, excludedAssemblyPrefixes);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets all public fields in typeRef and base type.
|
||||
/// </summary>
|
||||
/// <param name="typeRef"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<FieldDefinition> FindAllSerializableFields(this TypeReference typeRef, CodegenSession session,
|
||||
System.Type[] excludedBaseTypes = null, string[] excludedAssemblyPrefixes = null)
|
||||
{
|
||||
return typeRef.Resolve().FindAllPublicFields(session, excludedBaseTypes, excludedAssemblyPrefixes);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns if a typeRef is type.
|
||||
/// </summary>
|
||||
/// <param name="typeRef"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsType(this TypeReference typeRef, Type type)
|
||||
{
|
||||
if (type.IsGenericType)
|
||||
return typeRef.GetElementType().FullName == type.FullName;
|
||||
else
|
||||
return typeRef.FullName == type.FullName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns if typeRef is a multidimensional array.
|
||||
/// </summary>
|
||||
/// <param name="typeRef"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsMultidimensionalArray(this TypeReference typeRef)
|
||||
{
|
||||
return typeRef is ArrayType arrayType && arrayType.Rank > 1;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns if typeRef can be resolved.
|
||||
/// </summary>
|
||||
/// <param name="typeRef"></param>
|
||||
/// <returns></returns>
|
||||
public static bool CanBeResolved(this TypeReference typeRef, CodegenSession session)
|
||||
{
|
||||
while (typeRef != null)
|
||||
{
|
||||
if (typeRef.Scope.Name == "Windows")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeRef.Scope.Name == "mscorlib")
|
||||
{
|
||||
TypeDefinition resolved = typeRef.CachedResolve(session);
|
||||
return resolved != null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
typeRef = typeRef.CachedResolve(session).BaseType;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a generic type out of another type, if needed.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static TypeReference ConvertToGenericIfNeeded(this TypeDefinition type)
|
||||
{
|
||||
if (type.HasGenericParameters)
|
||||
{
|
||||
// get all the generic parameters and make a generic instance out of it
|
||||
TypeReference[] genericTypes = new TypeReference[type.GenericParameters.Count];
|
||||
for (int i = 0; i < type.GenericParameters.Count; i++)
|
||||
{
|
||||
genericTypes[i] = type.GenericParameters[i].GetElementType();
|
||||
}
|
||||
|
||||
return type.MakeGenericInstanceType(genericTypes);
|
||||
}
|
||||
else
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2344f5ab0fda07b498c03fbe0e082c14
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1423
phr/StickGame/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs
Normal file
1423
phr/StickGame/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6139ff104f3c24442b26dbc4e40d5ce5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,471 @@
|
||||
using FishNet.CodeGenerating.Extension;
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using FishNet.CodeGenerating.Processing;
|
||||
using FishNet.Configuring;
|
||||
using FishNet.Managing.Logging;
|
||||
using FishNet.Object;
|
||||
using FishNet.Object.Delegating;
|
||||
using FishNet.Object.Helping;
|
||||
using FishNet.Object.Prediction.Delegating;
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Cil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
internal class NetworkBehaviourHelper : CodegenBase
|
||||
{
|
||||
#region Reflection references.
|
||||
//Names.
|
||||
internal string FullName;
|
||||
//Prediction.
|
||||
public string ClearReplicateCache_MethodName = nameof(NetworkBehaviour.ClearReplicateCache_Virtual);
|
||||
public MethodReference Replicate_NonOwner_MethodRef;
|
||||
public MethodReference Replicate_Owner_MethodRef;
|
||||
public MethodReference Replicate_Reader_MethodRef;
|
||||
public MethodReference Replicate_ExitEarly_A_MethodRef;
|
||||
public MethodReference Reconcile_ExitEarly_A_MethodRef;
|
||||
public MethodReference Reconcile_Server_MethodRef;
|
||||
public FieldReference UsesPrediction_FieldRef;
|
||||
public MethodReference Replicate_Replay_Start_MethodRef;
|
||||
public MethodReference Reconcile_Client_MethodRef;
|
||||
public MethodReference Replicate_Replay_MethodRef;
|
||||
public MethodReference Reconcile_Reader_MethodRef;
|
||||
public MethodReference RegisterReplicateRpc_MethodRef;
|
||||
public MethodReference RegisterReconcileRpc_MethodRef;
|
||||
public MethodReference ReplicateRpcDelegate_Ctor_MethodRef;
|
||||
public MethodReference ReconcileRpcDelegate_Ctor_MethodRef;
|
||||
//public MethodReference Replicate_Server_SendToSpectators_MethodRef;
|
||||
//RPCs.
|
||||
public MethodReference SendServerRpc_MethodRef;
|
||||
public MethodReference SendObserversRpc_MethodRef;
|
||||
public MethodReference SendTargetRpc_MethodRef;
|
||||
public MethodReference DirtySyncType_MethodRef;
|
||||
public MethodReference RegisterServerRpc_MethodRef;
|
||||
public MethodReference RegisterObserversRpc_MethodRef;
|
||||
public MethodReference RegisterTargetRpc_MethodRef;
|
||||
public MethodReference ServerRpcDelegate_Ctor_MethodRef;
|
||||
public MethodReference ClientRpcDelegate_Ctor_MethodRef;
|
||||
//Is checks.
|
||||
public MethodReference IsClientInitialized_MethodRef;
|
||||
public MethodReference IsOwner_MethodRef;
|
||||
public MethodReference IsServerInitialized_MethodRef;
|
||||
public MethodReference IsHost_MethodRef;
|
||||
public MethodReference IsNetworked_MethodRef;
|
||||
//Misc.
|
||||
public TypeReference TypeRef;
|
||||
public MethodReference OwnerMatches_MethodRef;
|
||||
public MethodReference LocalConnection_MethodRef;
|
||||
public MethodReference Owner_MethodRef;
|
||||
public MethodReference RegisterSyncVarRead_MethodRef;
|
||||
public MethodReference NetworkInitializeIfDisabled_MethodRef;
|
||||
//TimeManager.
|
||||
public MethodReference TimeManager_MethodRef;
|
||||
#endregion
|
||||
|
||||
#region Const.
|
||||
internal const uint MAX_SYNCTYPE_ALLOWANCE = byte.MaxValue;
|
||||
internal const uint MAX_RPC_ALLOWANCE = ushort.MaxValue;
|
||||
internal const uint MAX_PREDICTION_ALLOWANCE = byte.MaxValue;
|
||||
internal const string AWAKE_METHOD_NAME = "Awake";
|
||||
internal const string DISABLE_LOGGING_TEXT = "This message may be disabled by setting the Logging field in your attribute to LoggingType.Off";
|
||||
#endregion
|
||||
|
||||
public override bool ImportReferences()
|
||||
{
|
||||
Type networkBehaviourType = typeof(NetworkBehaviour);
|
||||
TypeRef = base.ImportReference(networkBehaviourType);
|
||||
FullName = networkBehaviourType.FullName;
|
||||
base.ImportReference(networkBehaviourType);
|
||||
|
||||
//ServerRpcDelegate and ClientRpcDelegate constructors.
|
||||
ServerRpcDelegate_Ctor_MethodRef = base.ImportReference(typeof(ServerRpcDelegate).GetConstructors().First());
|
||||
ClientRpcDelegate_Ctor_MethodRef = base.ImportReference(typeof(ClientRpcDelegate).GetConstructors().First());
|
||||
//Prediction Rpc delegate constructors.
|
||||
ReplicateRpcDelegate_Ctor_MethodRef = base.ImportReference(typeof(ReplicateRpcDelegate).GetConstructors().First());
|
||||
ReconcileRpcDelegate_Ctor_MethodRef = base.ImportReference(typeof(ReconcileRpcDelegate).GetConstructors().First());
|
||||
|
||||
foreach (MethodInfo mi in networkBehaviourType.GetMethods((BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic)))
|
||||
{
|
||||
//CreateDelegates.
|
||||
if (mi.Name == nameof(NetworkBehaviour.RegisterServerRpc))
|
||||
RegisterServerRpc_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.RegisterObserversRpc))
|
||||
RegisterObserversRpc_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.RegisterTargetRpc))
|
||||
RegisterTargetRpc_MethodRef = base.ImportReference(mi);
|
||||
//Prediction delegates.
|
||||
else if (mi.Name == nameof(NetworkBehaviour.RegisterReplicateRpc))
|
||||
RegisterReplicateRpc_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.RegisterReconcileRpc))
|
||||
RegisterReconcileRpc_MethodRef = base.ImportReference(mi);
|
||||
//SendRpcs.
|
||||
else if (mi.Name == nameof(NetworkBehaviour.SendServerRpc))
|
||||
SendServerRpc_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.SendObserversRpc))
|
||||
SendObserversRpc_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.SendTargetRpc))
|
||||
SendTargetRpc_MethodRef = base.ImportReference(mi);
|
||||
//Prediction.
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Replicate_ExitEarly_A))
|
||||
Replicate_ExitEarly_A_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Replicate_NonOwner))
|
||||
Replicate_NonOwner_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Replicate_Reader))
|
||||
Replicate_Reader_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Reader))
|
||||
Reconcile_Reader_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Reconcile_ExitEarly_A))
|
||||
Reconcile_ExitEarly_A_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Server))
|
||||
Reconcile_Server_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Replicate_Owner))
|
||||
Replicate_Owner_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Client))
|
||||
Reconcile_Client_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.OwnerMatches))
|
||||
OwnerMatches_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.RegisterSyncVarRead))
|
||||
RegisterSyncVarRead_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.DirtySyncType))
|
||||
DirtySyncType_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.NetworkInitializeIfDisabled))
|
||||
NetworkInitializeIfDisabled_MethodRef = base.ImportReference(mi);
|
||||
}
|
||||
|
||||
foreach (PropertyInfo pi in networkBehaviourType.GetProperties((BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic)))
|
||||
{
|
||||
//Server/Client states.
|
||||
if (pi.Name == nameof(NetworkBehaviour.IsClientInitialized))
|
||||
IsClientInitialized_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
else if (pi.Name == nameof(NetworkBehaviour.IsServerInitialized))
|
||||
IsServerInitialized_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
else if (pi.Name == nameof(NetworkBehaviour.IsHost))
|
||||
IsHost_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
else if (pi.Name == nameof(NetworkBehaviour.IsOwner))
|
||||
IsOwner_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
else if (pi.Name == nameof(NetworkBehaviour.IsNetworked))
|
||||
IsNetworked_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
//Owner.
|
||||
else if (pi.Name == nameof(NetworkBehaviour.Owner))
|
||||
Owner_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
else if (pi.Name == nameof(NetworkBehaviour.LocalConnection))
|
||||
LocalConnection_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
//Misc.
|
||||
else if (pi.Name == nameof(NetworkBehaviour.TimeManager))
|
||||
TimeManager_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returnsthe child most Awake by iterating up childMostTypeDef.
|
||||
/// </summary>
|
||||
/// <param name="childMostTypeDef"></param>
|
||||
/// <param name="created"></param>
|
||||
/// <returns></returns>
|
||||
internal MethodDefinition GetAwakeMethodDefinition(TypeDefinition typeDef)
|
||||
{
|
||||
return typeDef.GetMethod(AWAKE_METHOD_NAME);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a replicate delegate.
|
||||
/// </summary>
|
||||
/// <param name="processor"></param>
|
||||
/// <param name="originalMethodDef"></param>
|
||||
/// <param name="readerMethodDef"></param>
|
||||
/// <param name="rpcType"></param>
|
||||
internal void CreateReplicateDelegate(MethodDefinition originalMethodDef, MethodDefinition readerMethodDef, uint methodHash)
|
||||
{
|
||||
MethodDefinition methodDef = originalMethodDef.DeclaringType.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
|
||||
ILProcessor processor = methodDef.Body.GetILProcessor();
|
||||
|
||||
List<Instruction> insts = new List<Instruction>();
|
||||
insts.Add(processor.Create(OpCodes.Ldarg_0));
|
||||
|
||||
insts.Add(processor.Create(OpCodes.Ldc_I4, (int)methodHash));
|
||||
|
||||
/* Create delegate and call NetworkBehaviour method. */
|
||||
insts.Add(processor.Create(OpCodes.Ldnull));
|
||||
insts.Add(processor.Create(OpCodes.Ldftn, readerMethodDef));
|
||||
|
||||
/* Has to be done last. This allows the NetworkBehaviour to
|
||||
* initialize it's fields first. */
|
||||
processor.InsertLast(insts);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a RPC delegate for rpcType.
|
||||
/// </summary>
|
||||
/// <param name="processor"></param>
|
||||
/// <param name="originalMethodDef"></param>
|
||||
/// <param name="readerMethodDef"></param>
|
||||
/// <param name="rpcType"></param>
|
||||
internal void CreateRpcDelegate(bool runLocally, TypeDefinition typeDef, MethodDefinition readerMethodDef, RpcType rpcType, uint methodHash, CustomAttribute rpcAttribute)
|
||||
{
|
||||
|
||||
|
||||
MethodDefinition methodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
|
||||
ILProcessor processor = methodDef.Body.GetILProcessor();
|
||||
|
||||
List<Instruction> insts = new List<Instruction>();
|
||||
insts.Add(processor.Create(OpCodes.Ldarg_0));
|
||||
insts.Add(processor.Create(OpCodes.Ldc_I4, (int)methodHash));
|
||||
|
||||
/* Create delegate and call NetworkBehaviour method. */
|
||||
insts.Add(processor.Create(OpCodes.Ldarg_0));
|
||||
insts.Add(processor.Create(OpCodes.Ldftn, readerMethodDef));
|
||||
//Server.
|
||||
if (rpcType == RpcType.Server)
|
||||
{
|
||||
insts.Add(processor.Create(OpCodes.Newobj, ServerRpcDelegate_Ctor_MethodRef));
|
||||
insts.Add(processor.Create(OpCodes.Call, RegisterServerRpc_MethodRef));
|
||||
}
|
||||
//Observers.
|
||||
else if (rpcType == RpcType.Observers)
|
||||
{
|
||||
insts.Add(processor.Create(OpCodes.Newobj, ClientRpcDelegate_Ctor_MethodRef));
|
||||
insts.Add(processor.Create(OpCodes.Call, RegisterObserversRpc_MethodRef));
|
||||
}
|
||||
//Target
|
||||
else if (rpcType == RpcType.Target)
|
||||
{
|
||||
insts.Add(processor.Create(OpCodes.Newobj, ClientRpcDelegate_Ctor_MethodRef));
|
||||
insts.Add(processor.Create(OpCodes.Call, RegisterTargetRpc_MethodRef));
|
||||
}
|
||||
|
||||
/* Has to be done last. This allows the NetworkBehaviour to
|
||||
* initialize it's fields first. */
|
||||
processor.InsertLast(insts);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates exit method condition if local client is not owner.
|
||||
/// </summary>
|
||||
/// <param name="retIfOwner">True if to ret when owner, false to ret when not owner.</param>
|
||||
/// <returns>Returns Ret instruction.</returns>
|
||||
internal Instruction CreateLocalClientIsOwnerCheck(MethodDefinition methodDef, LoggingType loggingType, bool notifyMessageCanBeDisabled, bool retIfOwner, bool insertFirst)
|
||||
{
|
||||
List<Instruction> instructions = new List<Instruction>();
|
||||
/* This is placed after the if check.
|
||||
* Should the if check pass then code
|
||||
* jumps to this instruction. */
|
||||
ILProcessor processor = methodDef.Body.GetILProcessor();
|
||||
Instruction endIf = processor.Create(OpCodes.Nop);
|
||||
|
||||
instructions.Add(processor.Create(OpCodes.Ldarg_0)); //argument: this
|
||||
//If !base.IsOwner endIf.
|
||||
instructions.Add(processor.Create(OpCodes.Call, IsOwner_MethodRef));
|
||||
if (retIfOwner)
|
||||
instructions.Add(processor.Create(OpCodes.Brfalse, endIf));
|
||||
else
|
||||
instructions.Add(processor.Create(OpCodes.Brtrue, endIf));
|
||||
//If logging is not disabled.
|
||||
if (loggingType != LoggingType.Off)
|
||||
{
|
||||
string disableLoggingText = (notifyMessageCanBeDisabled) ? DISABLE_LOGGING_TEXT : string.Empty;
|
||||
string msg = (retIfOwner) ?
|
||||
$"Cannot complete action because you are the owner of this object. {disableLoggingText}." :
|
||||
$"Cannot complete action because you are not the owner of this object. {disableLoggingText}.";
|
||||
|
||||
instructions.AddRange(base.GetClass<GeneralHelper>().LogMessage(methodDef, msg, loggingType));
|
||||
}
|
||||
//Return block.
|
||||
Instruction retInst = processor.Create(OpCodes.Ret);
|
||||
instructions.Add(retInst);
|
||||
//After if statement, jumped to when successful check.
|
||||
instructions.Add(endIf);
|
||||
|
||||
if (insertFirst)
|
||||
{
|
||||
processor.InsertFirst(instructions);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Instruction inst in instructions)
|
||||
processor.Append(inst);
|
||||
}
|
||||
|
||||
return retInst;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates exit method condition if remote client is not owner.
|
||||
/// </summary>
|
||||
/// <param name="processor"></param>
|
||||
internal Instruction CreateRemoteClientIsOwnerCheck(ILProcessor processor, ParameterDefinition connectionParameterDef)
|
||||
{
|
||||
/* This is placed after the if check.
|
||||
* Should the if check pass then code
|
||||
* jumps to this instruction. */
|
||||
Instruction endIf = processor.Create(OpCodes.Nop);
|
||||
|
||||
processor.Emit(OpCodes.Ldarg_0); //argument: this
|
||||
//If !base.IsOwner endIf.
|
||||
processor.Emit(OpCodes.Ldarg, connectionParameterDef);
|
||||
processor.Emit(OpCodes.Call, OwnerMatches_MethodRef);
|
||||
processor.Emit(OpCodes.Brtrue, endIf);
|
||||
//Return block.
|
||||
Instruction retInst = processor.Create(OpCodes.Ret);
|
||||
processor.Append(retInst);
|
||||
|
||||
//After if statement, jumped to when successful check.
|
||||
processor.Append(endIf);
|
||||
|
||||
return retInst;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates exit method condition if not client.
|
||||
/// </summary>
|
||||
/// <param name="useStatic">When true InstanceFinder.IsClient is used, when false base.IsClientInitialized is used.</param>
|
||||
internal void CreateIsClientCheck(MethodDefinition methodDef, LoggingType loggingType, bool useStatic, bool insertFirst, bool checkIsNetworked)
|
||||
{
|
||||
/* This is placed after the if check.
|
||||
* Should the if check pass then code
|
||||
* jumps to this instruction. */
|
||||
ILProcessor processor = methodDef.Body.GetILProcessor();
|
||||
Instruction endIf = processor.Create(OpCodes.Nop);
|
||||
|
||||
List<Instruction> instructions = new List<Instruction>();
|
||||
|
||||
if (checkIsNetworked)
|
||||
instructions.AddRange(CreateIsNetworkedCheck(methodDef, endIf));
|
||||
|
||||
//Checking against the NetworkObject.
|
||||
if (!useStatic)
|
||||
{
|
||||
instructions.Add(processor.Create(OpCodes.Ldarg_0)); //argument: this
|
||||
//If (!base.IsClient)
|
||||
instructions.Add(processor.Create(OpCodes.Call, IsClientInitialized_MethodRef));
|
||||
}
|
||||
//Checking instanceFinder.
|
||||
else
|
||||
{
|
||||
instructions.Add(processor.Create(OpCodes.Call, base.GetClass<ObjectHelper>().InstanceFinder_IsClient_MethodRef));
|
||||
}
|
||||
instructions.Add(processor.Create(OpCodes.Brtrue, endIf));
|
||||
//If warning then also append warning text.
|
||||
if (loggingType != LoggingType.Off)
|
||||
{
|
||||
string msg = $"Cannot complete action because client is not active. This may also occur if the object is not yet initialized, has deinitialized, or if it does not contain a NetworkObject component.";
|
||||
instructions.AddRange(base.GetClass<GeneralHelper>().LogMessage(methodDef, msg, loggingType));
|
||||
}
|
||||
//Add return.
|
||||
instructions.AddRange(CreateRetDefault(methodDef));
|
||||
//After if statement, jumped to when successful check.
|
||||
instructions.Add(endIf);
|
||||
|
||||
if (insertFirst)
|
||||
{
|
||||
processor.InsertFirst(instructions);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Instruction inst in instructions)
|
||||
processor.Append(inst);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates exit method condition if not server.
|
||||
/// </summary>
|
||||
/// <param name="useStatic">When true InstanceFinder.IsServer is used, when false base.IsServerInitialized is used.</param>
|
||||
internal void CreateIsServerCheck(MethodDefinition methodDef, LoggingType loggingType, bool useStatic, bool insertFirst, bool checkIsNetworked)
|
||||
{
|
||||
/* This is placed after the if check.
|
||||
* Should the if check pass then code
|
||||
* jumps to this instruction. */
|
||||
ILProcessor processor = methodDef.Body.GetILProcessor();
|
||||
Instruction endIf = processor.Create(OpCodes.Nop);
|
||||
|
||||
List<Instruction> instructions = new List<Instruction>();
|
||||
|
||||
if (checkIsNetworked)
|
||||
instructions.AddRange(CreateIsNetworkedCheck(methodDef, endIf));
|
||||
|
||||
if (!useStatic)
|
||||
{
|
||||
instructions.Add(processor.Create(OpCodes.Ldarg_0)); //argument: this
|
||||
//If (!base.IsServer)
|
||||
instructions.Add(processor.Create(OpCodes.Call, IsServerInitialized_MethodRef));
|
||||
}
|
||||
//Checking instanceFinder.
|
||||
else
|
||||
{
|
||||
instructions.Add(processor.Create(OpCodes.Call, base.GetClass<ObjectHelper>().InstanceFinder_IsServer_MethodRef));
|
||||
}
|
||||
instructions.Add(processor.Create(OpCodes.Brtrue, endIf));
|
||||
//If warning then also append warning text.
|
||||
if (loggingType != LoggingType.Off)
|
||||
{
|
||||
string msg = $"Cannot complete action because server is not active. This may also occur if the object is not yet initialized, has deinitialized, or if it does not contain a NetworkObject component.";
|
||||
instructions.AddRange(base.GetClass<GeneralHelper>().LogMessage(methodDef, msg, loggingType));
|
||||
}
|
||||
//Add return.
|
||||
instructions.AddRange(CreateRetDefault(methodDef));
|
||||
//After if statement, jumped to when successful check.
|
||||
instructions.Add(endIf);
|
||||
|
||||
if (insertFirst)
|
||||
{
|
||||
processor.InsertFirst(instructions);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Instruction inst in instructions)
|
||||
processor.Append(inst);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a call to base.IsNetworked and returns instructions.
|
||||
/// </summary>
|
||||
private List<Instruction> CreateIsNetworkedCheck(MethodDefinition methodDef, Instruction endIfInst)
|
||||
{
|
||||
List<Instruction> insts = new List<Instruction>();
|
||||
ILProcessor processor = methodDef.Body.GetILProcessor();
|
||||
insts.Add(processor.Create(OpCodes.Ldarg_0));
|
||||
insts.Add(processor.Create(OpCodes.Call, base.GetClass<NetworkBehaviourHelper>().IsNetworked_MethodRef));
|
||||
insts.Add(processor.Create(OpCodes.Brfalse, endIfInst));
|
||||
|
||||
return insts;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a return using the ReturnType for methodDef.
|
||||
/// </summary>
|
||||
/// <param name="processor"></param>
|
||||
/// <param name="methodDef"></param>
|
||||
/// <returns></returns>
|
||||
public List<Instruction> CreateRetDefault(MethodDefinition methodDef, ModuleDefinition importReturnModule = null)
|
||||
{
|
||||
ILProcessor processor = methodDef.Body.GetILProcessor();
|
||||
List<Instruction> instructions = new List<Instruction>();
|
||||
//If requires a value return.
|
||||
if (methodDef.ReturnType != methodDef.Module.TypeSystem.Void)
|
||||
{
|
||||
//Import type first.
|
||||
methodDef.Module.ImportReference(methodDef.ReturnType);
|
||||
if (importReturnModule != null)
|
||||
importReturnModule.ImportReference(methodDef.ReturnType);
|
||||
VariableDefinition vd = base.GetClass<GeneralHelper>().CreateVariable(methodDef, methodDef.ReturnType);
|
||||
instructions.Add(processor.Create(OpCodes.Ldloca_S, vd));
|
||||
instructions.Add(processor.Create(OpCodes.Initobj, vd.VariableType));
|
||||
instructions.Add(processor.Create(OpCodes.Ldloc, vd));
|
||||
}
|
||||
instructions.Add(processor.Create(OpCodes.Ret));
|
||||
|
||||
return instructions;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c42e06349d6890459a155a2afe17d41
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
using FishNet.Connection;
|
||||
using MonoFN.Cecil;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
internal class NetworkConnectionImports : CodegenBase
|
||||
{
|
||||
#region Reflection references.
|
||||
//Names.
|
||||
internal string FullName;
|
||||
public MethodReference IsLocalClient_Get_MethodRef;
|
||||
#endregion
|
||||
|
||||
#region Const.
|
||||
internal const uint MAX_RPC_ALLOWANCE = ushort.MaxValue;
|
||||
internal const string AWAKE_METHOD_NAME = "Awake";
|
||||
internal const string DISABLE_LOGGING_TEXT = "This message may be disabled by setting the Logging field in your attribute to LoggingType.Off";
|
||||
#endregion
|
||||
|
||||
public override bool ImportReferences()
|
||||
{
|
||||
Type type = typeof(NetworkConnection);
|
||||
base.ImportReference(type);
|
||||
|
||||
FullName = type.FullName;
|
||||
|
||||
foreach (PropertyInfo pi in type.GetProperties())
|
||||
{
|
||||
if (pi.Name == nameof(NetworkConnection.IsLocalClient))
|
||||
{
|
||||
IsLocalClient_Get_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d061dda8ed87ed48a08020942ad63f6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,100 @@
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using FishNet.Connection;
|
||||
using FishNet.Object;
|
||||
using FishNet.Object.Synchronizing;
|
||||
using FishNet.Object.Synchronizing.Internal;
|
||||
using MonoFN.Cecil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
internal class ObjectHelper : CodegenBase
|
||||
{
|
||||
#region Reflection references.
|
||||
//Fullnames.
|
||||
public string SyncVar_Name { get; private set; }
|
||||
public string SyncList_Name { get; private set; }
|
||||
public string SyncDictionary_Name { get; private set; }
|
||||
public string SyncHashSet_Name { get; private set; }
|
||||
//Is checks.
|
||||
public MethodReference InstanceFinder_IsServer_MethodRef { get; private set; }
|
||||
public MethodReference InstanceFinder_IsClient_MethodRef { get; private set; }
|
||||
//Misc.
|
||||
public TypeReference NetworkBehaviour_TypeRef { get; private set; }
|
||||
public MethodReference NetworkConnection_IsValid_MethodRef { get; private set; }
|
||||
public MethodReference NetworkConnection_IsActive_MethodRef { get; private set; }
|
||||
public MethodReference Dictionary_Add_UShort_SyncBase_MethodRef { get; private set; }
|
||||
public MethodReference NetworkConnection_GetIsLocalClient_MethodRef { get; private set; }
|
||||
#endregion
|
||||
|
||||
public override bool ImportReferences()
|
||||
{
|
||||
Type tmpType;
|
||||
/* SyncVar names. */
|
||||
//SyncVar.
|
||||
tmpType = typeof(SyncVar<>);
|
||||
base.ImportReference(tmpType);
|
||||
SyncVar_Name = tmpType.Name;
|
||||
/* SyncObject names. */
|
||||
//SyncList.
|
||||
tmpType = typeof(SyncList<>);
|
||||
base.ImportReference(tmpType);
|
||||
SyncList_Name = tmpType.Name;
|
||||
//SyncDictionary.
|
||||
tmpType = typeof(SyncDictionary<,>);
|
||||
base.ImportReference(tmpType);
|
||||
SyncDictionary_Name = tmpType.Name;
|
||||
//SyncHashSet.
|
||||
tmpType = typeof(SyncHashSet<>);
|
||||
base.ImportReference(tmpType);
|
||||
SyncHashSet_Name = tmpType.Name;
|
||||
|
||||
NetworkBehaviour_TypeRef = base.ImportReference(typeof(NetworkBehaviour));
|
||||
|
||||
tmpType = typeof(NetworkConnection);
|
||||
TypeReference networkConnectionTr = base.ImportReference(tmpType);
|
||||
foreach (PropertyDefinition item in networkConnectionTr.CachedResolve(base.Session).Properties)
|
||||
{
|
||||
if (item.Name == nameof(NetworkConnection.IsLocalClient))
|
||||
NetworkConnection_GetIsLocalClient_MethodRef = base.ImportReference(item.GetMethod);
|
||||
}
|
||||
|
||||
//Dictionary.Add(ushort, SyncBase).
|
||||
Type dictType = typeof(Dictionary<ushort, SyncBase>);
|
||||
TypeReference dictTypeRef = base.ImportReference(dictType);
|
||||
//Dictionary_Add_UShort_SyncBase_MethodRef = dictTypeRef.CachedResolve(base.Session).GetMethod("add_Item", )
|
||||
foreach (MethodDefinition item in dictTypeRef.CachedResolve(base.Session).Methods)
|
||||
{
|
||||
if (item.Name == nameof(Dictionary<ushort, SyncBase>.Add))
|
||||
{
|
||||
Dictionary_Add_UShort_SyncBase_MethodRef = base.ImportReference(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//InstanceFinder infos.
|
||||
Type instanceFinderType = typeof(InstanceFinder);
|
||||
foreach (PropertyInfo pi in instanceFinderType.GetProperties())
|
||||
{
|
||||
if (pi.Name == nameof(InstanceFinder.IsClient))
|
||||
InstanceFinder_IsClient_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
else if (pi.Name == nameof(InstanceFinder.IsServer))
|
||||
InstanceFinder_IsServer_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
}
|
||||
|
||||
//NetworkConnection.
|
||||
foreach (PropertyInfo pi in typeof(NetworkConnection).GetProperties((BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic)))
|
||||
{
|
||||
if (pi.Name == nameof(NetworkConnection.IsValid))
|
||||
NetworkConnection_IsValid_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
else if (pi.Name == nameof(NetworkConnection.IsActive))
|
||||
NetworkConnection_IsActive_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 033da35314653aa4689b4582e7929295
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,117 @@
|
||||
using FishNet.CodeGenerating.Extension;
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using FishNet.Connection;
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Cil;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using SR = System.Reflection;
|
||||
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
internal class PhysicsHelper : CodegenBase
|
||||
{
|
||||
#region Reflection references.
|
||||
public MethodReference GetScene_MethodRef;
|
||||
public MethodReference GetPhysicsScene2D_MethodRef;
|
||||
public MethodReference GetPhysicsScene3D_MethodRef;
|
||||
public MethodReference Physics3D_Simulate_MethodRef;
|
||||
public MethodReference Physics2D_Simulate_MethodRef;
|
||||
public MethodReference Physics3D_SyncTransforms_MethodRef;
|
||||
public MethodReference Physics2D_SyncTransforms_MethodRef;
|
||||
#endregion
|
||||
|
||||
public override bool ImportReferences()
|
||||
{
|
||||
SR.MethodInfo locMi;
|
||||
//GetScene.
|
||||
locMi = typeof(GameObject).GetMethod("get_scene");
|
||||
GetScene_MethodRef = base.ImportReference(locMi);
|
||||
|
||||
//Physics.SyncTransform.
|
||||
foreach (SR.MethodInfo mi in typeof(Physics).GetMethods())
|
||||
{
|
||||
if (mi.Name == nameof(Physics.SyncTransforms))
|
||||
{
|
||||
Physics3D_SyncTransforms_MethodRef = base.ImportReference(mi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach (SR.MethodInfo mi in typeof(Physics2D).GetMethods())
|
||||
{
|
||||
if (mi.Name == nameof(Physics2D.SyncTransforms))
|
||||
{
|
||||
Physics2D_SyncTransforms_MethodRef = base.ImportReference(mi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//PhysicsScene.Simulate.
|
||||
foreach (SR.MethodInfo mi in typeof(PhysicsScene).GetMethods())
|
||||
{
|
||||
if (mi.Name == nameof(PhysicsScene.Simulate))
|
||||
{
|
||||
Physics3D_Simulate_MethodRef = base.ImportReference(mi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach (SR.MethodInfo mi in typeof(PhysicsScene2D).GetMethods())
|
||||
{
|
||||
if (mi.Name == nameof(PhysicsScene2D.Simulate))
|
||||
{
|
||||
Physics2D_Simulate_MethodRef = base.ImportReference(mi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//GetPhysicsScene.
|
||||
foreach (SR.MethodInfo mi in typeof(PhysicsSceneExtensions).GetMethods())
|
||||
{
|
||||
if (mi.Name == nameof(PhysicsSceneExtensions.GetPhysicsScene))
|
||||
{
|
||||
GetPhysicsScene3D_MethodRef = base.ImportReference(mi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach (SR.MethodInfo mi in typeof(PhysicsSceneExtensions2D).GetMethods())
|
||||
{
|
||||
if (mi.Name == nameof(PhysicsSceneExtensions2D.GetPhysicsScene2D))
|
||||
{
|
||||
GetPhysicsScene2D_MethodRef = base.ImportReference(mi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns instructions to get a physics scene from a gameObject.
|
||||
/// </summary>
|
||||
public List<Instruction> GetPhysicsScene(MethodDefinition md, VariableDefinition gameObjectVd, bool threeDimensional)
|
||||
{
|
||||
ILProcessor processor = md.Body.GetILProcessor();
|
||||
return GetPhysicsScene(processor, gameObjectVd, threeDimensional);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns instructions to get a physics scene from a gameObject.
|
||||
/// </summary>
|
||||
public List<Instruction> GetPhysicsScene(ILProcessor processor, VariableDefinition gameObjectVd, bool threeDimensional)
|
||||
{
|
||||
List<Instruction> insts = new List<Instruction>();
|
||||
|
||||
//gameObject.scene.GetPhysics...
|
||||
insts.Add(processor.Create(OpCodes.Ldloc, gameObjectVd));
|
||||
insts.Add(processor.Create(GetScene_MethodRef.GetCallOpCode(base.Session), GetScene_MethodRef));
|
||||
if (threeDimensional)
|
||||
insts.Add(processor.Create(OpCodes.Call, GetPhysicsScene3D_MethodRef));
|
||||
else
|
||||
insts.Add(processor.Create(OpCodes.Call, GetPhysicsScene2D_MethodRef));
|
||||
|
||||
return insts;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 28ae27f7bc8e89547a606262508fdd36
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
using FishNet.Component.Prediction;
|
||||
using MonoFN.Cecil;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
internal class PredictedObjectHelper : CodegenBase
|
||||
{
|
||||
public override bool ImportReferences()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9a06c812bf785a44a38a5852ff866d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,71 @@
|
||||
using FishNet.Connection;
|
||||
using FishNet.Serializing;
|
||||
using MonoFN.Cecil;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
internal class ReaderImports : CodegenBase
|
||||
{
|
||||
#region Reflection references.
|
||||
public TypeReference PooledReader_TypeRef;
|
||||
public TypeReference Reader_TypeRef;
|
||||
public TypeReference NetworkConnection_TypeRef;
|
||||
public MethodReference PooledReader_ReadNetworkBehaviour_MethodRef;
|
||||
public MethodReference Reader_ReadPackedWhole_MethodRef;
|
||||
public MethodReference Reader_ReadDictionary_MethodRef;
|
||||
public MethodReference Reader_ReadList_MethodRef;
|
||||
public MethodReference Reader_ReadListCache_MethodRef;
|
||||
public MethodReference Reader_ReadArray_MethodRef;
|
||||
public TypeReference GenericReaderTypeRef;
|
||||
public TypeReference ReaderTypeRef;
|
||||
public MethodReference ReadSetMethodRef;
|
||||
public MethodReference ReadAutoPackSetMethodRef;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Imports references needed by this helper.
|
||||
/// </summary>
|
||||
/// <param name="moduleDef"></param>
|
||||
/// <returns></returns>
|
||||
public override bool ImportReferences()
|
||||
{
|
||||
ReaderProcessor rp = base.GetClass<ReaderProcessor>();
|
||||
|
||||
PooledReader_TypeRef = base.ImportReference(typeof(PooledReader));
|
||||
Reader_TypeRef = base.ImportReference(typeof(Reader));
|
||||
NetworkConnection_TypeRef = base.ImportReference(typeof(NetworkConnection));
|
||||
|
||||
GenericReaderTypeRef = base.ImportReference(typeof(GenericReader<>));
|
||||
ReaderTypeRef = base.ImportReference(typeof(Reader));
|
||||
|
||||
System.Reflection.PropertyInfo readPropertyInfo;
|
||||
readPropertyInfo = typeof(GenericReader<>).GetProperty(nameof(GenericReader<int>.Read));
|
||||
ReadSetMethodRef = base.ImportReference(readPropertyInfo.GetSetMethod());
|
||||
readPropertyInfo = typeof(GenericReader<>).GetProperty(nameof(GenericReader<int>.ReadAutoPack));
|
||||
ReadAutoPackSetMethodRef = base.ImportReference(readPropertyInfo.GetSetMethod());
|
||||
|
||||
|
||||
Type pooledReaderType = typeof(PooledReader);
|
||||
foreach (MethodInfo methodInfo in pooledReaderType.GetMethods())
|
||||
{
|
||||
int parameterCount = methodInfo.GetParameters().Length;
|
||||
/* Special methods. */
|
||||
if (methodInfo.Name == nameof(PooledReader.ReadPackedWhole))
|
||||
Reader_ReadPackedWhole_MethodRef = base.ImportReference(methodInfo);
|
||||
//Relay readers.
|
||||
else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadDictionary))
|
||||
Reader_ReadDictionary_MethodRef = base.ImportReference(methodInfo);
|
||||
else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadListAllocated))
|
||||
Reader_ReadList_MethodRef = base.ImportReference(methodInfo);
|
||||
else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadListCacheAllocated))
|
||||
Reader_ReadListCache_MethodRef = base.ImportReference(methodInfo);
|
||||
else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadArrayAllocated))
|
||||
Reader_ReadArray_MethodRef = base.ImportReference(methodInfo);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 350861dcbcbabc447acd37e4310b0697
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,22 @@
|
||||
using FishNet.Managing.Timing;
|
||||
using MonoFN.Cecil;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
|
||||
internal class TimeManagerHelper : CodegenBase
|
||||
{
|
||||
|
||||
#region Reflection references.
|
||||
#endregion
|
||||
|
||||
public override bool ImportReferences()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11dbcc0798e079e4a85fe98dfc9fe23a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,36 @@
|
||||
using FishNet.Transporting;
|
||||
using MonoFN.Cecil;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
internal class TransportHelper : CodegenBase
|
||||
{
|
||||
#region Reflection references.
|
||||
internal TypeReference Channel_TypeRef;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Resets cached values.
|
||||
/// </summary>
|
||||
private void ResetValues()
|
||||
{
|
||||
Channel_TypeRef = null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Imports references needed by this helper.
|
||||
/// </summary>
|
||||
/// <param name="moduleDef"></param>
|
||||
/// <returns></returns>
|
||||
public override bool ImportReferences()
|
||||
{
|
||||
ResetValues();
|
||||
|
||||
Channel_TypeRef = base.ImportReference(typeof(Channel));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ced44bfdb3068f4cb7513c9be85729a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c480d34b00248444d91a61e015b14e2a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,34 @@
|
||||
using MonoFN.Cecil;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
internal class TypeDefinitionComparer : IEqualityComparer<TypeDefinition>
|
||||
{
|
||||
public bool Equals(TypeDefinition a, TypeDefinition b)
|
||||
{
|
||||
return a.FullName == b.FullName;
|
||||
}
|
||||
|
||||
public int GetHashCode(TypeDefinition obj)
|
||||
{
|
||||
return obj.FullName.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class TypeReferenceComparer : IEqualityComparer<TypeReference>
|
||||
{
|
||||
public bool Equals(TypeReference a, TypeReference b)
|
||||
{
|
||||
return a.FullName == b.FullName;
|
||||
}
|
||||
|
||||
public int GetHashCode(TypeReference obj)
|
||||
{
|
||||
return obj.FullName.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b30600f0fdb27c4fb86c310b08f43b5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,56 @@
|
||||
using MonoFN.Cecil;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
|
||||
|
||||
|
||||
internal class CreatedSyncVar
|
||||
{
|
||||
public readonly TypeDefinition VariableTd;
|
||||
public readonly MethodReference GetValueMr;
|
||||
public readonly MethodReference SetValueMr;
|
||||
public readonly MethodReference SetSyncIndexMr;
|
||||
public readonly MethodReference ConstructorMr;
|
||||
public readonly GenericInstanceType SyncVarGit;
|
||||
public FieldDefinition SyncVarClassFd { get; private set; }
|
||||
|
||||
public MethodReference HookMr;
|
||||
public CreatedSyncVar(GenericInstanceType syncVarGit, TypeDefinition variableTd, MethodReference getValueMr, MethodReference setValueMr, MethodReference setSyncIndexMr,MethodReference hookMr, MethodReference constructorMr)
|
||||
{
|
||||
SyncVarGit = syncVarGit;
|
||||
VariableTd = variableTd;
|
||||
GetValueMr = getValueMr;
|
||||
SetValueMr = setValueMr;
|
||||
SetSyncIndexMr = setSyncIndexMr;
|
||||
HookMr = hookMr;
|
||||
ConstructorMr = constructorMr;
|
||||
}
|
||||
|
||||
public void SetSyncVarClassField(FieldDefinition fd)
|
||||
{
|
||||
SyncVarClassFd = fd;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class CreatedSyncType
|
||||
{
|
||||
public TypeDefinition StubClassTypeDefinition;
|
||||
public MethodReference GetValueMethodReference;
|
||||
public MethodReference SetValueMethodReference;
|
||||
public MethodReference GetPreviousClientValueMethodReference;
|
||||
public MethodReference ReadMethodReference;
|
||||
public MethodReference ConstructorMethodReference;
|
||||
public CreatedSyncType(TypeDefinition stubClassTypeDef, MethodReference getMethodRef, MethodReference setMethodRef, MethodReference getPreviousMethodRef, MethodReference readMethodRef, MethodReference constructorMethodRef)
|
||||
{
|
||||
StubClassTypeDefinition = stubClassTypeDef;
|
||||
GetValueMethodReference = getMethodRef;
|
||||
SetValueMethodReference = setMethodRef;
|
||||
GetPreviousClientValueMethodReference = getPreviousMethodRef;
|
||||
ReadMethodReference = readMethodRef;
|
||||
ConstructorMethodReference = constructorMethodRef;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0cae698c9bc732641892caabf04365dc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,192 @@
|
||||
using FishNet.CodeGenerating.Extension;
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using FishNet.Object;
|
||||
using FishNet.Serializing.Helping;
|
||||
using FishNet.Utility.Performance;
|
||||
using MonoFN.Cecil;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
|
||||
|
||||
internal class GeneratorHelper : CodegenBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets what objectTypeRef will be serialized as.
|
||||
/// </summary>
|
||||
public SerializerType GetSerializerType(TypeReference objectTr, bool writer, out TypeDefinition objectTd)
|
||||
{
|
||||
string errorPrefix = (writer) ? "CreateWrite: " : "CreateRead: ";
|
||||
objectTd = null;
|
||||
|
||||
/* Check if already has a serializer. */
|
||||
if (writer)
|
||||
{
|
||||
if (base.GetClass<WriterProcessor>().GetWriteMethodReference(objectTr) != null)
|
||||
{
|
||||
base.LogError($"Writer already exist for {objectTr.FullName}.");
|
||||
return SerializerType.Invalid;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (base.GetClass<ReaderProcessor>().GetReadMethodReference(objectTr) != null)
|
||||
{
|
||||
base.LogError($"Reader already exist for {objectTr.FullName}.");
|
||||
return SerializerType.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
objectTd = objectTr.CachedResolve(base.Session);
|
||||
//Invalid typeDef.
|
||||
if (objectTd == null)
|
||||
{
|
||||
base.LogError($"{errorPrefix}{objectTd.FullName} could not be resolved.");
|
||||
return SerializerType.Invalid;
|
||||
}
|
||||
//Intentionally excluded.
|
||||
if (objectTd.CustomAttributes.Count > 0)
|
||||
{
|
||||
foreach (CustomAttribute item in objectTd.CustomAttributes)
|
||||
{
|
||||
if (item.AttributeType.Is(typeof(CodegenExcludeAttribute)))
|
||||
return SerializerType.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
//By reference.
|
||||
if (objectTr.IsByReference)
|
||||
{
|
||||
base.LogError($"{errorPrefix}Cannot pass {objectTr.Name} by reference");
|
||||
return SerializerType.Invalid;
|
||||
}
|
||||
/* Arrays have to be processed first because it's possible for them to meet other conditions
|
||||
* below and be processed wrong. */
|
||||
else if (objectTr.IsArray)
|
||||
{
|
||||
if (objectTr.IsMultidimensionalArray())
|
||||
{
|
||||
base.LogError($"{errorPrefix}{objectTr.Name} is an unsupported type. Multidimensional arrays are not supported");
|
||||
return SerializerType.Invalid;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SerializerType.Array;
|
||||
}
|
||||
}
|
||||
//Enum.
|
||||
else if (objectTd.IsEnum)
|
||||
{
|
||||
return SerializerType.Enum;
|
||||
}
|
||||
else if (objectTd.Is(typeof(Dictionary<,>)))
|
||||
{
|
||||
return SerializerType.Dictionary;
|
||||
}
|
||||
else if (objectTd.Is(typeof(List<>)))
|
||||
{
|
||||
return SerializerType.List;
|
||||
}
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
else if (objectTd.Is(typeof(ListCache<>))) //Remove on 2024/01/01
|
||||
{
|
||||
return SerializerType.ListCache;
|
||||
}
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
else if (objectTd.InheritsFrom<NetworkBehaviour>(base.Session))
|
||||
{
|
||||
return SerializerType.NetworkBehaviour;
|
||||
}
|
||||
else if (objectTr.IsNullable(base.Session))
|
||||
{
|
||||
GenericInstanceType git = objectTr as GenericInstanceType;
|
||||
if (git == null || git.GenericArguments.Count != 1)
|
||||
return SerializerType.Invalid;
|
||||
else
|
||||
return SerializerType.Nullable;
|
||||
}
|
||||
//Invalid type. This must be called after trying to generate everything but class.
|
||||
else if (!CanGenerateSerializer(objectTd))
|
||||
{
|
||||
return SerializerType.Invalid;
|
||||
}
|
||||
//If here then the only type left is struct or class.
|
||||
else if (objectTr.IsClassOrStruct(base.Session))
|
||||
{
|
||||
return SerializerType.ClassOrStruct;
|
||||
}
|
||||
//Unknown type.
|
||||
else
|
||||
{
|
||||
base.LogError($"{errorPrefix}{objectTr.Name} is an unsupported type. Mostly because we don't know what the heck it is. Please let us know so we can fix this.");
|
||||
return SerializerType.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns if objectTd can have a serializer generated for it.
|
||||
/// </summary>
|
||||
private bool CanGenerateSerializer(TypeDefinition objectTd)
|
||||
{
|
||||
string errorText = $"{objectTd.Name} is not a supported type. Use a supported type or provide a custom serializer";
|
||||
|
||||
System.Type unityObjectType = typeof(UnityEngine.Object);
|
||||
//Unable to determine type, cannot generate for.
|
||||
if (objectTd == null)
|
||||
{
|
||||
base.LogError(errorText);
|
||||
return false;
|
||||
}
|
||||
//Component.
|
||||
if (objectTd.InheritsFrom<UnityEngine.Component>(base.Session))
|
||||
{
|
||||
base.LogError(errorText);
|
||||
return false;
|
||||
}
|
||||
//Unity Object.
|
||||
if (objectTd.Is(unityObjectType))
|
||||
{
|
||||
base.LogError(errorText);
|
||||
return false;
|
||||
}
|
||||
//ScriptableObject.
|
||||
if (objectTd.Is(typeof(UnityEngine.ScriptableObject)))
|
||||
{
|
||||
base.LogError(errorText);
|
||||
return false;
|
||||
}
|
||||
//Has generic parameters.
|
||||
if (objectTd.HasGenericParameters)
|
||||
{
|
||||
base.LogError(errorText);
|
||||
return false;
|
||||
}
|
||||
//Is an interface.
|
||||
if (objectTd.IsInterface)
|
||||
{
|
||||
base.LogError(errorText);
|
||||
return false;
|
||||
}
|
||||
//Is abstract.
|
||||
if (objectTd.IsAbstract)
|
||||
{
|
||||
base.LogError(errorText);
|
||||
return false;
|
||||
}
|
||||
if (objectTd.InheritsFrom(base.Session, unityObjectType) && objectTd.IsExcluded(GeneralHelper.UNITYENGINE_ASSEMBLY_PREFIX))
|
||||
{
|
||||
base.LogError(errorText);
|
||||
return false;
|
||||
}
|
||||
|
||||
//If here type is valid.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b1882eac63e3d94aad8f41915bc1ab8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
|
||||
internal enum QolAttributeType
|
||||
{
|
||||
None,
|
||||
Server,
|
||||
Client
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 357a22940018b8e49976e13272c5b2ef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
|
||||
internal enum SerializerType
|
||||
{
|
||||
Invalid,
|
||||
Enum,
|
||||
Array,
|
||||
List,
|
||||
ListCache,
|
||||
NetworkBehaviour,
|
||||
ClassOrStruct,
|
||||
Nullable,
|
||||
Dictionary,
|
||||
Null,
|
||||
ByReference,
|
||||
MultiDimensionalArray
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e7f1bbf5c398c3e4e92e53ec3e49d5e9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
using MonoFN.Cecil.Cil;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Data used to modify an RpcIndex should the class have to be rebuilt.
|
||||
/// </summary>
|
||||
internal class SyncIndexData
|
||||
{
|
||||
public uint SyncCount = 0;
|
||||
public List<Instruction> DelegateInstructions = new List<Instruction>();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55f2e751e4788464b8394f6b8bdb548a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
|
||||
public enum SyncType
|
||||
{
|
||||
Unset,
|
||||
Variable,
|
||||
List,
|
||||
Dictionary,
|
||||
HashSet,
|
||||
Custom
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 44c753d6ac0c7864c9962d91703b2afe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,100 @@
|
||||
using FishNet.Object;
|
||||
using FishNet.Serializing;
|
||||
using MonoFN.Cecil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
internal class WriterImports : CodegenBase
|
||||
{
|
||||
#region Reflection references.
|
||||
public MethodReference WriterPool_GetWriter_MethodRef;
|
||||
public MethodReference WriterPool_GetWriterLength_MethodRef;
|
||||
public MethodReference Writer_WritePackedWhole_MethodRef;
|
||||
public TypeReference PooledWriter_TypeRef;
|
||||
public TypeReference Writer_TypeRef;
|
||||
public MethodReference PooledWriter_Dispose_MethodRef;
|
||||
public MethodReference Writer_WriteDictionary_MethodRef;
|
||||
public MethodReference Writer_WriteList_MethodRef;
|
||||
public MethodReference Writer_WriteListCache_MethodRef;
|
||||
public MethodReference Writer_WriteArray_MethodRef;
|
||||
public TypeReference AutoPackTypeRef;
|
||||
|
||||
public TypeReference GenericWriterTypeRef;
|
||||
public TypeReference WriterTypeRef;
|
||||
public MethodReference WriteGetSetMethodRef;
|
||||
public MethodReference WriteAutoPackGetSetMethodRef;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Imports references needed by this helper.
|
||||
/// </summary>
|
||||
/// <param name="moduleDef"></param>
|
||||
/// <returns></returns>
|
||||
public override bool ImportReferences()
|
||||
{
|
||||
PooledWriter_TypeRef = base.ImportReference(typeof(PooledWriter));
|
||||
Writer_TypeRef = base.ImportReference(typeof(Writer));
|
||||
AutoPackTypeRef = base.ImportReference(typeof(AutoPackType));
|
||||
|
||||
GenericWriterTypeRef = base.ImportReference(typeof(GenericWriter<>));
|
||||
WriterTypeRef = base.ImportReference(typeof(Writer));
|
||||
|
||||
PropertyInfo writePropertyInfo;
|
||||
writePropertyInfo = typeof(GenericWriter<>).GetProperty(nameof(GenericWriter<int>.Write));
|
||||
WriteGetSetMethodRef = base.ImportReference(writePropertyInfo.GetSetMethod());
|
||||
writePropertyInfo = typeof(GenericWriter<>).GetProperty(nameof(GenericWriter<int>.WriteAutoPack));
|
||||
WriteAutoPackGetSetMethodRef = base.ImportReference(writePropertyInfo.GetSetMethod());
|
||||
|
||||
//WriterPool.GetWriter
|
||||
Type writerPoolType = typeof(WriterPool);
|
||||
base.ImportReference(writerPoolType);
|
||||
foreach (var methodInfo in writerPoolType.GetMethods())
|
||||
{
|
||||
if (methodInfo.Name == nameof(WriterPool.GetWriter))
|
||||
{
|
||||
//GetWriter().
|
||||
if (methodInfo.GetParameters().Length == 0)
|
||||
{
|
||||
WriterPool_GetWriter_MethodRef = base.ImportReference(methodInfo);
|
||||
}
|
||||
//GetWriter(?).
|
||||
else if (methodInfo.GetParameters().Length == 1)
|
||||
{
|
||||
ParameterInfo pi = methodInfo.GetParameters()[0];
|
||||
//GetWriter(int).
|
||||
if (pi.ParameterType == typeof(int))
|
||||
WriterPool_GetWriterLength_MethodRef = base.ImportReference(methodInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WriterProcessor gwh = base.GetClass<WriterProcessor>();
|
||||
Type pooledWriterType = typeof(PooledWriter);
|
||||
foreach (MethodInfo methodInfo in pooledWriterType.GetMethods())
|
||||
{
|
||||
int parameterCount = methodInfo.GetParameters().Length;
|
||||
|
||||
if (methodInfo.Name == nameof(PooledWriter.Store))
|
||||
PooledWriter_Dispose_MethodRef = base.ImportReference(methodInfo);
|
||||
else if (methodInfo.Name == nameof(PooledWriter.WritePackedWhole))
|
||||
Writer_WritePackedWhole_MethodRef = base.ImportReference(methodInfo);
|
||||
//Relay writers.
|
||||
else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteDictionary))
|
||||
Writer_WriteDictionary_MethodRef = base.ImportReference(methodInfo);
|
||||
else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteList))
|
||||
Writer_WriteList_MethodRef = base.ImportReference(methodInfo);
|
||||
else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteListCache))
|
||||
Writer_WriteListCache_MethodRef = base.ImportReference(methodInfo);
|
||||
else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteArray))
|
||||
Writer_WriteArray_MethodRef = base.ImportReference(methodInfo);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 425638e29bab6f1488e8865c9e3f8b57
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
phr/StickGame/Assets/FishNet/CodeGenerating/ILCore.meta
Normal file
8
phr/StickGame/Assets/FishNet/CodeGenerating/ILCore.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1808ca0399d069499d3ecb4e031c3e2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,407 @@
|
||||
using FishNet.Broadcast;
|
||||
using FishNet.CodeGenerating.Extension;
|
||||
using FishNet.CodeGenerating.Helping;
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using FishNet.CodeGenerating.Processing;
|
||||
using FishNet.CodeGenerating.Processing.Rpc;
|
||||
using FishNet.Configuring;
|
||||
using FishNet.Serializing.Helping;
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Cil;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Unity.CompilationPipeline.Common.ILPostProcessing;
|
||||
|
||||
namespace FishNet.CodeGenerating.ILCore
|
||||
{
|
||||
public class FishNetILPP : ILPostProcessor
|
||||
{
|
||||
#region Const.
|
||||
internal const string RUNTIME_ASSEMBLY_NAME = "FishNet.Runtime";
|
||||
#endregion
|
||||
|
||||
public override bool WillProcess(ICompiledAssembly compiledAssembly)
|
||||
{
|
||||
if (compiledAssembly.Name.StartsWith("Unity."))
|
||||
return false;
|
||||
if (compiledAssembly.Name.StartsWith("UnityEngine."))
|
||||
return false;
|
||||
if (compiledAssembly.Name.StartsWith("UnityEditor."))
|
||||
return false;
|
||||
if (compiledAssembly.Name.Contains("Editor"))
|
||||
return false;
|
||||
|
||||
/* This line contradicts the one below where referencesFishNet
|
||||
* becomes true if the assembly is FishNetAssembly. This is here
|
||||
* intentionally to stop codegen from running on the runtime
|
||||
* fishnet assembly, but the option below is for debugging. I would
|
||||
* comment out this check if I wanted to compile fishnet runtime. */
|
||||
//if (CODEGEN_THIS_NAMESPACE.Length == 0)
|
||||
//{
|
||||
// if (compiledAssembly.Name == RUNTIME_ASSEMBLY_NAME)
|
||||
// return false;
|
||||
//}
|
||||
bool referencesFishNet = FishNetILPP.IsFishNetAssembly(compiledAssembly) || compiledAssembly.References.Any(filePath => Path.GetFileNameWithoutExtension(filePath) == RUNTIME_ASSEMBLY_NAME);
|
||||
return referencesFishNet;
|
||||
}
|
||||
public override ILPostProcessor GetInstance() => this;
|
||||
|
||||
public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
|
||||
{
|
||||
AssemblyDefinition assemblyDef = ILCoreHelper.GetAssemblyDefinition(compiledAssembly);
|
||||
if (assemblyDef == null)
|
||||
return null;
|
||||
|
||||
//Check WillProcess again; somehow certain editor scripts skip the WillProcess check.
|
||||
if (!WillProcess(compiledAssembly))
|
||||
return null;
|
||||
|
||||
CodegenSession session = new CodegenSession();
|
||||
if (!session.Initialize(assemblyDef.MainModule))
|
||||
return null;
|
||||
|
||||
bool modified = false;
|
||||
|
||||
bool fnAssembly = IsFishNetAssembly(compiledAssembly);
|
||||
if (fnAssembly)
|
||||
modified |= ModifyMakePublicMethods(session);
|
||||
/* If one or more scripts use RPCs but don't inherit NetworkBehaviours
|
||||
* then don't bother processing the rest. */
|
||||
if (session.GetClass<NetworkBehaviourProcessor>().NonNetworkBehaviourHasInvalidAttributes(session.Module.Types))
|
||||
return new ILPostProcessResult(null, session.Diagnostics);
|
||||
|
||||
modified |= session.GetClass<WriterProcessor>().Process();
|
||||
modified |= session.GetClass<ReaderProcessor>().Process();
|
||||
modified |= CreateDeclaredSerializerDelegates(session);
|
||||
modified |= CreateDeclaredSerializers(session);
|
||||
modified |= CreateDeclaredComparerDelegates(session);
|
||||
modified |= CreateIBroadcast(session);
|
||||
#if !DISABLE_QOL_ATTRIBUTES
|
||||
modified |= CreateQOLAttributes(session);
|
||||
#endif
|
||||
modified |= CreateNetworkBehaviours(session);
|
||||
modified |= CreateGenericReadWriteDelegates(session);
|
||||
|
||||
if (fnAssembly)
|
||||
{
|
||||
AssemblyNameReference anr = session.Module.AssemblyReferences.FirstOrDefault<AssemblyNameReference>(x => x.FullName == session.Module.Assembly.FullName);
|
||||
if (anr != null)
|
||||
session.Module.AssemblyReferences.Remove(anr);
|
||||
}
|
||||
|
||||
/* If there are warnings about SyncVars being in different assemblies.
|
||||
* This is awful ... codegen would need to be reworked to save
|
||||
* syncvars across all assemblies so that scripts referencing them from
|
||||
* another assembly can have it's instructions changed. This however is an immense
|
||||
* amount of work so it will have to be put on hold, for... a long.. long while. */
|
||||
if (session.DifferentAssemblySyncVars.Count > 0)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine($"Assembly {session.Module.Name} has inherited access to SyncVars in different assemblies. When accessing SyncVars across assemblies be sure to use Get/Set methods withinin the inherited assembly script to change SyncVars. Accessible fields are:");
|
||||
|
||||
foreach (FieldDefinition item in session.DifferentAssemblySyncVars)
|
||||
sb.AppendLine($"Field {item.Name} within {item.DeclaringType.FullName} in assembly {item.Module.Name}.");
|
||||
|
||||
session.LogWarning("v------- IMPORTANT -------v");
|
||||
session.LogWarning(sb.ToString());
|
||||
session.DifferentAssemblySyncVars.Clear();
|
||||
}
|
||||
|
||||
//session.LogWarning($"Assembly {compiledAssembly.Name} took {stopwatch.ElapsedMilliseconds}.");
|
||||
if (!modified)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryStream pe = new MemoryStream();
|
||||
MemoryStream pdb = new MemoryStream();
|
||||
WriterParameters writerParameters = new WriterParameters
|
||||
{
|
||||
SymbolWriterProvider = new PortablePdbWriterProvider(),
|
||||
SymbolStream = pdb,
|
||||
WriteSymbols = true
|
||||
};
|
||||
assemblyDef.Write(pe, writerParameters);
|
||||
return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), session.Diagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makees methods public scope which use CodegenMakePublic attribute.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool ModifyMakePublicMethods(CodegenSession session)
|
||||
{
|
||||
string makePublicTypeFullName = typeof(CodegenMakePublicAttribute).FullName;
|
||||
foreach (TypeDefinition td in session.Module.Types)
|
||||
{
|
||||
foreach (MethodDefinition md in td.Methods)
|
||||
{
|
||||
foreach (CustomAttribute ca in md.CustomAttributes)
|
||||
{
|
||||
if (ca.AttributeType.FullName == makePublicTypeFullName)
|
||||
{
|
||||
md.Attributes &= ~MethodAttributes.Assembly;
|
||||
md.Attributes |= MethodAttributes.Public;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//There is always at least one modified.
|
||||
return true;
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates delegates for user declared serializers.
|
||||
/// </summary>
|
||||
internal bool CreateDeclaredSerializerDelegates(CodegenSession session)
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
TypeAttributes readWriteExtensionTypeAttr = (TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract);
|
||||
List<TypeDefinition> allTypeDefs = session.Module.Types.ToList();
|
||||
foreach (TypeDefinition td in allTypeDefs)
|
||||
{
|
||||
if (session.GetClass<GeneralHelper>().IgnoreTypeDefinition(td))
|
||||
continue;
|
||||
|
||||
if (td.Attributes.HasFlag(readWriteExtensionTypeAttr))
|
||||
modified |= session.GetClass<CustomSerializerProcessor>().CreateSerializerDelegates(td, true);
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates serializers for custom types within user declared serializers.
|
||||
/// </summary>
|
||||
private bool CreateDeclaredSerializers(CodegenSession session)
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
TypeAttributes readWriteExtensionTypeAttr = (TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract);
|
||||
List<TypeDefinition> allTypeDefs = session.Module.Types.ToList();
|
||||
foreach (TypeDefinition td in allTypeDefs)
|
||||
{
|
||||
if (session.GetClass<GeneralHelper>().IgnoreTypeDefinition(td))
|
||||
continue;
|
||||
|
||||
if (td.Attributes.HasFlag(readWriteExtensionTypeAttr))
|
||||
modified |= session.GetClass<CustomSerializerProcessor>().CreateSerializers(td);
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates delegates for user declared comparers.
|
||||
/// </summary>
|
||||
internal bool CreateDeclaredComparerDelegates(CodegenSession session)
|
||||
{
|
||||
bool modified = false;
|
||||
List<TypeDefinition> allTypeDefs = session.Module.Types.ToList();
|
||||
foreach (TypeDefinition td in allTypeDefs)
|
||||
{
|
||||
if (session.GetClass<GeneralHelper>().IgnoreTypeDefinition(td))
|
||||
continue;
|
||||
|
||||
modified |= session.GetClass<CustomSerializerProcessor>().CreateComparerDelegates(td);
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creaters serializers and calls for IBroadcast.
|
||||
/// </summary>
|
||||
/// <param name="moduleDef"></param>
|
||||
/// <param name="diagnostics"></param>
|
||||
private bool CreateIBroadcast(CodegenSession session)
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
string networkBehaviourFullName = session.GetClass<NetworkBehaviourHelper>().FullName;
|
||||
HashSet<TypeDefinition> typeDefs = new HashSet<TypeDefinition>();
|
||||
foreach (TypeDefinition td in session.Module.Types)
|
||||
{
|
||||
TypeDefinition climbTd = td;
|
||||
do
|
||||
{
|
||||
//Reached NetworkBehaviour class.
|
||||
if (climbTd.FullName == networkBehaviourFullName)
|
||||
break;
|
||||
|
||||
///* Check initial class as well all types within
|
||||
// * the class. Then check all of it's base classes. */
|
||||
if (climbTd.ImplementsInterface<IBroadcast>())
|
||||
typeDefs.Add(climbTd);
|
||||
//7ms
|
||||
|
||||
//Add nested. Only going to go a single layer deep.
|
||||
foreach (TypeDefinition nestedTypeDef in td.NestedTypes)
|
||||
{
|
||||
if (nestedTypeDef.ImplementsInterface<IBroadcast>())
|
||||
typeDefs.Add(nestedTypeDef);
|
||||
}
|
||||
//0ms
|
||||
|
||||
climbTd = climbTd.GetNextBaseTypeDefinition(session);
|
||||
//this + name check 40ms
|
||||
} while (climbTd != null);
|
||||
|
||||
}
|
||||
|
||||
|
||||
//Create reader/writers for found typeDefs.
|
||||
foreach (TypeDefinition td in typeDefs)
|
||||
{
|
||||
TypeReference typeRef = session.ImportReference(td);
|
||||
bool canSerialize = session.GetClass<GeneralHelper>().HasSerializerAndDeserializer(typeRef, true);
|
||||
if (!canSerialize)
|
||||
session.LogError($"Broadcast {td.Name} does not support serialization. Use a supported type or create a custom serializer.");
|
||||
else
|
||||
modified = true;
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles QOLAttributes such as [Server].
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool CreateQOLAttributes(CodegenSession session)
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
bool codeStripping = false;
|
||||
|
||||
List<TypeDefinition> allTypeDefs = session.Module.Types.ToList();
|
||||
|
||||
/* First pass, potentially only pass.
|
||||
* If code stripping them this will be run again. The first iteration
|
||||
* is to ensure things are removed in the proper order. */
|
||||
foreach (TypeDefinition td in allTypeDefs)
|
||||
{
|
||||
if (session.GetClass<GeneralHelper>().IgnoreTypeDefinition(td))
|
||||
continue;
|
||||
|
||||
modified |= session.GetClass<QolAttributeProcessor>().Process(td, codeStripping);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates NetworkBehaviour changes.
|
||||
/// </summary>
|
||||
/// <param name="moduleDef"></param>
|
||||
/// <param name="diagnostics"></param>
|
||||
private bool CreateNetworkBehaviours(CodegenSession session)
|
||||
{
|
||||
bool modified = false;
|
||||
//Get all network behaviours to process.
|
||||
List<TypeDefinition> networkBehaviourTypeDefs = session.Module.Types
|
||||
.Where(td => td.IsSubclassOf(session, session.GetClass<NetworkBehaviourHelper>().FullName))
|
||||
.ToList();
|
||||
|
||||
//Moment a NetworkBehaviour exist the assembly is considered modified.
|
||||
if (networkBehaviourTypeDefs.Count > 0)
|
||||
modified = true;
|
||||
|
||||
/* Remove types which are inherited. This gets the child most networkbehaviours.
|
||||
* Since processing iterates all parent classes there's no reason to include them */
|
||||
RemoveInheritedTypeDefinitions(networkBehaviourTypeDefs);
|
||||
|
||||
/* This holds all sync types created, synclist, dictionary, var
|
||||
* and so on. This data is used after all syncvars are made so
|
||||
* other methods can look for references to created synctypes and
|
||||
* replace accessors accordingly. */
|
||||
List<(SyncType, ProcessedSync)> allProcessedSyncs = new List<(SyncType, ProcessedSync)>();
|
||||
HashSet<string> allProcessedCallbacks = new HashSet<string>();
|
||||
List<TypeDefinition> processedClasses = new List<TypeDefinition>();
|
||||
|
||||
foreach (TypeDefinition typeDef in networkBehaviourTypeDefs)
|
||||
{
|
||||
session.ImportReference(typeDef);
|
||||
//Synctypes processed for this nb and it's inherited classes.
|
||||
List<(SyncType, ProcessedSync)> processedSyncs = new List<(SyncType, ProcessedSync)>();
|
||||
session.GetClass<NetworkBehaviourProcessor>().ProcessLocal(typeDef, processedSyncs);
|
||||
//Add to all processed.
|
||||
allProcessedSyncs.AddRange(processedSyncs);
|
||||
}
|
||||
|
||||
/* Must run through all scripts should user change syncvar
|
||||
* from outside the networkbehaviour. */
|
||||
if (allProcessedSyncs.Count > 0)
|
||||
{
|
||||
foreach (TypeDefinition td in session.Module.Types)
|
||||
{
|
||||
session.GetClass<NetworkBehaviourSyncProcessor>().ReplaceGetSets(td, allProcessedSyncs);
|
||||
session.GetClass<RpcProcessor>().RedirectBaseCalls();
|
||||
}
|
||||
}
|
||||
|
||||
/* Removes typedefinitions which are inherited by
|
||||
* another within tds. For example, if the collection
|
||||
* td contains A, B, C and our structure is
|
||||
* A : B : C then B and C will be removed from the collection
|
||||
* Since they are both inherited by A. */
|
||||
void RemoveInheritedTypeDefinitions(List<TypeDefinition> tds)
|
||||
{
|
||||
HashSet<TypeDefinition> inheritedTds = new HashSet<TypeDefinition>();
|
||||
/* Remove any networkbehaviour typedefs which are inherited by
|
||||
* another networkbehaviour typedef. When a networkbehaviour typedef
|
||||
* is processed so are all of the inherited types. */
|
||||
for (int i = 0; i < tds.Count; i++)
|
||||
{
|
||||
/* Iterates all base types and
|
||||
* adds them to inheritedTds so long
|
||||
* as the base type is not a NetworkBehaviour. */
|
||||
TypeDefinition copyTd = tds[i].GetNextBaseTypeDefinition(session);
|
||||
while (copyTd != null)
|
||||
{
|
||||
//Class is NB.
|
||||
if (copyTd.FullName == session.GetClass<NetworkBehaviourHelper>().FullName)
|
||||
break;
|
||||
|
||||
inheritedTds.Add(copyTd);
|
||||
copyTd = copyTd.GetNextBaseTypeDefinition(session);
|
||||
}
|
||||
}
|
||||
|
||||
//Remove all inherited types.
|
||||
foreach (TypeDefinition item in inheritedTds)
|
||||
tds.Remove(item);
|
||||
}
|
||||
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates generic delegates for all read and write methods.
|
||||
/// </summary>
|
||||
/// <param name="moduleDef"></param>
|
||||
/// <param name="diagnostics"></param>
|
||||
private bool CreateGenericReadWriteDelegates(CodegenSession session)
|
||||
{
|
||||
session.GetClass<WriterProcessor>().CreateStaticMethodDelegates();
|
||||
session.GetClass<ReaderProcessor>().CreateStaticMethodDelegates();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static bool IsFishNetAssembly(ICompiledAssembly assembly) => (assembly.Name == FishNetILPP.RUNTIME_ASSEMBLY_NAME);
|
||||
internal static bool IsFishNetAssembly(CodegenSession session) => (session.Module.Assembly.Name.Name == FishNetILPP.RUNTIME_ASSEMBLY_NAME);
|
||||
internal static bool IsFishNetAssembly(ModuleDefinition moduleDef) => (moduleDef.Assembly.Name.Name == FishNetILPP.RUNTIME_ASSEMBLY_NAME);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f03d76b376c1d5b4591039af6fd4c9e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,38 @@
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Cil;
|
||||
using System.IO;
|
||||
using Unity.CompilationPipeline.Common.ILPostProcessing;
|
||||
|
||||
namespace FishNet.CodeGenerating.ILCore
|
||||
{
|
||||
internal static class ILCoreHelper
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Returns AssembleDefinition for compiledAssembly.
|
||||
/// </summary>
|
||||
/// <param name="compiledAssembly"></param>
|
||||
/// <returns></returns>
|
||||
internal static AssemblyDefinition GetAssemblyDefinition(ICompiledAssembly compiledAssembly)
|
||||
{
|
||||
PostProcessorAssemblyResolver assemblyResolver = new PostProcessorAssemblyResolver(compiledAssembly);
|
||||
ReaderParameters readerParameters = new ReaderParameters
|
||||
{
|
||||
SymbolStream = new MemoryStream(compiledAssembly.InMemoryAssembly.PdbData),
|
||||
SymbolReaderProvider = new PortablePdbReaderProvider(),
|
||||
AssemblyResolver = assemblyResolver,
|
||||
ReflectionImporterProvider = new PostProcessorReflectionImporterProvider(),
|
||||
ReadingMode = ReadingMode.Immediate
|
||||
};
|
||||
|
||||
AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(new MemoryStream(compiledAssembly.InMemoryAssembly.PeData), readerParameters);
|
||||
//Allows us to resolve inside FishNet assembly, such as for components.
|
||||
assemblyResolver.AddAssemblyDefinitionBeingOperatedOn(assemblyDefinition);
|
||||
|
||||
return assemblyDefinition;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dfcfb917dd9268744962ae61aa0115b7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,139 @@
|
||||
using MonoFN.Cecil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Unity.CompilationPipeline.Common.ILPostProcessing;
|
||||
|
||||
namespace FishNet.CodeGenerating
|
||||
{
|
||||
internal class PostProcessorAssemblyResolver : IAssemblyResolver
|
||||
{
|
||||
private readonly string[] m_AssemblyReferences;
|
||||
private readonly Dictionary<string, AssemblyDefinition> m_AssemblyCache = new Dictionary<string, AssemblyDefinition>();
|
||||
private readonly ICompiledAssembly m_CompiledAssembly;
|
||||
private AssemblyDefinition m_SelfAssembly;
|
||||
|
||||
public PostProcessorAssemblyResolver(ICompiledAssembly compiledAssembly)
|
||||
{
|
||||
m_CompiledAssembly = compiledAssembly;
|
||||
m_AssemblyReferences = compiledAssembly.References;
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public AssemblyDefinition Resolve(AssemblyNameReference name) => Resolve(name, new ReaderParameters(ReadingMode.Deferred));
|
||||
|
||||
public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
|
||||
{
|
||||
lock (m_AssemblyCache)
|
||||
{
|
||||
if (name.Name == m_CompiledAssembly.Name)
|
||||
{
|
||||
return m_SelfAssembly;
|
||||
}
|
||||
|
||||
var fileName = FindFile(name);
|
||||
if (fileName == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var lastWriteTime = File.GetLastWriteTime(fileName);
|
||||
var cacheKey = $"{fileName}{lastWriteTime}";
|
||||
if (m_AssemblyCache.TryGetValue(cacheKey, out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
parameters.AssemblyResolver = this;
|
||||
|
||||
var ms = MemoryStreamFor(fileName);
|
||||
var pdb = $"{fileName}.pdb";
|
||||
if (File.Exists(pdb))
|
||||
{
|
||||
parameters.SymbolStream = MemoryStreamFor(pdb);
|
||||
}
|
||||
|
||||
var assemblyDefinition = AssemblyDefinition.ReadAssembly(ms, parameters);
|
||||
m_AssemblyCache.Add(cacheKey, assemblyDefinition);
|
||||
|
||||
return assemblyDefinition;
|
||||
}
|
||||
}
|
||||
|
||||
private string FindFile(AssemblyNameReference name)
|
||||
{
|
||||
var fileName = m_AssemblyReferences.FirstOrDefault(r => Path.GetFileName(r) == $"{name.Name}.dll");
|
||||
if (fileName != null)
|
||||
{
|
||||
return fileName;
|
||||
}
|
||||
|
||||
// perhaps the type comes from an exe instead
|
||||
fileName = m_AssemblyReferences.FirstOrDefault(r => Path.GetFileName(r) == $"{name.Name}.exe");
|
||||
if (fileName != null)
|
||||
{
|
||||
return fileName;
|
||||
}
|
||||
|
||||
//Unfortunately the current ICompiledAssembly API only provides direct references.
|
||||
//It is very much possible that a postprocessor ends up investigating a type in a directly
|
||||
//referenced assembly, that contains a field that is not in a directly referenced assembly.
|
||||
//if we don't do anything special for that situation, it will fail to resolve. We should fix this
|
||||
//in the ILPostProcessing API. As a workaround, we rely on the fact here that the indirect references
|
||||
//are always located next to direct references, so we search in all directories of direct references we
|
||||
//got passed, and if we find the file in there, we resolve to it.
|
||||
return m_AssemblyReferences
|
||||
.Select(Path.GetDirectoryName)
|
||||
.Distinct()
|
||||
.Select(parentDir => Path.Combine(parentDir, $"{name.Name}.dll"))
|
||||
.FirstOrDefault(File.Exists);
|
||||
}
|
||||
|
||||
private static MemoryStream MemoryStreamFor(string fileName)
|
||||
{
|
||||
return Retry(10, TimeSpan.FromSeconds(1), () =>
|
||||
{
|
||||
byte[] byteArray;
|
||||
using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
byteArray = new byte[fs.Length];
|
||||
var readLength = fs.Read(byteArray, 0, (int)fs.Length);
|
||||
if (readLength != fs.Length)
|
||||
{
|
||||
throw new InvalidOperationException("File read length is not full length of file.");
|
||||
}
|
||||
}
|
||||
|
||||
return new MemoryStream(byteArray);
|
||||
});
|
||||
}
|
||||
|
||||
private static MemoryStream Retry(int retryCount, TimeSpan waitTime, Func<MemoryStream> func)
|
||||
{
|
||||
try
|
||||
{
|
||||
return func();
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
if (retryCount == 0)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Caught IO Exception, trying {retryCount} more times");
|
||||
Thread.Sleep(waitTime);
|
||||
|
||||
return Retry(retryCount - 1, waitTime, func);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddAssemblyDefinitionBeingOperatedOn(AssemblyDefinition assemblyDefinition)
|
||||
{
|
||||
m_SelfAssembly = assemblyDefinition;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c247f4266b2864eb96e6a9ae6557d31
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,22 @@
|
||||
using MonoFN.Cecil;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace FishNet.CodeGenerating.ILCore
|
||||
{
|
||||
internal class PostProcessorReflectionImporter : DefaultReflectionImporter
|
||||
{
|
||||
private const string k_SystemPrivateCoreLib = "System.Private.CoreLib";
|
||||
private readonly AssemblyNameReference m_CorrectCorlib;
|
||||
|
||||
public PostProcessorReflectionImporter(ModuleDefinition module) : base(module)
|
||||
{
|
||||
m_CorrectCorlib = module.AssemblyReferences.FirstOrDefault(a => a.Name == "mscorlib" || a.Name == "netstandard" || a.Name == k_SystemPrivateCoreLib);
|
||||
}
|
||||
|
||||
public override AssemblyNameReference ImportReference(AssemblyName reference)
|
||||
{
|
||||
return m_CorrectCorlib != null && reference.Name == k_SystemPrivateCoreLib ? m_CorrectCorlib : base.ImportReference(reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 484e8ad8c4dde382ea67036b32935ef1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
using MonoFN.Cecil;
|
||||
|
||||
namespace FishNet.CodeGenerating.ILCore
|
||||
{
|
||||
internal class PostProcessorReflectionImporterProvider : IReflectionImporterProvider
|
||||
{
|
||||
public IReflectionImporter GetReflectionImporter(ModuleDefinition moduleDef)
|
||||
{
|
||||
return new PostProcessorReflectionImporter(moduleDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9273a5dad109ab0783891e36c983080
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15ff4c71a3c972440810ac633b69764d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,61 @@
|
||||
using MonoFN.Cecil;
|
||||
using SR = System.Reflection;
|
||||
|
||||
|
||||
namespace FishNet.CodeGenerating
|
||||
{
|
||||
internal abstract class CodegenBase
|
||||
{
|
||||
//Lazy debug checks.
|
||||
public bool IsIsolatedAsm => (Module.Name.Contains("IsolatedAsm"));
|
||||
public bool IsRuntimeAsm => (Module.Name.Contains("FishNet.Runtime"));
|
||||
|
||||
public CodegenSession Session { get; private set; }
|
||||
public ModuleDefinition Module { get; private set; }
|
||||
|
||||
public virtual bool ImportReferences() { return true; }
|
||||
|
||||
public void Initialize(CodegenSession session)
|
||||
{
|
||||
Session = session;
|
||||
Module = session.Module;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns class of type if found within Session.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
internal T GetClass<T>() where T : CodegenBase => Session.GetClass<T>();
|
||||
|
||||
#region Logging.
|
||||
/// <summary>
|
||||
/// Logs a warning.
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
internal void LogWarning(string msg) => Session.LogWarning(msg);
|
||||
/// <summary>
|
||||
/// Logs an error.
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
internal void LogError(string msg) => Session.LogError(msg);
|
||||
#endregion
|
||||
|
||||
#region ImportReference.
|
||||
public MethodReference ImportReference(SR.MethodBase method) => Session.ImportReference(method);
|
||||
public MethodReference ImportReference(SR.MethodBase method, IGenericParameterProvider context) => Session.ImportReference(method, context);
|
||||
public TypeReference ImportReference(TypeReference type) => Session.ImportReference(type);
|
||||
public TypeReference ImportReference(TypeReference type, IGenericParameterProvider context) => Session.ImportReference(type, context);
|
||||
public FieldReference ImportReference(FieldReference field) => Session.ImportReference(field);
|
||||
public FieldReference ImportReference(FieldReference field, IGenericParameterProvider context) => Session.ImportReference(field, context);
|
||||
public FieldReference ImportReference(SR.FieldInfo field) => Session.ImportReference(field);
|
||||
public FieldReference ImportReference(SR.FieldInfo field, IGenericParameterProvider context) => Session.ImportReference(field, context);
|
||||
public MethodReference ImportReference(MethodReference method) => Session.ImportReference(method);
|
||||
public MethodReference ImportReference(MethodReference method, IGenericParameterProvider context) => Session.ImportReference(method, context);
|
||||
public TypeReference ImportReference(System.Type type) => Session.ImportReference(type, null);
|
||||
public TypeReference ImportReference(System.Type type, IGenericParameterProvider context) => Session.ImportReference(type, context);
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8462034e5255191499a018bd8fbbf751
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,361 @@
|
||||
|
||||
using FishNet.CodeGenerating.Extension;
|
||||
using FishNet.CodeGenerating.Helping;
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using FishNet.Serializing;
|
||||
using FishNet.Serializing.Helping;
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Cil;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.CodeGenerating.Processing
|
||||
{
|
||||
internal class CustomSerializerProcessor : CodegenBase
|
||||
{
|
||||
|
||||
#region Types.
|
||||
internal enum ExtensionType
|
||||
{
|
||||
None,
|
||||
Write,
|
||||
Read
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal bool CreateSerializerDelegates(TypeDefinition typeDef, bool replace)
|
||||
{
|
||||
bool modified = false;
|
||||
/* Find all declared methods and register delegates to them.
|
||||
* After they are all registered create any custom writers
|
||||
* needed to complete the declared methods. It's important to
|
||||
* make generated writers after so that a generated method
|
||||
* isn't made for a type when the user has already made a declared one. */
|
||||
foreach (MethodDefinition methodDef in typeDef.Methods)
|
||||
{
|
||||
ExtensionType extensionType = GetExtensionType(methodDef);
|
||||
if (extensionType == ExtensionType.None)
|
||||
continue;
|
||||
if (base.GetClass<GeneralHelper>().CodegenExclude(methodDef))
|
||||
continue;
|
||||
|
||||
MethodReference methodRef = base.ImportReference(methodDef);
|
||||
if (extensionType == ExtensionType.Write)
|
||||
{
|
||||
base.GetClass<WriterProcessor>().AddWriterMethod(methodRef.Parameters[1].ParameterType, methodRef, false, !replace);
|
||||
modified = true;
|
||||
}
|
||||
else if (extensionType == ExtensionType.Read)
|
||||
{
|
||||
base.GetClass<ReaderProcessor>().AddReaderMethod(methodRef.ReturnType, methodRef, false, !replace);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates serializers for any custom types for declared methods.
|
||||
/// </summary>
|
||||
/// <param name="declaredMethods"></param>
|
||||
/// <param name="moduleDef"></param>
|
||||
internal bool CreateSerializers(TypeDefinition typeDef)
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
List<(MethodDefinition, ExtensionType)> declaredMethods = new List<(MethodDefinition, ExtensionType)>();
|
||||
/* Go through all custom serializers again and see if
|
||||
* they use any types that the user didn't make a serializer for
|
||||
* and that there isn't a built-in type for. Create serializers
|
||||
* for these types. */
|
||||
foreach (MethodDefinition methodDef in typeDef.Methods)
|
||||
{
|
||||
ExtensionType extensionType = GetExtensionType(methodDef);
|
||||
if (extensionType == ExtensionType.None)
|
||||
continue;
|
||||
if (base.GetClass<GeneralHelper>().CodegenExclude(methodDef))
|
||||
continue;
|
||||
|
||||
declaredMethods.Add((methodDef, extensionType));
|
||||
modified = true;
|
||||
}
|
||||
//Now that all declared are loaded see if any of them need generated serializers.
|
||||
foreach ((MethodDefinition methodDef, ExtensionType extensionType) in declaredMethods)
|
||||
CreateSerializers(extensionType, methodDef);
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a custom serializer for any types not handled within users declared.
|
||||
/// </summary>
|
||||
/// <param name="extensionType"></param>
|
||||
/// <param name="moduleDef"></param>
|
||||
/// <param name="methodDef"></param>
|
||||
/// <param name="diagnostics"></param>
|
||||
private void CreateSerializers(ExtensionType extensionType, MethodDefinition methodDef)
|
||||
{
|
||||
for (int i = 0; i < methodDef.Body.Instructions.Count; i++)
|
||||
CheckToModifyInstructions(extensionType, methodDef, ref i);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates delegates for custom comparers.
|
||||
/// </summary>
|
||||
internal bool CreateComparerDelegates(TypeDefinition typeDef)
|
||||
{
|
||||
bool modified = false;
|
||||
GeneralHelper gh = base.GetClass<GeneralHelper>();
|
||||
/* Find all declared methods and register delegates to them.
|
||||
* After they are all registered create any custom writers
|
||||
* needed to complete the declared methods. It's important to
|
||||
* make generated writers after so that a generated method
|
||||
* isn't made for a type when the user has already made a declared one. */
|
||||
foreach (MethodDefinition methodDef in typeDef.Methods)
|
||||
{
|
||||
if (gh.CodegenExclude(methodDef))
|
||||
continue;
|
||||
if (!methodDef.HasCustomAttribute<CustomComparerAttribute>())
|
||||
continue;
|
||||
//Validate return type.
|
||||
if (methodDef.ReturnType.FullName != gh.GetTypeReference(typeof(bool)).FullName)
|
||||
{
|
||||
base.LogError($"Comparer method {methodDef.Name} in type {typeDef.FullName} must return bool.");
|
||||
continue;
|
||||
}
|
||||
/* Make sure parameters are correct. */
|
||||
//Invalid count.
|
||||
if (methodDef.Parameters.Count != 2)
|
||||
{
|
||||
base.LogError($"Comparer method {methodDef.Name} in type {typeDef.FullName} must have exactly two parameters, each of the same type which is being compared.");
|
||||
continue;
|
||||
}
|
||||
TypeReference p0Tr = methodDef.Parameters[0].ParameterType;
|
||||
TypeReference p1Tr = methodDef.Parameters[0].ParameterType;
|
||||
//Not the same types.
|
||||
if (p0Tr != p1Tr)
|
||||
{
|
||||
base.LogError($"Both parameters must be the same type in comparer method {methodDef.Name} in type {typeDef.FullName}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
base.ImportReference(methodDef);
|
||||
base.ImportReference(p0Tr);
|
||||
gh.RegisterComparerDelegate(methodDef, p0Tr);
|
||||
gh.CreateComparerDelegate(methodDef, p0Tr);
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if instructions need to be modified and does so.
|
||||
/// </summary>
|
||||
/// <param name="methodDef"></param>
|
||||
/// <param name="instructionIndex"></param>
|
||||
private void CheckToModifyInstructions(ExtensionType extensionType, MethodDefinition methodDef, ref int instructionIndex)
|
||||
{
|
||||
Instruction instruction = methodDef.Body.Instructions[instructionIndex];
|
||||
//Fields.
|
||||
if (instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld)
|
||||
CheckFieldReferenceInstruction(extensionType, methodDef, ref instructionIndex);
|
||||
//Method calls.
|
||||
else if (instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Callvirt)
|
||||
CheckCallInstruction(extensionType, methodDef, ref instructionIndex, (MethodReference)instruction.Operand);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a reader or writer must be generated for a field type.
|
||||
/// </summary>
|
||||
/// <param name="methodDef"></param>
|
||||
/// <param name="instructionIndex"></param>
|
||||
private void CheckFieldReferenceInstruction(ExtensionType extensionType, MethodDefinition methodDef, ref int instructionIndex)
|
||||
{
|
||||
Instruction instruction = methodDef.Body.Instructions[instructionIndex];
|
||||
FieldReference field = (FieldReference)instruction.Operand;
|
||||
TypeReference typeRef = field.DeclaringType;
|
||||
|
||||
if (typeRef.IsType(typeof(GenericWriter<>)) || typeRef.IsType(typeof(GenericReader<>)) && typeRef.IsGenericInstance)
|
||||
{
|
||||
GenericInstanceType typeGenericInst = (GenericInstanceType)typeRef;
|
||||
TypeReference parameterType = typeGenericInst.GenericArguments[0];
|
||||
CreateReaderOrWriter(extensionType, methodDef, ref instructionIndex, parameterType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a reader or writer must be generated for a call type.
|
||||
/// </summary>
|
||||
/// <param name="extensionType"></param>
|
||||
/// <param name="moduleDef"></param>
|
||||
/// <param name="methodDef"></param>
|
||||
/// <param name="instructionIndex"></param>
|
||||
/// <param name="method"></param>
|
||||
private void CheckCallInstruction(ExtensionType extensionType, MethodDefinition methodDef, ref int instructionIndex, MethodReference method)
|
||||
{
|
||||
if (!method.IsGenericInstance)
|
||||
return;
|
||||
|
||||
//True if call is to read/write.
|
||||
bool canCreate = (
|
||||
method.Is<Writer>(nameof(Writer.Write)) ||
|
||||
method.Is<Reader>(nameof(Reader.Read))
|
||||
);
|
||||
|
||||
if (canCreate)
|
||||
{
|
||||
GenericInstanceMethod instanceMethod = (GenericInstanceMethod)method;
|
||||
TypeReference parameterType = instanceMethod.GenericArguments[0];
|
||||
if (parameterType.IsGenericParameter)
|
||||
return;
|
||||
|
||||
CreateReaderOrWriter(extensionType, methodDef, ref instructionIndex, parameterType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a reader or writer for parameterType if needed. Otherwise calls existing reader.
|
||||
/// </summary>
|
||||
private void CreateReaderOrWriter(ExtensionType extensionType, MethodDefinition methodDef, ref int instructionIndex, TypeReference parameterType)
|
||||
{
|
||||
if (!parameterType.IsGenericParameter && parameterType.CanBeResolved(base.Session))
|
||||
{
|
||||
TypeDefinition typeDefinition = parameterType.CachedResolve(base.Session);
|
||||
//If class and not value type check for accessible constructor.
|
||||
if (typeDefinition.IsClass && !typeDefinition.IsValueType)
|
||||
{
|
||||
MethodDefinition constructor = typeDefinition.GetDefaultConstructor(base.Session);
|
||||
//Constructor is inaccessible, cannot create serializer for type.
|
||||
if (constructor != null && !constructor.IsPublic)
|
||||
{
|
||||
base.LogError($"Unable to generator serializers for {typeDefinition.FullName} because it's constructor is not public.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ILProcessor processor = methodDef.Body.GetILProcessor();
|
||||
|
||||
//Find already existing read or write method.
|
||||
MethodReference createdMethodRef = (extensionType == ExtensionType.Write) ?
|
||||
base.GetClass<WriterProcessor>().GetWriteMethodReference(parameterType) :
|
||||
base.GetClass<ReaderProcessor>().GetReadMethodReference(parameterType);
|
||||
|
||||
//If a created method already exist nothing further is required.
|
||||
if (createdMethodRef != null)
|
||||
{
|
||||
TryInsertAutoPack(ref instructionIndex);
|
||||
//Replace call to generic with already made serializer.
|
||||
Instruction newInstruction = processor.Create(OpCodes.Call, createdMethodRef);
|
||||
methodDef.Body.Instructions[instructionIndex] = newInstruction;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
createdMethodRef = (extensionType == ExtensionType.Write) ?
|
||||
base.GetClass<WriterProcessor>().CreateWriter(parameterType) :
|
||||
base.GetClass<ReaderProcessor>().CreateReader(parameterType);
|
||||
}
|
||||
|
||||
//If method was created.
|
||||
if (createdMethodRef != null)
|
||||
{
|
||||
TryInsertAutoPack(ref instructionIndex);
|
||||
//Set new instruction.
|
||||
Instruction newInstruction = processor.Create(OpCodes.Call, createdMethodRef);
|
||||
methodDef.Body.Instructions[instructionIndex] = newInstruction;
|
||||
}
|
||||
}
|
||||
|
||||
void TryInsertAutoPack(ref int insertIndex)
|
||||
{
|
||||
if (IsAutoPackMethod(parameterType, extensionType))
|
||||
{
|
||||
ILProcessor processor = methodDef.Body.GetILProcessor();
|
||||
AutoPackType packType = base.GetClass<GeneralHelper>().GetDefaultAutoPackType(parameterType);
|
||||
Instruction autoPack = processor.Create(OpCodes.Ldc_I4, (int)packType);
|
||||
methodDef.Body.Instructions.Insert(insertIndex, autoPack);
|
||||
insertIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if a typeRef serializer requires or uses autopacktype.
|
||||
/// </summary>
|
||||
private bool IsAutoPackMethod(TypeReference typeRef, ExtensionType extensionType)
|
||||
{
|
||||
if (extensionType == ExtensionType.Write)
|
||||
return base.GetClass<WriterProcessor>().IsAutoPackedType(typeRef);
|
||||
else if (extensionType == ExtensionType.Read)
|
||||
return base.GetClass<ReaderProcessor>().IsAutoPackedType(typeRef);
|
||||
else
|
||||
return false;
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the RPC attribute on a method, if one exist. Otherwise returns null.
|
||||
/// </summary>
|
||||
/// <param name="methodDef"></param>
|
||||
/// <returns></returns>
|
||||
private ExtensionType GetExtensionType(MethodDefinition methodDef)
|
||||
{
|
||||
bool hasExtensionAttribute = methodDef.HasCustomAttribute<System.Runtime.CompilerServices.ExtensionAttribute>();
|
||||
if (!hasExtensionAttribute)
|
||||
return ExtensionType.None;
|
||||
|
||||
bool write = (methodDef.ReturnType == methodDef.Module.TypeSystem.Void);
|
||||
|
||||
//Return None for Mirror types.
|
||||
#if MIRROR
|
||||
if (write)
|
||||
{
|
||||
if (methodDef.Parameters.Count > 0 && methodDef.Parameters[0].ParameterType.FullName == "Mirror.NetworkWriter")
|
||||
return ExtensionType.None;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (methodDef.Parameters.Count > 0 && methodDef.Parameters[0].ParameterType.FullName == "Mirror.NetworkReader")
|
||||
return ExtensionType.None;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
string prefix = (write) ? WriterProcessor.WRITE_PREFIX : ReaderProcessor.READ_PREFIX;
|
||||
|
||||
//Does not contain prefix.
|
||||
if (methodDef.Name.Length < prefix.Length || methodDef.Name.Substring(0, prefix.Length) != prefix)
|
||||
return ExtensionType.None;
|
||||
|
||||
//Make sure first parameter is right.
|
||||
if (methodDef.Parameters.Count >= 1)
|
||||
{
|
||||
TypeReference tr = methodDef.Parameters[0].ParameterType;
|
||||
if (tr.FullName != base.GetClass<WriterImports>().Writer_TypeRef.FullName &&
|
||||
tr.FullName != base.GetClass<ReaderImports>().Reader_TypeRef.FullName)
|
||||
return ExtensionType.None;
|
||||
}
|
||||
|
||||
if (write && methodDef.Parameters.Count < 2)
|
||||
{
|
||||
base.LogError($"{methodDef.FullName} must have at least two parameters, the first being PooledWriter, and second value to write.");
|
||||
return ExtensionType.None;
|
||||
}
|
||||
else if (!write && methodDef.Parameters.Count < 1)
|
||||
{
|
||||
base.LogError($"{methodDef.FullName} must have at least one parameters, the first being PooledReader.");
|
||||
return ExtensionType.None;
|
||||
}
|
||||
|
||||
return (write) ? ExtensionType.Write : ExtensionType.Read;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9269fd8a62199e24c965b4c99b641244
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,523 @@
|
||||
using FishNet.CodeGenerating.Extension;
|
||||
using FishNet.CodeGenerating.Helping;
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using FishNet.CodeGenerating.Processing.Rpc;
|
||||
using FishNet.Configuring;
|
||||
using FishNet.Object;
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Cil;
|
||||
using MonoFN.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace FishNet.CodeGenerating.Processing
|
||||
{
|
||||
internal class NetworkBehaviourProcessor : CodegenBase
|
||||
{
|
||||
#region Private.
|
||||
/// <summary>
|
||||
/// Classes which have been processed for all NetworkBehaviour features.
|
||||
/// </summary>
|
||||
private HashSet<TypeDefinition> _processedClasses = new HashSet<TypeDefinition>();
|
||||
#endregion
|
||||
|
||||
#region Const.
|
||||
internal const string EARLY_INITIALIZED_NAME = "NetworkInitializeEarly_";
|
||||
internal const string LATE_INITIALIZED_NAME = "NetworkInitializeLate_";
|
||||
internal const string NETWORKINITIALIZE_EARLY_INTERNAL_NAME = "NetworkInitialize___Early";
|
||||
internal const string NETWORKINITIALIZE_LATE_INTERNAL_NAME = "NetworkInitialize__Late";
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <param name="processedSyncs">SyncTypes processed for typeDef and inherited.</param>
|
||||
/// <returns></returns>
|
||||
internal bool ProcessLocal(TypeDefinition typeDef, List<(SyncType, ProcessedSync)> processedSyncs)
|
||||
{
|
||||
bool modified = false;
|
||||
TypeDefinition copyTypeDef = typeDef;
|
||||
|
||||
//TypeDefs which are using prediction.
|
||||
List<TypeDefinition> _usesPredictionTypeDefs = new List<TypeDefinition>();
|
||||
|
||||
//Make collection of NBs to processor.
|
||||
List<TypeDefinition> typeDefs = new List<TypeDefinition>();
|
||||
do
|
||||
{
|
||||
if (!HasClassBeenProcessed(copyTypeDef))
|
||||
{
|
||||
//Disallow nested network behaviours.
|
||||
ICollection<TypeDefinition> nestedTds = copyTypeDef.NestedTypes;
|
||||
foreach (TypeDefinition item in nestedTds)
|
||||
{
|
||||
if (item.InheritsNetworkBehaviour(base.Session))
|
||||
{
|
||||
base.LogError($"{copyTypeDef.FullName} contains nested NetworkBehaviours. These are not supported.");
|
||||
return modified;
|
||||
}
|
||||
}
|
||||
|
||||
typeDefs.Add(copyTypeDef);
|
||||
}
|
||||
copyTypeDef = TypeDefinitionExtensionsOld.GetNextBaseClassToProcess(copyTypeDef, base.Session);
|
||||
} while (copyTypeDef != null);
|
||||
|
||||
/* Reverse type definitions so that the parent
|
||||
* is first. This counts indexes up as we go further
|
||||
* down the children. By doing so we do not have to
|
||||
* rebuild rpc or synctype indexes when a parent is inherited
|
||||
* multiple times. EG: with this solution if parent had 1 sync type
|
||||
* and childA had 2 the parent would be index 0, and childA would have 1 and 2.
|
||||
* But also, if childB inherited childA it would have 3+.
|
||||
*
|
||||
* Going in reverse also gaurantees the awake method will already be created
|
||||
* or modified in any class a child inherits. This lets us call it appropriately
|
||||
* as well error if the awake does not exist, such as could not be created. */
|
||||
typeDefs.Reverse();
|
||||
|
||||
foreach (TypeDefinition td in typeDefs)
|
||||
{
|
||||
/* Create NetworkInitialize before-hand so the other procesors
|
||||
* can use it. */
|
||||
MethodDefinition networkInitializeIfDisabledMd;
|
||||
CreateNetworkInitializeMethods(td, out networkInitializeIfDisabledMd);
|
||||
CallNetworkInitializesFromNetworkInitializeIfDisabled(networkInitializeIfDisabledMd);
|
||||
|
||||
|
||||
|
||||
/* Prediction. */
|
||||
/* Run prediction first since prediction will modify
|
||||
* user data passed into prediction methods. Because of this
|
||||
* other RPCs should use the modified version and reader/writers
|
||||
* made for prediction. */
|
||||
if (base.GetClass<PredictionProcessor>().Process(td))
|
||||
{
|
||||
_usesPredictionTypeDefs.Add(td);
|
||||
modified = true;
|
||||
}
|
||||
//25ms
|
||||
|
||||
/* RPCs. */
|
||||
modified |= base.GetClass<RpcProcessor>().ProcessLocal(td);
|
||||
//30ms
|
||||
/* //perf rpcCounts can be optimized by having different counts
|
||||
* for target, observers, server, replicate, and reoncile rpcs. Since
|
||||
* each registers to their own delegates this is possible. */
|
||||
|
||||
/* SyncTypes. */
|
||||
modified |= base.GetClass<NetworkBehaviourSyncProcessor>().ProcessLocal(td, processedSyncs);
|
||||
|
||||
//Call base networkinitialize early/late.
|
||||
CallBaseOnNetworkInitializeMethods(td);
|
||||
//Add networkinitialize executed check to early/late.
|
||||
AddNetworkInitializeExecutedChecks(td);
|
||||
|
||||
//Copy user logic from awake into a new method.
|
||||
CopyAwakeUserLogic(td);
|
||||
/* Create awake method or if already exist make
|
||||
* it public virtual. */
|
||||
if (!ModifyAwakeMethod(td, out bool awakeCreated))
|
||||
{
|
||||
//This is a hard fail and will break the solution so throw here.
|
||||
base.LogError($"{td.FullName} has an Awake method which could not be modified, or could not be found. This often occurs when a child class is in an assembly different from the parent, and the parent does not implement Awake. To resolve this make an Awake in {td.Name} public virtual.");
|
||||
return modified;
|
||||
}
|
||||
|
||||
//Calls NetworkInitializeEarly from awake.
|
||||
CallMethodFromAwake(td, NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
|
||||
//Only call base if awake was created. Otherwise let the users implementation handle base calling.
|
||||
if (awakeCreated)
|
||||
CallBaseAwake(td);
|
||||
//Call logic user may have put in awake.
|
||||
CallAwakeUserLogic(td);
|
||||
//NetworkInitializeLate from awake.
|
||||
CallMethodFromAwake(td, NETWORKINITIALIZE_LATE_INTERNAL_NAME);
|
||||
//Since awake methods are erased ret has to be added at the end.
|
||||
AddReturnToAwake(td);
|
||||
|
||||
//70ms
|
||||
_processedClasses.Add(td);
|
||||
}
|
||||
|
||||
if (processedSyncs.Count > NetworkBehaviourHelper.MAX_SYNCTYPE_ALLOWANCE)
|
||||
{
|
||||
base.LogError($"Found {processedSyncs.Count} SyncTypes within {typeDef.FullName} and inherited classes. The maximum number of allowed SyncTypes within type and inherited types is {NetworkBehaviourHelper.MAX_SYNCTYPE_ALLOWANCE}. Remove SyncTypes or condense them using data containers, or a custom SyncObject.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If here then all inerited classes for firstTypeDef have
|
||||
* been processed. */
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name to use for user awake logic method.
|
||||
/// </summary>
|
||||
internal string GetAwakeUserLogicMethodDefinition(TypeDefinition td) => $"Awake_UserLogic_{td.FullName}_{base.Module.Name}";
|
||||
|
||||
/// <summary>
|
||||
/// Returns if a class has been processed.
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
/// <returns></returns>
|
||||
private bool HasClassBeenProcessed(TypeDefinition typeDef)
|
||||
{
|
||||
return _processedClasses.Contains(typeDef);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if any typeDefs have attributes which are not allowed to be used outside NetworkBehaviour.
|
||||
/// </summary>
|
||||
/// <param name="typeDefs"></param>
|
||||
/// <returns></returns>
|
||||
internal bool NonNetworkBehaviourHasInvalidAttributes(Collection<TypeDefinition> typeDefs)
|
||||
{
|
||||
NetworkBehaviourSyncProcessor nbSyncProcessor = base.GetClass<NetworkBehaviourSyncProcessor>();
|
||||
RpcProcessor rpcProcessor = base.GetClass<RpcProcessor>();
|
||||
|
||||
foreach (TypeDefinition typeDef in typeDefs)
|
||||
{
|
||||
//Inherits, don't need to check.
|
||||
if (typeDef.InheritsNetworkBehaviour(base.Session))
|
||||
continue;
|
||||
|
||||
//Check each method for attribute.
|
||||
foreach (MethodDefinition md in typeDef.Methods)
|
||||
{
|
||||
//Has RPC attribute but doesn't inherit from NB.
|
||||
if (rpcProcessor.Attributes.HasRpcAttributes(md))
|
||||
{
|
||||
base.LogError($"{typeDef.FullName} has one or more RPC attributes but does not inherit from NetworkBehaviour.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//Check fields for attribute.
|
||||
foreach (FieldDefinition fd in typeDef.Fields)
|
||||
{
|
||||
if (nbSyncProcessor.IsSyncType(fd))
|
||||
{
|
||||
base.LogError($"{typeDef.FullName} implements one or more SyncTypes but does not inherit from NetworkBehaviour.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Fallthrough / pass.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calls the next awake method if the nested awake was created by codegen.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private void CallBaseAwake(TypeDefinition td)
|
||||
{
|
||||
/* If base is not a class which can be processed then there
|
||||
* is no need to continue. */
|
||||
if (!td.CanProcessBaseType(base.Session))
|
||||
return;
|
||||
|
||||
MethodReference baseAwakeMr = td.GetMethodReferenceInBase(base.Session, NetworkBehaviourHelper.AWAKE_METHOD_NAME);
|
||||
//This Awake.
|
||||
MethodDefinition tdAwakeMd = td.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
|
||||
|
||||
ILProcessor processor = tdAwakeMd.Body.GetILProcessor();
|
||||
processor.Emit(OpCodes.Ldarg_0); //base.
|
||||
processor.Emit(OpCodes.Call, baseAwakeMr);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calls the next awake method if the nested awake was created by codegen.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private void CallAwakeUserLogic(TypeDefinition td)
|
||||
{
|
||||
//UserLogic.
|
||||
MethodDefinition userLogicMd = td.GetMethod(GetAwakeUserLogicMethodDefinition(td));
|
||||
/* Userlogic may be null if Awake was created.
|
||||
* If so, there's no need to proceed. */
|
||||
if (userLogicMd == null)
|
||||
return;
|
||||
|
||||
//This Awake.
|
||||
MethodDefinition awakeMd = td.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
|
||||
//Call logic.
|
||||
base.GetClass<GeneralHelper>().CallCopiedMethod(awakeMd, userLogicMd);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a check to NetworkInitialize to see if it has already run.
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
private void AddNetworkInitializeExecutedChecks(TypeDefinition typeDef)
|
||||
{
|
||||
AddCheck(NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
|
||||
AddCheck(NETWORKINITIALIZE_LATE_INTERNAL_NAME);
|
||||
|
||||
void AddCheck(string methodName)
|
||||
{
|
||||
string fieldName = $"{methodName}{typeDef.FullName}{typeDef.Module.Name}_Excuted";
|
||||
MethodDefinition md = typeDef.GetMethod(methodName);
|
||||
if (md == null)
|
||||
return;
|
||||
|
||||
TypeReference boolTr = base.GetClass<GeneralHelper>().GetTypeReference(typeof(bool));
|
||||
FieldReference fr = typeDef.GetOrCreateFieldReference(base.Session, fieldName, FieldAttributes.Private, boolTr, out bool created);
|
||||
|
||||
if (created)
|
||||
{
|
||||
List<Instruction> insts = new List<Instruction>();
|
||||
ILProcessor processor = md.Body.GetILProcessor();
|
||||
//Add check if already called.
|
||||
//if (alreadyInitialized) return;
|
||||
Instruction skipFirstRetInst = processor.Create(OpCodes.Nop);
|
||||
insts.Add(processor.Create(OpCodes.Ldarg_0));
|
||||
insts.Add(processor.Create(OpCodes.Ldfld, fr));
|
||||
insts.Add(processor.Create(OpCodes.Brfalse_S, skipFirstRetInst));
|
||||
insts.Add(processor.Create(OpCodes.Ret));
|
||||
insts.Add(skipFirstRetInst);
|
||||
//Set field to true.
|
||||
insts.Add(processor.Create(OpCodes.Ldarg_0));
|
||||
insts.Add(processor.Create(OpCodes.Ldc_I4_1));
|
||||
insts.Add(processor.Create(OpCodes.Stfld, fr));
|
||||
processor.InsertFirst(insts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls base for NetworkInitializeEarly/Late on a TypeDefinition.
|
||||
/// </summary>
|
||||
private void CallBaseOnNetworkInitializeMethods(TypeDefinition typeDef)
|
||||
{
|
||||
//If base class cannot have a networkinitialize no reason to continue.
|
||||
if (!typeDef.CanProcessBaseType(base.Session))
|
||||
return;
|
||||
|
||||
string[] initializeMethodNames = new string[] { NETWORKINITIALIZE_EARLY_INTERNAL_NAME, NETWORKINITIALIZE_LATE_INTERNAL_NAME };
|
||||
foreach (string mdName in initializeMethodNames)
|
||||
{
|
||||
/* Awake will always exist because it was added previously.
|
||||
* Get awake for copy and base of copy. */
|
||||
MethodDefinition thisMd = typeDef.GetMethod(mdName);
|
||||
ILProcessor processor = thisMd.Body.GetILProcessor();
|
||||
|
||||
/* Awake will always exist because it was added previously.
|
||||
* Get awake for copy and base of copy. */
|
||||
MethodReference baseMr = typeDef.GetMethodReferenceInBase(base.Session, mdName);
|
||||
MethodDefinition baseMd = baseMr.CachedResolve(base.Session);
|
||||
|
||||
bool alreadyHasBaseCall = false;
|
||||
//Check if already calls baseAwake.
|
||||
foreach (Instruction item in thisMd.Body.Instructions)
|
||||
{
|
||||
//If a call or call virt. Although, callvirt should never occur.
|
||||
if (item.OpCode == OpCodes.Call || item.OpCode == OpCodes.Callvirt)
|
||||
{
|
||||
if (item.Operand != null && item.Operand.GetType().Name == nameof(MethodDefinition))
|
||||
{
|
||||
MethodDefinition md = (MethodDefinition)item.Operand;
|
||||
if (md == baseMd)
|
||||
{
|
||||
alreadyHasBaseCall = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!alreadyHasBaseCall)
|
||||
{
|
||||
//Create instructions for base call.
|
||||
List<Instruction> instructions = new List<Instruction>
|
||||
{
|
||||
processor.Create(OpCodes.Ldarg_0), //this.
|
||||
processor.Create(OpCodes.Call, baseMr)
|
||||
};
|
||||
processor.InsertFirst(instructions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds returns awake method definitions within awakeDatas.
|
||||
/// </summary>
|
||||
private void AddReturnToAwake(TypeDefinition td)
|
||||
{
|
||||
//This Awake.
|
||||
MethodDefinition awakeMd = td.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
|
||||
ILProcessor processor = awakeMd.Body.GetILProcessor();
|
||||
//If no instructions or the last instruction isnt ret.
|
||||
if (processor.Body.Instructions.Count == 0
|
||||
|| processor.Body.Instructions[processor.Body.Instructions.Count - 1].OpCode != OpCodes.Ret)
|
||||
{
|
||||
processor.Emit(OpCodes.Ret);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls a method by name from awake.
|
||||
/// </summary>
|
||||
private void CallMethodFromAwake(TypeDefinition typeDef, string methodName)
|
||||
{
|
||||
//Will never be null because we added it previously.
|
||||
MethodDefinition awakeMethodDef = typeDef.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
|
||||
MethodReference networkInitMr = typeDef.GetMethodReference(base.Session, methodName);
|
||||
|
||||
ILProcessor processor = awakeMethodDef.Body.GetILProcessor();
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(networkInitMr.GetCallOpCode(base.Session), networkInitMr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an 'NetworkInitialize' method which is called by the childmost class to initialize scripts on Awake.
|
||||
/// </summary>
|
||||
private void CreateNetworkInitializeMethods(TypeDefinition typeDef, out MethodDefinition networkInitializeIfDisabledMd)
|
||||
{
|
||||
CreateMethod(NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
|
||||
CreateMethod(NETWORKINITIALIZE_LATE_INTERNAL_NAME);
|
||||
networkInitializeIfDisabledMd = CreateMethod(nameof(NetworkBehaviour.NetworkInitializeIfDisabled));
|
||||
|
||||
MethodDefinition CreateMethod(string name, MethodDefinition copied = null)
|
||||
{
|
||||
bool created;
|
||||
MethodDefinition md = typeDef.GetOrCreateMethodDefinition(base.Session, name, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES, typeDef.Module.TypeSystem.Void, out created);
|
||||
|
||||
if (created)
|
||||
{
|
||||
//Emit ret into new method.
|
||||
ILProcessor processor = md.Body.GetILProcessor();
|
||||
//End of method return.
|
||||
processor.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
return md;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates an 'NetworkInitialize' method which is called by the childmost class to initialize scripts on Awake.
|
||||
/// </summary>
|
||||
private void CallNetworkInitializesFromNetworkInitializeIfDisabled(MethodDefinition networkInitializeIfDisabledMd)
|
||||
{
|
||||
ILProcessor processor = networkInitializeIfDisabledMd.Body.GetILProcessor();
|
||||
|
||||
networkInitializeIfDisabledMd.Body.Instructions.Clear();
|
||||
CallMethod(NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
|
||||
CallMethod(NETWORKINITIALIZE_LATE_INTERNAL_NAME);
|
||||
processor.Emit(OpCodes.Ret);
|
||||
|
||||
void CallMethod(string name)
|
||||
{
|
||||
MethodReference initIfDisabledMr = networkInitializeIfDisabledMd.DeclaringType.GetMethodReference(base.Session, name);
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(initIfDisabledMr.GetCallOpCode(base.Session), initIfDisabledMr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies logic from users Awake if present, to a new method.
|
||||
/// </summary>
|
||||
private void CopyAwakeUserLogic(TypeDefinition typeDef)
|
||||
{
|
||||
MethodDefinition awakeMd = typeDef.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
|
||||
//If found copy.
|
||||
if (awakeMd != null)
|
||||
base.GetClass<GeneralHelper>().CopyIntoNewMethod(awakeMd, GetAwakeUserLogicMethodDefinition(typeDef), out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Erases content in awake if it already exist, otherwise makes a new Awake.
|
||||
/// Makes Awake public and virtual.
|
||||
/// </summary>
|
||||
/// <returns>True if successful.</returns>
|
||||
private bool ModifyAwakeMethod(TypeDefinition typeDef, out bool created)
|
||||
{
|
||||
MethodDefinition awakeMd = typeDef.GetOrCreateMethodDefinition(base.Session, NetworkBehaviourHelper.AWAKE_METHOD_NAME, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES, typeDef.Module.TypeSystem.Void, out created);
|
||||
|
||||
//Awake is found. Check for invalid return type.
|
||||
if (!created)
|
||||
{
|
||||
if (awakeMd.ReturnType != typeDef.Module.TypeSystem.Void)
|
||||
{
|
||||
base.LogError($"IEnumerator Awake methods are not supported within NetworkBehaviours.");
|
||||
return false;
|
||||
}
|
||||
//Make public if good.
|
||||
awakeMd.SetPublicAttributes();
|
||||
}
|
||||
//Already was made.
|
||||
else
|
||||
{
|
||||
ILProcessor processor = awakeMd.Body.GetILProcessor();
|
||||
processor.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
//Clear original awake.
|
||||
awakeMd.Body.Instructions.Clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes all Awake methods within typeDef and base classes public and virtual.
|
||||
/// </summary>
|
||||
/// <param name="typeDef"></param>
|
||||
internal void CreateFirstNetworkInitializeCall(TypeDefinition typeDef, MethodDefinition firstUserAwakeMethodDef, MethodDefinition firstNetworkInitializeMethodDef)
|
||||
{
|
||||
ILProcessor processor;
|
||||
//Get awake for current method.
|
||||
MethodDefinition thisAwakeMethodDef = typeDef.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
|
||||
bool created = false;
|
||||
|
||||
//If no awake then make one.
|
||||
if (thisAwakeMethodDef == null)
|
||||
{
|
||||
created = true;
|
||||
|
||||
thisAwakeMethodDef = new MethodDefinition(NetworkBehaviourHelper.AWAKE_METHOD_NAME, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES,
|
||||
typeDef.Module.TypeSystem.Void);
|
||||
thisAwakeMethodDef.Body.InitLocals = true;
|
||||
typeDef.Methods.Add(thisAwakeMethodDef);
|
||||
|
||||
processor = thisAwakeMethodDef.Body.GetILProcessor();
|
||||
processor.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
//MethodRefs for networkinitialize and awake.
|
||||
MethodReference networkInitializeMethodRef = typeDef.Module.ImportReference(firstNetworkInitializeMethodDef);
|
||||
|
||||
processor = thisAwakeMethodDef.Body.GetILProcessor();
|
||||
//Create instructions for base call.
|
||||
List<Instruction> instructions = new List<Instruction>
|
||||
{
|
||||
processor.Create(OpCodes.Ldarg_0), //this.
|
||||
processor.Create(OpCodes.Call, networkInitializeMethodRef)
|
||||
};
|
||||
|
||||
/* If awake was created then make a call to the users
|
||||
* first awake. There's no reason to do this if awake
|
||||
* already existed because the user would have control
|
||||
* over making that call. */
|
||||
if (created && firstUserAwakeMethodDef != null)
|
||||
{
|
||||
MethodReference baseAwakeMethodRef = typeDef.Module.ImportReference(firstUserAwakeMethodDef);
|
||||
instructions.Add(processor.Create(OpCodes.Ldarg_0));//this.
|
||||
instructions.Add(processor.Create(OpCodes.Call, baseAwakeMethodRef));
|
||||
}
|
||||
|
||||
processor.InsertFirst(instructions);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 23866e4d620216745a837fa99e801164
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec95af37f78b9e340b5eaa199c1af94a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
<<<<<<<< HEAD:Assets/FishNet/CodeGenerating/Processing/Prediction.meta
|
||||
guid: 98f6937bb72a7254b92b4656f28b7333
|
||||
folderAsset: yes
|
||||
========
|
||||
guid: 89b00a3741f72ac4e861a08cf1202228
|
||||
>>>>>>>> origin/3-pre-3.1:Assets/Test/InitializeOrder/InitializeOrder_Test.unity.meta
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0663606e86b0b34bae85df164747e72
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user