feat: Added FishNet plugin to project and began implementing/testing online multiplayer
This commit is contained in:
parent
6d89d9d48e
commit
6d83c876de
26
Assets/DefaultPrefabObjects.asset
Normal file
26
Assets/DefaultPrefabObjects.asset
Normal file
@ -0,0 +1,26 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3ad70174b079c2f4ebc7931d3dd1af6f, type: 3}
|
||||
m_Name: DefaultPrefabObjects
|
||||
m_EditorClassIdentifier:
|
||||
_prefabs:
|
||||
- {fileID: 4320456058255827552, guid: 65cd4fa5e050652409dd9b062365c7e8, type: 3}
|
||||
- {fileID: 394654179909484550, guid: 46f84e56340e83e4ba7c6abbccd2ae3f, type: 3}
|
||||
- {fileID: 8475222101369129519, guid: 8cf33e8e99a9b0c4c8f29ff725650de6, type: 3}
|
||||
- {fileID: 4512293259955182956, guid: 44611030e61220d42ab7c37ba3c0ea92, type: 3}
|
||||
- {fileID: 4512293259955182956, guid: 0d6d0f48b03b17f49a6340103cd9b9d0, type: 3}
|
||||
- {fileID: 4512293259955182956, guid: f32d4c19de900e64cb73cedcb8ba6f70, type: 3}
|
||||
- {fileID: 4512293259955182956, guid: 300370bdf7819da41937e0beac65b32c, type: 3}
|
||||
- {fileID: 611616139817875448, guid: bf5f023b4017a5e41a9815ec5745df3d, type: 3}
|
||||
- {fileID: 201277550, guid: 5b712878ecece354ba4ffb026c0a221c, type: 3}
|
||||
- {fileID: 201277550, guid: 8ef354bfc16ca8a459074c3fa9b6727e, type: 3}
|
||||
- {fileID: 8192566354860284824, guid: 6331b3542e64a564c81bc39cedf70c8d, type: 3}
|
||||
8
Assets/DefaultPrefabObjects.asset.meta
Normal file
8
Assets/DefaultPrefabObjects.asset.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb50798712a8fcd40b22abbc470201fb
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Editor.meta
Normal file
8
Assets/Editor.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 38a7f445babd57442af500242aa934ed
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/FishNet.meta
Normal file
8
Assets/FishNet.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1525dbf8ebd59e438b504fa19c4fd6c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/FishNet/CodeGenerating.meta
Normal file
8
Assets/FishNet/CodeGenerating.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a03c3ea914b8ed649a14606e1430e404
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/FishNet/CodeGenerating/Extension.meta
Normal file
8
Assets/FishNet/CodeGenerating/Extension.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2e223f21744b544bbb7e93020199efb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,22 @@
|
||||
using MonoFN.Cecil;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping.Extension
|
||||
{
|
||||
|
||||
internal static class FieldDefinitionExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Makes a FieldDefinition generic if it has generic parameters.
|
||||
/// </summary>
|
||||
public static FieldReference TryMakeGenericInstance(this FieldDefinition fd, CodegenSession session)
|
||||
{
|
||||
FieldReference fr = session.ImportReference(fd);
|
||||
return fr.TryMakeGenericInstance();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de84fa9739ed55945b58147a773e1740
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,40 @@
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes a FieldReference generic if it has generic parameters.
|
||||
/// </summary>
|
||||
public static FieldReference TryMakeGenericInstance(this FieldReference fr)
|
||||
{
|
||||
TypeReference declaringTr = fr.DeclaringType;
|
||||
|
||||
if (declaringTr.HasGenericParameters)
|
||||
{
|
||||
GenericInstanceType git = declaringTr.MakeGenericInstanceType();
|
||||
FieldReference result = new FieldReference(fr.Name, fr.FieldType, git);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return fr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d983ebd0c9e1b745902030c2f7a8e99
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
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,235 @@
|
||||
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 a parameter and returns added parameters.
|
||||
/// </summary>
|
||||
public static ParameterDefinition CreateParameter(this MethodDefinition thisMr, CodegenSession session, ParameterAttributes attr, System.Type type)
|
||||
{
|
||||
TypeReference parameterTypeRef = session.ImportReference(type);
|
||||
ParameterDefinition pd = new ParameterDefinition($"p{thisMr.Parameters.Count}", attr, parameterTypeRef);
|
||||
thisMr.Parameters.Add(pd);
|
||||
return pd;
|
||||
}
|
||||
/// <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>
|
||||
/// Removes ret if it exist at the end of the method. Returns if ret was removed.
|
||||
/// </summary>
|
||||
internal static bool RemoveEndRet(this MethodDefinition md, CodegenSession session)
|
||||
{
|
||||
int count = md.Body.Instructions.Count;
|
||||
if (count > 0 && md.Body.Instructions[count - 1].OpCode == OpCodes.Ret)
|
||||
{
|
||||
md.Body.Instructions.RemoveAt(count - 1);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <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,315 @@
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using FishNet.Object.Prediction;
|
||||
using FishNet.Utility.Performance;
|
||||
using MonoFN.Cecil;
|
||||
using System.Collections.Generic;
|
||||
|
||||
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)
|
||||
{
|
||||
session.ImportReference(pd.ParameterType.CachedResolve(session));
|
||||
md.Parameters.Add(pd);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (GenericParameter item in methodTemplate.GenericParameters)
|
||||
{
|
||||
session.ImportReference(item);
|
||||
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 a TypeDefintiion found in typeDef or up it's hierarchy.
|
||||
/// </summary>
|
||||
/// <param name="checkTypeDef">True to check if typeDef equals fullName.</param>
|
||||
/// <returns></returns>
|
||||
public static TypeDefinition GetTypeDefinitionInBase(this TypeDefinition typeDef, CodegenSession session, string targetFullName, bool checkTypeDef)
|
||||
{
|
||||
if (typeDef == null)
|
||||
return null;
|
||||
if (!checkTypeDef)
|
||||
typeDef = typeDef.GetNextBaseTypeDefinition(session);
|
||||
|
||||
while (typeDef != null)
|
||||
{
|
||||
if (typeDef.FullName == targetFullName)
|
||||
return typeDef;
|
||||
|
||||
typeDef = typeDef.GetNextBaseTypeDefinition(session);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Makes a GenericInstanceType.
|
||||
/// </summary>
|
||||
public static GenericInstanceType MakeGenericInstanceType(this TypeDefinition self, CodegenSession session)
|
||||
{
|
||||
TypeReference tr = session.ImportReference(self);
|
||||
return tr.MakeGenericInstanceType();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9f00cf3dc8b90b469c3c9cb8b87fc88
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,52 @@
|
||||
|
||||
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:
|
||||
9
Assets/FishNet/CodeGenerating/FN_README.txt
Normal file
9
Assets/FishNet/CodeGenerating/FN_README.txt
Normal file
@ -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.
|
||||
7
Assets/FishNet/CodeGenerating/FN_README.txt.meta
Normal file
7
Assets/FishNet/CodeGenerating/FN_README.txt.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9133eb285bd7f3c4f89f4d7a2a079c6b
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/FishNet/CodeGenerating/Helpers.meta
Normal file
8
Assets/FishNet/CodeGenerating/Helpers.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d27bb367daea26d46a8fa5f6ca02865e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
74
Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs
Normal file
74
Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs
Normal file
@ -0,0 +1,74 @@
|
||||
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;
|
||||
#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;
|
||||
#if PREDICTION_1
|
||||
ReplicateAttribute_FullName = typeof(ReplicateAttribute).FullName;
|
||||
ReconcileAttribute_FullName = typeof(ReconcileAttribute).FullName;
|
||||
#else
|
||||
ReplicateAttribute_FullName = typeof(ReplicateAttribute).FullName;
|
||||
ReconcileAttribute_FullName = typeof(ReconcileAttribute).FullName;
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d32f3dc23b55598429c5cfe6156e6243
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
184
Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs
Normal file
184
Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs
Normal file
@ -0,0 +1,184 @@
|
||||
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 TransportHelper()
|
||||
, new NetworkConnectionImports(), new PredictedObjectHelper(), new GeneratorHelper()
|
||||
, new CustomSerializerProcessor()
|
||||
, new NetworkBehaviourProcessor()
|
||||
, new QolAttributeProcessor()
|
||||
, new RpcProcessor()
|
||||
, new SyncTypeProcessor()
|
||||
, 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)
|
||||
{
|
||||
Diagnostics.AddWarning(msg);
|
||||
}
|
||||
/// <summary>
|
||||
/// Logs an error.
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
internal void LogError(string msg)
|
||||
{
|
||||
Diagnostics.AddError(msg);
|
||||
}
|
||||
#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
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
11
Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs.meta
Normal file
11
Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e8416eee3308f54fa942003de975420
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
120
Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs
Normal file
120
Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs
Normal file
@ -0,0 +1,120 @@
|
||||
//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, DeclaredSyncType> _createdSyncVars = new Dictionary<string, DeclaredSyncType>();
|
||||
|
||||
// #region Relfection references.
|
||||
// private TypeReference _syncBase_TypeRef;
|
||||
// internal TypeReference SyncVar_TypeRef;
|
||||
// private MethodReference _syncVar_InitializeOnce_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()
|
||||
// {
|
||||
// Type svType = typeof(SyncVar<>);
|
||||
// SyncVar_TypeRef = base.ImportReference(svType);
|
||||
// _syncVar_InitializeOnce_MethodRef = base.ImportReference(svType.GetMethod(nameof(SyncVar<int>.InitializeOnce)));
|
||||
// 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 DeclaredSyncType 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 DeclaredSyncType createdSyncVar))
|
||||
// {
|
||||
// return createdSyncVar;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (!createMissing)
|
||||
// return null;
|
||||
|
||||
// base.ImportReference(dataTd);
|
||||
|
||||
// GenericInstanceType originalFdGit = SyncVar_TypeRef.MakeGenericInstanceType(new TypeReference[] { dataTr });
|
||||
// TypeReference genericDataTr = originalFdGit.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 initializeOnceMrGit = _syncVar_InitializeOnce_MethodRef.MakeHostInstanceGeneric(base.Session, originalFdGit);
|
||||
|
||||
// if (!base.GetClass<SyncTypeProcessor>().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, originalFdGit);
|
||||
// }
|
||||
// //SetValue.
|
||||
// else if (md.Name == SETVALUE_NAME)
|
||||
// {
|
||||
// MethodReference mr = base.ImportReference(md);
|
||||
// setValueMr = mr.MakeHostInstanceGeneric(base.Session, originalFdGit);
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (setValueMr == null || getValueMr == null)
|
||||
// return null;
|
||||
|
||||
// DeclaredSyncType csv = new DeclaredSyncType(originalFd, originalFdGit, dataTd, initializeOnceMrGit, null);
|
||||
// _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:
|
||||
8
Assets/FishNet/CodeGenerating/Helpers/Extension.meta
Normal file
8
Assets/FishNet/CodeGenerating/Helpers/Extension.meta
Normal file
@ -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,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,198 @@
|
||||
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 List<Instruction> DebugLog(this ILProcessor processor, CodegenSession session, string txt)
|
||||
{
|
||||
List<Instruction> insts = new List<Instruction>();
|
||||
insts.Add(processor.Create(OpCodes.Ldstr, txt));
|
||||
insts.Add(processor.Create(OpCodes.Call, session.GetClass<GeneralHelper>().Debug_LogCommon_MethodRef));
|
||||
return insts;
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a debug log for vd without any conditions.
|
||||
/// </summary>
|
||||
public static List<Instruction> DebugLog(this ILProcessor processor, CodegenSession session, VariableDefinition vd)
|
||||
{
|
||||
List<Instruction> insts = new List<Instruction>();
|
||||
insts.Add(processor.Create(OpCodes.Ldloc, vd));
|
||||
insts.Add(processor.Create(OpCodes.Box, vd.VariableType));
|
||||
insts.Add(processor.Create(OpCodes.Call, session.GetClass<GeneralHelper>().Debug_LogCommon_MethodRef));
|
||||
return insts;
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a debug log for vd without any conditions.
|
||||
/// </summary>
|
||||
public static List<Instruction> DebugLog(this ILProcessor processor, CodegenSession session, FieldDefinition fd, bool loadArg0)
|
||||
{
|
||||
List<Instruction> insts = new List<Instruction>();
|
||||
if (loadArg0)
|
||||
insts.Add(processor.Create(OpCodes.Ldarg_0));
|
||||
insts.Add(processor.Create(OpCodes.Ldfld, fd));
|
||||
insts.Add(processor.Create(OpCodes.Box, fd.FieldType));
|
||||
insts.Add(processor.Create(OpCodes.Call, session.GetClass<GeneralHelper>().Debug_LogCommon_MethodRef));
|
||||
return insts;
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a debug log for pd without any conditions.
|
||||
/// </summary>
|
||||
public static List<Instruction> DebugLog(this ILProcessor processor, CodegenSession session, ParameterDefinition pd)
|
||||
{
|
||||
List<Instruction> insts = new List<Instruction>();
|
||||
insts.Add(processor.Create(OpCodes.Ldloc, pd));
|
||||
insts.Add(processor.Create(OpCodes.Box, pd.ParameterType));
|
||||
insts.Add(processor.Create(OpCodes.Call, session.GetClass<GeneralHelper>().Debug_LogCommon_MethodRef));
|
||||
return insts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes Ret if the last instruction.
|
||||
/// </summary>
|
||||
/// <returns>True if ret was removed.</returns>
|
||||
public static bool RemoveEndRet(this ILProcessor processor)
|
||||
{
|
||||
int instCount = processor.Body.Instructions.Count;
|
||||
if (instCount == 0)
|
||||
return false;
|
||||
|
||||
if (processor.Body.Instructions[instCount-1].OpCode == OpCodes.Ret)
|
||||
{
|
||||
processor.Body.Instructions.RemoveAt(instCount - 1);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
///// <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,174 @@
|
||||
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>
|
||||
/// Removes ret if it exist at the end of the method. Returns if ret was removed.
|
||||
/// </summary>
|
||||
internal static bool RemoveEndRet(this MethodReference mr, CodegenSession session)
|
||||
{
|
||||
MethodDefinition md = mr.CachedResolve(session);
|
||||
return MethodDefinitionExtensions.RemoveEndRet(md, session);
|
||||
}
|
||||
/// <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.HasExcludeSerializationAttribute(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.HasExcludeSerializationAttribute(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:
|
||||
1353
Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs
Normal file
1353
Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs.meta
Normal file
11
Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6139ff104f3c24442b26dbc4e40d5ce5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
507
Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs
Normal file
507
Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs
Normal file
@ -0,0 +1,507 @@
|
||||
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.
|
||||
|
||||
#if PREDICTION_1
|
||||
public string ClearReplicateCache_MethodName = nameof(NetworkBehaviour.ClearReplicateCache_Virtual);
|
||||
#else
|
||||
public string Reconcile_Client_Start_MethodName = nameof(NetworkBehaviour.Reconcile_Client_Start);
|
||||
public string Replicate_Replay_Start_MethodName = nameof(NetworkBehaviour.Replicate_Replay_Start);
|
||||
public MethodReference Replicate_NonAuthoritative_MethodRef;
|
||||
public MethodReference Replicate_Authortative_MethodRef;
|
||||
#endif
|
||||
#if PREDICTION_1
|
||||
public MethodReference Replicate_Owner_MethodRef;
|
||||
public MethodReference Replicate_NonOwner_MethodRef;
|
||||
#endif
|
||||
public MethodReference Replicate_Reader_MethodRef;
|
||||
#if PREDICTION_1
|
||||
public MethodReference Replicate_ExitEarly_A_MethodRef;
|
||||
public MethodReference Reconcile_ExitEarly_A_MethodRef;
|
||||
#endif
|
||||
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 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);
|
||||
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_Server))
|
||||
Reconcile_Server_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Client))
|
||||
Reconcile_Client_MethodRef = base.ImportReference(mi);
|
||||
//#if !PREDICTION_1
|
||||
// else if (mi.Name == nameof(NetworkBehaviour.Replicate_Server_SendToSpectators_Internal))
|
||||
// Replicate_Server_SendToSpectators_MethodRef = base.ImportReference(mi);
|
||||
//#endif
|
||||
//Misc.
|
||||
else if (mi.Name == nameof(NetworkBehaviour.OwnerMatches))
|
||||
OwnerMatches_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);
|
||||
//Prediction
|
||||
#if PREDICTION_1
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Replicate_ExitEarly_A))
|
||||
Replicate_ExitEarly_A_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.Replicate_Owner))
|
||||
Replicate_Owner_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Replicate_NonOwner))
|
||||
Replicate_NonOwner_MethodRef = base.ImportReference(mi);
|
||||
#else
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Replicate_Authoritative))
|
||||
Replicate_Authortative_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == Replicate_Replay_Start_MethodName)
|
||||
Replicate_Replay_Start_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Replicate_Replay))
|
||||
Replicate_Replay_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Replicate_NonAuthoritative))
|
||||
Replicate_NonAuthoritative_MethodRef = base.ImportReference(mi);
|
||||
#endif
|
||||
}
|
||||
|
||||
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.IsHostStarted))
|
||||
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);
|
||||
}
|
||||
|
||||
//#if !PREDICTION_1
|
||||
// foreach (FieldInfo fi in networkBehaviourType.GetFields((BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic)))
|
||||
// {
|
||||
// if (fi.Name == nameof(NetworkBehaviour.UsesPrediction))
|
||||
// {
|
||||
// UsesPrediction_FieldRef = base.ImportReference(fi);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//#endif
|
||||
|
||||
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:
|
||||
99
Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs
Normal file
99
Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs
Normal file
@ -0,0 +1,99 @@
|
||||
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;
|
||||
/* SyncType names */
|
||||
//SyncVar.
|
||||
tmpType = typeof(SyncVar<>);
|
||||
base.ImportReference(tmpType);
|
||||
SyncVar_Name = tmpType.Name;
|
||||
//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.IsClientStarted))
|
||||
InstanceFinder_IsClient_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
else if (pi.Name == nameof(InstanceFinder.IsServerStarted))
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
11
Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs.meta
Normal file
11
Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 033da35314653aa4689b4582e7929295
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
117
Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs
Normal file
117
Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs.meta
Normal file
11
Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs.meta
Normal file
@ -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:
|
||||
66
Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs
Normal file
66
Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs
Normal file
@ -0,0 +1,66 @@
|
||||
using FishNet.CodeGenerating.Extension;
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
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_ReadArray_MethodRef;
|
||||
public TypeReference GenericReader_TypeRef;
|
||||
|
||||
public MethodReference GenericReader_ReadUnpacked_MethodRef;
|
||||
public MethodReference GenericReader_ReadAutoPacked_MethodRef;
|
||||
#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));
|
||||
GenericReader_TypeRef = base.ImportReference(typeof(GenericReader<>));
|
||||
|
||||
TypeDefinition genericWriterTd = GenericReader_TypeRef.CachedResolve(base.Session);
|
||||
GenericReader_ReadUnpacked_MethodRef = base.ImportReference(genericWriterTd.GetMethod(nameof(GenericReader<int>.SetReadUnpacked)));
|
||||
GenericReader_ReadAutoPacked_MethodRef = base.ImportReference(genericWriterTd.GetMethod(nameof(GenericReader<int>.SetReadAutoPacked)));
|
||||
|
||||
|
||||
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.ReadDictionaryAllocated))
|
||||
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.ReadArrayAllocated))
|
||||
Reader_ReadArray_MethodRef = base.ImportReference(methodInfo);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs.meta
Normal file
11
Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 350861dcbcbabc447acd37e4310b0697
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
22
Assets/FishNet/CodeGenerating/Helpers/TimeManagerHelper.cs
Normal file
22
Assets/FishNet/CodeGenerating/Helpers/TimeManagerHelper.cs
Normal file
@ -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:
|
||||
36
Assets/FishNet/CodeGenerating/Helpers/TransportHelper.cs
Normal file
36
Assets/FishNet/CodeGenerating/Helpers/TransportHelper.cs
Normal file
@ -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:
|
||||
8
Assets/FishNet/CodeGenerating/Helpers/Typed.meta
Normal file
8
Assets/FishNet/CodeGenerating/Helpers/Typed.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c480d34b00248444d91a61e015b14e2a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
34
Assets/FishNet/CodeGenerating/Helpers/Typed/Comparers.cs
Normal file
34
Assets/FishNet/CodeGenerating/Helpers/Typed/Comparers.cs
Normal file
@ -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:
|
||||
186
Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs
Normal file
186
Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs
Normal file
@ -0,0 +1,186 @@
|
||||
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(ExcludeSerializationAttribute)))
|
||||
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;
|
||||
}
|
||||
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,19 @@
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
|
||||
internal enum SerializerType
|
||||
{
|
||||
Invalid,
|
||||
Enum,
|
||||
Array,
|
||||
List,
|
||||
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:
|
||||
17
Assets/FishNet/CodeGenerating/Helpers/Typed/SyncIndexData.cs
Normal file
17
Assets/FishNet/CodeGenerating/Helpers/Typed/SyncIndexData.cs
Normal file
@ -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:
|
||||
14
Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs
Normal file
14
Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
public enum SyncType
|
||||
{
|
||||
Unset,
|
||||
Variable,
|
||||
List,
|
||||
Dictionary,
|
||||
HashSet,
|
||||
Custom,
|
||||
Unhandled,
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs.meta
Normal file
11
Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 44c753d6ac0c7864c9962d91703b2afe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
97
Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs
Normal file
97
Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs
Normal file
@ -0,0 +1,97 @@
|
||||
using FishNet.CodeGenerating.Extension;
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
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_WriteArray_MethodRef;
|
||||
public TypeReference AutoPackTypeRef;
|
||||
|
||||
public TypeReference GenericWriter_TypeRef;
|
||||
public MethodReference GenericWriter_WriteUnpacked_MethodRef;
|
||||
public MethodReference GenericWriter_WriteAutoPacked_MethodRef;
|
||||
public MethodReference Writer_WriteUnpacked_MethodRef;
|
||||
#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));
|
||||
GenericWriter_TypeRef = base.ImportReference(typeof(GenericWriter<>));
|
||||
Writer_WriteUnpacked_MethodRef = Writer_TypeRef.CachedResolve(base.Session).GetMethodReference(base.Session, nameof(Writer.WriteUnpacked));
|
||||
|
||||
|
||||
TypeDefinition genericWriterTd = GenericWriter_TypeRef.CachedResolve(base.Session);
|
||||
GenericWriter_WriteUnpacked_MethodRef = base.ImportReference(genericWriterTd.GetMethod(nameof(GenericWriter<int>.SetWriteUnpacked)));
|
||||
GenericWriter_WriteAutoPacked_MethodRef = base.ImportReference(genericWriterTd.GetMethod(nameof(GenericWriter<int>.SetWriteAutoPacked)));
|
||||
|
||||
//WriterPool.GetWriter
|
||||
Type writerPoolType = typeof(WriterPool);
|
||||
base.ImportReference(writerPoolType);
|
||||
foreach (var methodInfo in writerPoolType.GetMethods())
|
||||
{
|
||||
if (methodInfo.Name == nameof(WriterPool.Retrieve))
|
||||
{
|
||||
//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.WriteArray))
|
||||
Writer_WriteArray_MethodRef = base.ImportReference(methodInfo);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs.meta
Normal file
11
Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 425638e29bab6f1488e8865c9e3f8b57
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/FishNet/CodeGenerating/ILCore.meta
Normal file
8
Assets/FishNet/CodeGenerating/ILCore.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1808ca0399d069499d3ecb4e031c3e2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
401
Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs
Normal file
401
Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs
Normal file
@ -0,0 +1,401 @@
|
||||
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.Editing.Upgrading;
|
||||
using FishNet.Serializing.Helping;
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Cil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
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 |= CreateSerializerInitializeDelegates(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
|
||||
{
|
||||
TryLogV3ToV4Helpers(session);
|
||||
|
||||
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>
|
||||
/// Logs warning if v3 to v4 helpers are enabled.
|
||||
/// </summary>
|
||||
private void TryLogV3ToV4Helpers(CodegenSession session)
|
||||
{
|
||||
#if !FISHNET_DISABLE_V3TOV4_HELPERS
|
||||
/* There is no way to check if this has run already once per codegen
|
||||
* so the only option is to print per session, which means
|
||||
* a print will occur every time an assembly compiles. This means
|
||||
* several prints will potentially occur per script change.
|
||||
*
|
||||
* However, these warnings typically only print when all errors are gone.
|
||||
* When this is true the user may go ahead and disable this warning
|
||||
* as instructed. */
|
||||
session.LogWarning(UpgradeFromV3ToV4Menu.EnabledWarning);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Makees methods public scope which use CodegenMakePublic attribute.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool ModifyMakePublicMethods(CodegenSession session)
|
||||
{
|
||||
string makePublicTypeFullName = typeof(MakePublicAttribute).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;
|
||||
|
||||
List<TypeDefinition> allTypeDefs = session.Module.Types.ToList();
|
||||
foreach (TypeDefinition td in allTypeDefs)
|
||||
{
|
||||
if (session.GetClass<GeneralHelper>().HasExcludeSerializationAttribute(td))
|
||||
continue;
|
||||
|
||||
if (td.Attributes.HasFlag(WriterProcessor.CUSTOM_SERIALIZER_TYPEDEF_ATTRIBUTES))
|
||||
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;
|
||||
|
||||
List<TypeDefinition> allTypeDefs = session.Module.Types.ToList();
|
||||
foreach (TypeDefinition td in allTypeDefs)
|
||||
{
|
||||
if (session.GetClass<GeneralHelper>().HasExcludeSerializationAttribute(td))
|
||||
continue;
|
||||
|
||||
if (td.Attributes.HasFlag(WriterProcessor.CUSTOM_SERIALIZER_TYPEDEF_ATTRIBUTES))
|
||||
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>().HasExcludeSerializationAttribute(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>().HasExcludeSerializationAttribute(td))
|
||||
continue;
|
||||
|
||||
modified |= session.GetClass<QolAttributeProcessor>().Process(td, codeStripping);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates NetworkBehaviour changes.
|
||||
/// </summary>
|
||||
private bool CreateNetworkBehaviours(CodegenSession session)
|
||||
{
|
||||
//Get all network behaviours to process.
|
||||
List<TypeDefinition> networkBehaviourTypeDefs = session.Module.Types
|
||||
.Where(td => td.IsSubclassOf(session, session.GetClass<NetworkBehaviourHelper>().FullName))
|
||||
.ToList();
|
||||
|
||||
/* Remove types which are inherited. This gets the child most networkbehaviours.
|
||||
* Since processing iterates upward from each child there is no reason
|
||||
* to include any inherited NBs. */
|
||||
RemoveInheritedTypeDefinitions(networkBehaviourTypeDefs);
|
||||
|
||||
foreach (TypeDefinition typeDef in networkBehaviourTypeDefs)
|
||||
{
|
||||
session.ImportReference(typeDef);
|
||||
session.GetClass<NetworkBehaviourProcessor>().ProcessLocal(typeDef);
|
||||
}
|
||||
|
||||
//Call base methods on RPCs.
|
||||
foreach (TypeDefinition td in session.Module.Types)
|
||||
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. */
|
||||
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);
|
||||
}
|
||||
|
||||
//Moment a NetworkBehaviour exist the assembly is considered modified.
|
||||
bool modified = (networkBehaviourTypeDefs.Count > 0);
|
||||
return modified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates generic delegates for all read and write methods.
|
||||
/// </summary>
|
||||
/// <param name="moduleDef"></param>
|
||||
/// <param name="diagnostics"></param>
|
||||
private bool CreateSerializerInitializeDelegates(CodegenSession session)
|
||||
{
|
||||
session.GetClass<WriterProcessor>().CreateInitializeDelegates();
|
||||
session.GetClass<ReaderProcessor>().CreateInitializeDelegates();
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
11
Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs.meta
Normal file
11
Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f03d76b376c1d5b4591039af6fd4c9e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
38
Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs
Normal file
38
Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs
Normal file
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs.meta
Normal file
11
Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs.meta
Normal file
@ -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:
|
||||
8
Assets/FishNet/CodeGenerating/Processing.meta
Normal file
8
Assets/FishNet/CodeGenerating/Processing.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15ff4c71a3c972440810ac633b69764d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
61
Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs
Normal file
61
Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs
Normal file
@ -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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs.meta
Normal file
11
Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8462034e5255191499a018bd8fbbf751
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,367 @@
|
||||
|
||||
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>().HasNotSerializableAttribute(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>().HasNotSerializableAttribute(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.HasNotSerializableAttribute(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)
|
||||
{
|
||||
ReaderProcessor rp = base.GetClass<ReaderProcessor>();
|
||||
WriterProcessor wp = base.GetClass<WriterProcessor>();
|
||||
////If parameterType has user declared do nothing.
|
||||
//if (wp.IsGlobalSerializer(parameterType))
|
||||
// return;
|
||||
|
||||
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) ?
|
||||
wp.GetWriteMethodReference(parameterType) :
|
||||
rp.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) ?
|
||||
wp.CreateWriter(parameterType) :
|
||||
rp.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.CUSTOM_WRITER_PREFIX : ReaderProcessor.CUSTOM_READER_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,543 @@
|
||||
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;
|
||||
|
||||
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
|
||||
|
||||
internal bool ProcessLocal(TypeDefinition typeDef)
|
||||
{
|
||||
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<SyncTypeProcessor>().ProcessLocal(td);
|
||||
|
||||
//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 here then all inerited classes for firstTypeDef have
|
||||
* been processed. */
|
||||
//Sets UsesPrediction in NetworkBehaviours.
|
||||
SetUsesPrediction(_usesPredictionTypeDefs);
|
||||
|
||||
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)
|
||||
{
|
||||
SyncTypeProcessor stProcessor = base.GetClass<SyncTypeProcessor>();
|
||||
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 (stProcessor.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;
|
||||
|
||||
//Base Awake.
|
||||
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>
|
||||
/// Sets UsesPrediction to true on NetworkBehaviours.
|
||||
/// </summary>
|
||||
private void SetUsesPrediction(List<TypeDefinition> typeDefs)
|
||||
{
|
||||
//#if !PREDICTION_1
|
||||
// NetworkBehaviourHelper nbh = base.GetClass<NetworkBehaviourHelper>();
|
||||
|
||||
// foreach (TypeDefinition td in typeDefs)
|
||||
// {
|
||||
// MethodDefinition md = td.GetMethod(NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
|
||||
// ILProcessor processor = md.Body.GetILProcessor();
|
||||
|
||||
// int lastInstructionIndex = (md.Body.Instructions.Count - 1);
|
||||
// //Remove opcode if present. It will be added back on after.
|
||||
// if (lastInstructionIndex >= 0 && md.Body.Instructions[lastInstructionIndex].OpCode == OpCodes.Ret)
|
||||
// md.Body.Instructions.RemoveAt(lastInstructionIndex);
|
||||
|
||||
// //Set field.
|
||||
// processor.Emit(OpCodes.Ldarg_0); //base.
|
||||
// processor.Emit(OpCodes.Ldc_I4_1); //true.
|
||||
// processor.Emit(OpCodes.Stfld, nbh.UsesPrediction_FieldRef);
|
||||
// processor.Emit(OpCodes.Ret);
|
||||
// }
|
||||
//#endif
|
||||
}
|
||||
|
||||
/// <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:
|
||||
12
Assets/FishNet/CodeGenerating/Processing/Prediction.meta
Normal file
12
Assets/FishNet/CodeGenerating/Processing/Prediction.meta
Normal file
@ -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:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user