mirror of
https://github.com/Dadechin/Unity-WebSocket.git
synced 2025-07-16 10:14:33 +00:00
first commit
first commit
This commit is contained in:
parent
4ad6af53b9
commit
2108da7906
28
Assets/DefaultPrefabObjects.asset
Normal file
28
Assets/DefaultPrefabObjects.asset
Normal file
|
@ -0,0 +1,28 @@
|
|||
%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: 3007804520713623267, guid: e3ebf1e5214cbb941b2da4f7add0060a, type: 3}
|
||||
- {fileID: 8475222101369129519, guid: 8cf33e8e99a9b0c4c8f29ff725650de6, type: 3}
|
||||
- {fileID: 4512293259955182956, guid: 44611030e61220d42ab7c37ba3c0ea92, type: 3}
|
||||
- {fileID: 4512293259955182956, guid: 0d6d0f48b03b17f49a6340103cd9b9d0, type: 3}
|
||||
- {fileID: 611616139817875448, guid: bf5f023b4017a5e41a9815ec5745df3d, type: 3}
|
||||
- {fileID: 4512293259955182956, guid: 35639798ad77fc145871588b25d66259, type: 3}
|
||||
- {fileID: 4512293259955182956, guid: fe2b65b02f0484b41aa8cfa9fbbb0e1d, type: 3}
|
||||
- {fileID: 4512293259955182956, guid: dafef736ca1ae384e9a19eb672843563, type: 3}
|
||||
- {fileID: 4512293259955182956, guid: f32d4c19de900e64cb73cedcb8ba6f70, type: 3}
|
||||
- {fileID: 4512293259955182956, guid: b8017cef39731ba439c70fecc09488e3, type: 3}
|
||||
- {fileID: 201277550, guid: 5b712878ecece354ba4ffb026c0a221c, type: 3}
|
||||
- {fileID: 201277550, guid: 26a567abbe21227428f5c3d309d1d73c, 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: 4c21a388bb04ca646927587df80f819a
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: de84fa9739ed55945b58147a773e1740
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Extension/FieldDefinitionExtensions.cs
|
||||
uploadId: 762203
|
|
@ -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(fr.Name, fr.FieldType, git);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return fr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6d983ebd0c9e1b745902030c2f7a8e99
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Extension/FieldReferenceExtensions.cs
|
||||
uploadId: 762203
|
|
@ -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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 127d8312da53b3e49ba9e3e4c6348857
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Extension/ILProcessorExtensions.cs
|
||||
uploadId: 762203
|
|
@ -0,0 +1,239 @@
|
|||
using FishNet.CodeGenerating.Helping;
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Cil;
|
||||
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(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 thisMd, CodegenSession session, ParameterAttributes attr, System.Type type)
|
||||
{
|
||||
TypeReference parameterTypeRef = session.ImportReference(type);
|
||||
ParameterDefinition pd = new($"p{thisMd.Parameters.Count}", attr, parameterTypeRef);
|
||||
thisMd.Parameters.Add(pd);
|
||||
return pd;
|
||||
}
|
||||
|
||||
/// <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();
|
||||
|
||||
foreach (ParameterDefinition pd in otherMd.Parameters)
|
||||
{
|
||||
session.ImportReference(pd.ParameterType.CachedResolve(session));
|
||||
int currentCount = thisMd.Parameters.Count;
|
||||
string name = (pd.Name + currentCount);
|
||||
ParameterDefinition parameterDef = new(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;
|
||||
|
||||
if (parameterDef == null || thisMd.Parameters == null)
|
||||
{
|
||||
session.LogError($"ParameterDefinition or collection is null. Definition null: {parameterDef == null}. Collection null: {thisMd.Parameters == null}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
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(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);
|
||||
}
|
||||
|
||||
public static MethodDefinition CreateCopy(this MethodDefinition copiedMd, CodegenSession session, string nameOverride = null, MethodAttributes? attributesOverride = null)
|
||||
{
|
||||
session.ImportReference(copiedMd.ReturnType);
|
||||
|
||||
MethodAttributes attr = (attributesOverride.HasValue) ? attributesOverride.Value : copiedMd.Attributes;
|
||||
string name = (nameOverride == null) ? copiedMd.Name : nameOverride;
|
||||
MethodDefinition result = new(name, attr, copiedMd.ReturnType);
|
||||
foreach (GenericParameter item in copiedMd.GenericParameters)
|
||||
result.GenericParameters.Add(item);
|
||||
|
||||
result.CreateParameters(session, copiedMd);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 866ed457fe28c3e4b9698d87b9abd709
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Extension/MethodDefinitionExtensions.cs
|
||||
uploadId: 762203
|
|
@ -0,0 +1,314 @@
|
|||
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(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(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(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(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(declaringType);
|
||||
foreach (GenericParameter item in declaringType.GenericParameters)
|
||||
git.GenericArguments.Add(item);
|
||||
fr = new(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(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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c9f00cf3dc8b90b469c3c9cb8b87fc88
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Extension/TypeDefinitionExtensions.cs
|
||||
uploadId: 762203
|
|
@ -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(self);
|
||||
foreach (GenericParameter argument in self.GenericParameters)
|
||||
instance.GenericArguments.Add(argument);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 239b1b10db80c594d93b7ba4ee2c1ec5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Extension/TypeReferenceExtensions.cs
|
||||
uploadId: 762203
|
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.
|
14
Assets/FishNet/CodeGenerating/FN_README.txt.meta
Normal file
14
Assets/FishNet/CodeGenerating/FN_README.txt.meta
Normal file
|
@ -0,0 +1,14 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9133eb285bd7f3c4f89f4d7a2a079c6b
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/FN_README.txt
|
||||
uploadId: 762203
|
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:
|
68
Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs
Normal file
68
Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs
Normal file
|
@ -0,0 +1,68 @@
|
|||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using FishNet.Object;
|
||||
using FishNet.Object.Helping;
|
||||
using FishNet.Object.Prediction;
|
||||
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;
|
||||
ReplicateAttribute_FullName = typeof(ReplicateAttribute).FullName;
|
||||
ReconcileAttribute_FullName = typeof(ReconcileAttribute).FullName;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns type of Rpc attributeFullName is for.
|
||||
/// </summary>
|
||||
/// <param name="attributeFullName"></param>
|
||||
/// <returns></returns>
|
||||
public RpcType GetRpcAttributeType(CustomAttribute ca)
|
||||
{
|
||||
if (ca.Is(ServerRpcAttribute_FullName))
|
||||
return RpcType.Server;
|
||||
else if (ca.Is(ObserversRpcAttribute_FullName))
|
||||
return RpcType.Observers;
|
||||
else if (ca.Is(TargetRpcAttribute_FullName))
|
||||
return RpcType.Target;
|
||||
else
|
||||
return RpcType.None;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns type of Rpc attributeFullName is for.
|
||||
/// </summary>
|
||||
/// <param name="attributeFullName"></param>
|
||||
/// <returns></returns>
|
||||
internal QolAttributeType GetQolAttributeType(string attributeFullName)
|
||||
{
|
||||
if (attributeFullName == ServerAttribute_FullName)
|
||||
return QolAttributeType.Server;
|
||||
else if (attributeFullName == ClientAttribute_FullName)
|
||||
return QolAttributeType.Client;
|
||||
else
|
||||
return QolAttributeType.None;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d32f3dc23b55598429c5cfe6156e6243
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs
|
||||
uploadId: 762203
|
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();
|
||||
|
||||
|
||||
/// <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();
|
||||
|
||||
/// <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();
|
||||
|
||||
_bases = new()
|
||||
{
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
18
Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs.meta
Normal file
18
Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs.meta
Normal file
|
@ -0,0 +1,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3e8416eee3308f54fa942003de975420
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs
|
||||
uploadId: 762203
|
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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 935ec97b96b35b94f8c6880c6908304c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs
|
||||
uploadId: 762203
|
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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a66d771ab331fae408142a5c04abd74e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/CustomAttributeExtensions.cs
|
||||
uploadId: 762203
|
|
@ -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()
|
||||
{
|
||||
DiagnosticType = diagnosticType,
|
||||
File = sequencePoint?.Document.Url.Replace($"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}", ""),
|
||||
Line = sequencePoint?.StartLine ?? 0,
|
||||
Column = sequencePoint?.StartColumn ?? 0,
|
||||
MessageData = $" - {message}"
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fb7b65b572b01444cbe3c9d830cd3587
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/Diagnostics.cs
|
||||
uploadId: 762203
|
|
@ -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();
|
||||
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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1a7e03137ca78704e999f3a3dc68b953
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/GetConstructor.cs
|
||||
uploadId: 762203
|
|
@ -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();
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 820cf8401d4d71c4196dda444559ef8a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/ILProcessorExtensions.cs
|
||||
uploadId: 762203
|
|
@ -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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 360667149f16b6c4aba61fd05427cbfb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/InstructionExtensions.cs
|
||||
uploadId: 762203
|
|
@ -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(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(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(self.Name, self.ReturnType, instanceType)
|
||||
{
|
||||
CallingConvention = self.CallingConvention,
|
||||
HasThis = self.HasThis,
|
||||
ExplicitThis = self.ExplicitThis
|
||||
};
|
||||
|
||||
foreach (ParameterDefinition parameter in self.Parameters)
|
||||
reference.Parameters.Add(new(parameter.ParameterType));
|
||||
|
||||
foreach (GenericParameter generic_parameter in self.GenericParameters)
|
||||
reference.GenericParameters.Add(new(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(self.Name, self.ReturnType, git)
|
||||
{
|
||||
CallingConvention = self.CallingConvention,
|
||||
HasThis = self.HasThis,
|
||||
ExplicitThis = self.ExplicitThis
|
||||
};
|
||||
|
||||
foreach (ParameterDefinition parameter in self.Parameters)
|
||||
reference.Parameters.Add(new(parameter.ParameterType));
|
||||
|
||||
foreach (GenericParameter generic_parameter in self.GenericParameters)
|
||||
reference.GenericParameters.Add(new(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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ee1c15a06ab386e439ec5aa41e3496f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/MethodReferenceExtensions.cs
|
||||
uploadId: 762203
|
|
@ -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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 42648785493390646898f5fa13e1dfd6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/ModuleDefinitionExtensions.cs
|
||||
uploadId: 762203
|
|
@ -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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 31071055a2e388141b8f11e1ba4e147e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/ParameterDefinitionExtensions.cs
|
||||
uploadId: 762203
|
|
@ -0,0 +1,504 @@
|
|||
using FishNet.CodeGenerating.Extension;
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
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(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 (typeDef.FullName == typeof(LoadSceneParameters).FullName)
|
||||
return false;
|
||||
|
||||
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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 645e49fe7eeff3a4e9eb65d77fc6e2ca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/TypeDefinitionExtensions.cs
|
||||
uploadId: 762203
|
|
@ -0,0 +1,141 @@
|
|||
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)
|
||||
{
|
||||
if (typeRef is ArrayType && typeRef.Name.Contains("[][]"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2344f5ab0fda07b498c03fbe0e082c14
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/TypeReferenceExtensions.cs
|
||||
uploadId: 762203
|
1454
Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs
Normal file
1454
Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs
Normal file
File diff suppressed because it is too large
Load Diff
18
Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs.meta
Normal file
18
Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs.meta
Normal file
|
@ -0,0 +1,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6139ff104f3c24442b26dbc4e40d5ce5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs
|
||||
uploadId: 762203
|
471
Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs
Normal file
471
Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs
Normal file
|
@ -0,0 +1,471 @@
|
|||
using FishNet.CodeGenerating.Extension;
|
||||
using FishNet.CodeGenerating.Helping.Extension;
|
||||
using FishNet.CodeGenerating.Processing;
|
||||
using FishNet.Configuring;
|
||||
using FishNet.Managing.Logging;
|
||||
using FishNet.Object;
|
||||
using FishNet.Object.Delegating;
|
||||
using FishNet.Object.Helping;
|
||||
using FishNet.Object.Prediction.Delegating;
|
||||
using MonoFN.Cecil;
|
||||
using MonoFN.Cecil.Cil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using MethodAttributes = MonoFN.Cecil.MethodAttributes;
|
||||
|
||||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
internal class NetworkBehaviourHelper : CodegenBase
|
||||
{
|
||||
#region Reflection references.
|
||||
//Names.
|
||||
internal string FullName;
|
||||
//Prediction.
|
||||
|
||||
public MethodReference Replicate_Reader_MethodRef;
|
||||
public MethodReference Reconcile_Server_MethodRef;
|
||||
//public FieldReference UsesPrediction_FieldRef;
|
||||
public MethodReference EmptyReplicatesQueueIntoHistory_Start_MethodRef;
|
||||
public MethodReference EmptyReplicatesQueueIntoHistory_MethodRef;
|
||||
public MethodReference Reconcile_Client_Start_MethodRef;
|
||||
public MethodReference Replicate_Replay_Start_MethodRef;
|
||||
public MethodReference Replicate_Current_MethodRef;
|
||||
public MethodReference Reconcile_Client_MethodRef;
|
||||
public MethodReference Reconcile_Current_MethodRef;
|
||||
public MethodReference ClearReplicateCache_Internal_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 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)))
|
||||
{
|
||||
if (mi.Name == nameof(NetworkBehaviour.GetIsNetworked))
|
||||
IsNetworked_MethodRef = base.ImportReference(mi);
|
||||
//CreateDelegates.
|
||||
else 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);
|
||||
//Misc.
|
||||
else if (mi.Name == nameof(NetworkBehaviour.OwnerMatches))
|
||||
OwnerMatches_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.NetworkInitializeIfDisabled))
|
||||
NetworkInitializeIfDisabled_MethodRef = base.ImportReference(mi);
|
||||
//Prediction
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Replicate_Current))
|
||||
Replicate_Current_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Replicate_Replay_Start))
|
||||
Replicate_Replay_Start_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.EmptyReplicatesQueueIntoHistory))
|
||||
EmptyReplicatesQueueIntoHistory_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.EmptyReplicatesQueueIntoHistory_Start))
|
||||
EmptyReplicatesQueueIntoHistory_Start_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Client_Start))
|
||||
Reconcile_Client_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_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);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Current))
|
||||
Reconcile_Current_MethodRef = base.ImportReference(mi);
|
||||
else if (mi.Name == nameof(NetworkBehaviour.ClearReplicateCache_Internal))
|
||||
ClearReplicateCache_Internal_MethodRef = base.ImportReference(mi);
|
||||
}
|
||||
|
||||
foreach (PropertyInfo pi in networkBehaviourType.GetProperties((BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic)))
|
||||
{
|
||||
//Server/Client states.
|
||||
if (pi.Name == nameof(NetworkBehaviour.IsClientInitialized))
|
||||
IsClientInitialized_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
else if (pi.Name == nameof(NetworkBehaviour.IsServerInitialized))
|
||||
IsServerInitialized_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
else if (pi.Name == nameof(NetworkBehaviour.IsHostStarted))
|
||||
IsHost_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
else if (pi.Name == nameof(NetworkBehaviour.IsOwner))
|
||||
IsOwner_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
//Owner.
|
||||
else if (pi.Name == nameof(NetworkBehaviour.Owner))
|
||||
Owner_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
else if (pi.Name == nameof(NetworkBehaviour.LocalConnection))
|
||||
LocalConnection_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
//Misc.
|
||||
else if (pi.Name == nameof(NetworkBehaviour.TimeManager))
|
||||
TimeManager_MethodRef = base.ImportReference(pi.GetMethod);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returnsthe child most Awake by iterating up childMostTypeDef.
|
||||
/// </summary>
|
||||
/// <param name="childMostTypeDef"></param>
|
||||
/// <param name="created"></param>
|
||||
/// <returns></returns>
|
||||
internal MethodDefinition GetAwakeMethodDefinition(TypeDefinition typeDef)
|
||||
{
|
||||
return typeDef.GetMethod(AWAKE_METHOD_NAME);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a replicate delegate.
|
||||
/// </summary>
|
||||
/// <param name="processor"></param>
|
||||
/// <param name="originalMethodDef"></param>
|
||||
/// <param name="readerMethodDef"></param>
|
||||
/// <param name="rpcType"></param>
|
||||
internal void CreateReplicateDelegate(MethodDefinition originalMethodDef, MethodDefinition readerMethodDef, uint methodHash)
|
||||
{
|
||||
MethodDefinition methodDef = originalMethodDef.DeclaringType.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
|
||||
ILProcessor processor = methodDef.Body.GetILProcessor();
|
||||
|
||||
List<Instruction> insts = new();
|
||||
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();
|
||||
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();
|
||||
/* 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();
|
||||
|
||||
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();
|
||||
|
||||
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();
|
||||
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();
|
||||
//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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0c42e06349d6890459a155a2afe17d41
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs
|
||||
uploadId: 762203
|
|
@ -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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6d061dda8ed87ed48a08020942ad63f6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionImports.cs
|
||||
uploadId: 762203
|
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
18
Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs.meta
Normal file
18
Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs.meta
Normal file
|
@ -0,0 +1,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 033da35314653aa4689b4582e7929295
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs
|
||||
uploadId: 762203
|
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();
|
||||
|
||||
//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;
|
||||
}
|
||||
}
|
||||
}
|
18
Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs.meta
Normal file
18
Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs.meta
Normal file
|
@ -0,0 +1,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 28ae27f7bc8e89547a606262508fdd36
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs
|
||||
uploadId: 762203
|
|
@ -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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e9a06c812bf785a44a38a5852ff866d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/PredictedObjectHelper.cs
|
||||
uploadId: 762203
|
63
Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs
Normal file
63
Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
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_Read_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_Read_MethodRef = base.ImportReference(genericWriterTd.GetMethod(nameof(GenericReader<int>.SetRead)));
|
||||
|
||||
Type pooledReaderType = typeof(PooledReader);
|
||||
foreach (MethodInfo methodInfo in pooledReaderType.GetMethods())
|
||||
{
|
||||
int parameterCount = methodInfo.GetParameters().Length;
|
||||
/* Special methods. */
|
||||
if (methodInfo.Name == nameof(PooledReader.ReadUnsignedPackedWhole))
|
||||
Reader_ReadPackedWhole_MethodRef = base.ImportReference(methodInfo);
|
||||
//Relay readers.
|
||||
else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadDictionary))
|
||||
Reader_ReadDictionary_MethodRef = base.ImportReference(methodInfo);
|
||||
else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadList))
|
||||
Reader_ReadList_MethodRef = base.ImportReference(methodInfo);
|
||||
else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadArrayAllocated))
|
||||
Reader_ReadArray_MethodRef = base.ImportReference(methodInfo);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
18
Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs.meta
Normal file
18
Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs.meta
Normal file
|
@ -0,0 +1,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 350861dcbcbabc447acd37e4310b0697
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs
|
||||
uploadId: 762203
|
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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 11dbcc0798e079e4a85fe98dfc9fe23a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/TimeManagerHelper.cs
|
||||
uploadId: 762203
|
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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6ced44bfdb3068f4cb7513c9be85729a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/TransportHelper.cs
|
||||
uploadId: 762203
|
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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2b30600f0fdb27c4fb86c310b08f43b5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/Comparers.cs
|
||||
uploadId: 762203
|
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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9b1882eac63e3d94aad8f41915bc1ab8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs
|
||||
uploadId: 762203
|
|
@ -0,0 +1,12 @@
|
|||
namespace FishNet.CodeGenerating.Helping
|
||||
{
|
||||
|
||||
internal enum QolAttributeType
|
||||
{
|
||||
None,
|
||||
Server,
|
||||
Client
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 357a22940018b8e49976e13272c5b2ef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/QOLAttributeType.cs
|
||||
uploadId: 762203
|
|
@ -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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e7f1bbf5c398c3e4e92e53ec3e49d5e9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/SerializatierType.cs
|
||||
uploadId: 762203
|
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();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 55f2e751e4788464b8394f6b8bdb548a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/SyncIndexData.cs
|
||||
uploadId: 762203
|
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,
|
||||
}
|
||||
|
||||
}
|
18
Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs.meta
Normal file
18
Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs.meta
Normal file
|
@ -0,0 +1,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 44c753d6ac0c7864c9962d91703b2afe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs
|
||||
uploadId: 762203
|
100
Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs
Normal file
100
Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs
Normal file
|
@ -0,0 +1,100 @@
|
|||
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_Signed_MethodRef;
|
||||
public MethodReference Writer_WritePackedWhole_Unsigned_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_Write_MethodRef;
|
||||
public MethodReference Writer_Write_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_Write_MethodRef = Writer_TypeRef.CachedResolve(base.Session).GetMethodReference(base.Session, nameof(Writer.Write));
|
||||
|
||||
|
||||
TypeDefinition genericWriterTd = GenericWriter_TypeRef.CachedResolve(base.Session);
|
||||
GenericWriter_Write_MethodRef = base.ImportReference(genericWriterTd.GetMethod(nameof(GenericWriter<int>.SetWrite)));
|
||||
|
||||
//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.WriteUnsignedPackedWhole))
|
||||
{
|
||||
//todo: check if signed or not and set to signed/unsigned variable.
|
||||
//do the same changes for methods which call these.
|
||||
//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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
18
Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs.meta
Normal file
18
Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs.meta
Normal file
|
@ -0,0 +1,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 425638e29bab6f1488e8865c9e3f8b57
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs
|
||||
uploadId: 762203
|
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:
|
425
Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs
Normal file
425
Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs
Normal file
|
@ -0,0 +1,425 @@
|
|||
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 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();
|
||||
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(null, session.Diagnostics);
|
||||
|
||||
modified |= session.GetClass<WriterProcessor>().Process();
|
||||
modified |= session.GetClass<ReaderProcessor>().Process();
|
||||
modified |= CreateDeclaredSerializerDelegates(session);
|
||||
modified |= CreateDeclaredSerializers(session);
|
||||
modified |= CreateDeclaredComparerDelegates(session);
|
||||
modified |= CreateIncludeSerializationSerializers(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();
|
||||
sb.AppendLine($"Assembly {session.Module.Name} has inherited access to SyncVars in different assemblies. When accessing SyncVars across assemblies be sure to use Get/Set methods withinin the inherited assembly script to change SyncVars. Accessible fields are:");
|
||||
|
||||
foreach (FieldDefinition item in session.DifferentAssemblySyncVars)
|
||||
sb.AppendLine($"Field {item.Name} within {item.DeclaringType.FullName} in assembly {item.Module.Name}.");
|
||||
|
||||
session.LogWarning("v------- IMPORTANT -------v");
|
||||
session.LogWarning(sb.ToString());
|
||||
session.DifferentAssemblySyncVars.Clear();
|
||||
}
|
||||
|
||||
//session.LogWarning($"Assembly {compiledAssembly.Name} took {stopwatch.ElapsedMilliseconds}.");
|
||||
if (!modified)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryStream pe = new();
|
||||
MemoryStream pdb = new();
|
||||
WriterParameters writerParameters = new()
|
||||
{
|
||||
SymbolWriterProvider = new PortablePdbWriterProvider(),
|
||||
SymbolStream = pdb,
|
||||
WriteSymbols = true
|
||||
};
|
||||
assemblyDef.Write(pe, writerParameters);
|
||||
return new(new(pe.ToArray(), pdb.ToArray()), session.Diagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Makees methods public scope which use CodegenMakePublic attribute.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool ModifyMakePublicMethods(CodegenSession session)
|
||||
{
|
||||
string makePublicTypeFullName = typeof(MakePublicAttribute).FullName;
|
||||
foreach (TypeDefinition td in session.Module.Types)
|
||||
{
|
||||
foreach (CustomAttribute tdCustomAttribute in td.CustomAttributes)
|
||||
{
|
||||
if (tdCustomAttribute.AttributeType.FullName == makePublicTypeFullName)
|
||||
{
|
||||
td.Attributes &= ~TypeAttributes.NotPublic;
|
||||
td.Attributes |= TypeAttributes.Public;
|
||||
}
|
||||
}
|
||||
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>
|
||||
/// Creates serializers for types that use IncludeSerialization attribute.
|
||||
/// </summary>
|
||||
private bool CreateIncludeSerializationSerializers(CodegenSession session)
|
||||
{
|
||||
string attributeName = typeof(IncludeSerializationAttribute).FullName;
|
||||
WriterProcessor wp = session.GetClass<WriterProcessor>();
|
||||
ReaderProcessor rp = session.GetClass<ReaderProcessor>();
|
||||
|
||||
bool modified = false;
|
||||
List<TypeDefinition> allTypeDefs = session.Module.Types.ToList();
|
||||
foreach (TypeDefinition td in allTypeDefs)
|
||||
{
|
||||
if (!CanSerialize())
|
||||
continue;
|
||||
|
||||
TypeReference tr = session.ImportReference(td);
|
||||
if (wp.CreateWriter(tr) != null && rp.CreateReader(tr) != null)
|
||||
modified = true;
|
||||
else
|
||||
session.LogError($"Failed to create serializers for {td.FullName}.");
|
||||
|
||||
bool CanSerialize()
|
||||
{
|
||||
foreach (CustomAttribute item in td.CustomAttributes)
|
||||
{
|
||||
if (item.AttributeType.FullName == attributeName)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
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();
|
||||
/* 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);
|
||||
}
|
||||
}
|
18
Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs.meta
Normal file
18
Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs.meta
Normal file
|
@ -0,0 +1,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f03d76b376c1d5b4591039af6fd4c9e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs
|
||||
uploadId: 762203
|
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(compiledAssembly);
|
||||
ReaderParameters readerParameters = new()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
18
Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs.meta
Normal file
18
Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs.meta
Normal file
|
@ -0,0 +1,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dfcfb917dd9268744962ae61aa0115b7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs
|
||||
uploadId: 762203
|
|
@ -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();
|
||||
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(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(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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2c247f4266b2864eb96e6a9ae6557d31
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/ILCore/PostProcessorAssemblyResolver.cs
|
||||
uploadId: 762203
|
|
@ -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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 484e8ad8c4dde382ea67036b32935ef1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporter.cs
|
||||
uploadId: 762203
|
|
@ -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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f9273a5dad109ab0783891e36c983080
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporterProvider.cs
|
||||
uploadId: 762203
|
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
|
||||
|
||||
}
|
||||
|
||||
}
|
18
Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs.meta
Normal file
18
Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs.meta
Normal file
|
@ -0,0 +1,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8462034e5255191499a018bd8fbbf751
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs
|
||||
uploadId: 762203
|
|
@ -0,0 +1,341 @@
|
|||
|
||||
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();
|
||||
/* 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)
|
||||
{
|
||||
//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)
|
||||
{
|
||||
//Set new instruction.
|
||||
Instruction newInstruction = processor.Create(OpCodes.Call, createdMethodRef);
|
||||
methodDef.Body.Instructions[instructionIndex] = newInstruction;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9269fd8a62199e24c965b4c99b641244
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Processing/CustomSerializerProcessor.cs
|
||||
uploadId: 762203
|
|
@ -0,0 +1,503 @@
|
|||
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();
|
||||
#endregion
|
||||
|
||||
#region Const.
|
||||
internal const string NETWORKINITIALIZE_EARLY_INTERNAL_NAME = "NetworkInitialize___Early";
|
||||
internal const string NETWORKINITIALIZE_LATE_INTERNAL_NAME = "NetworkInitialize___Late";
|
||||
private readonly string[] _networkInitializeMethodNames = new string[] { NETWORKINITIALIZE_EARLY_INTERNAL_NAME, NETWORKINITIALIZE_LATE_INTERNAL_NAME };
|
||||
#endregion
|
||||
|
||||
internal bool ProcessLocal(TypeDefinition typeDef)
|
||||
{
|
||||
bool modified = false;
|
||||
TypeDefinition copyTypeDef = typeDef;
|
||||
|
||||
//TypeDefs which are using prediction.
|
||||
List<TypeDefinition> _usesPredictionTypeDefs = new();
|
||||
|
||||
//Make collection of NBs to processor.
|
||||
List<TypeDefinition> typeDefs = new();
|
||||
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);
|
||||
CallNetworkInitialize(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. */
|
||||
|
||||
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();
|
||||
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 is null cannot call base.
|
||||
if (typeDef.BaseType == null)
|
||||
return;
|
||||
|
||||
foreach (string mdName in _networkInitializeMethodNames)
|
||||
{
|
||||
MethodReference baseMr = typeDef.GetMethodReferenceInBase(base.Session, mdName);
|
||||
//Not found in base.
|
||||
if (baseMr == null)
|
||||
continue;
|
||||
|
||||
MethodDefinition baseMd = baseMr.CachedResolve(base.Session);
|
||||
|
||||
MethodDefinition thisMd = typeDef.GetMethod(mdName);
|
||||
ILProcessor processor = thisMd.Body.GetILProcessor();
|
||||
|
||||
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()
|
||||
{
|
||||
processor.Create(OpCodes.Ldarg_0), //this.
|
||||
processor.Create(OpCodes.Call, baseMr)
|
||||
};
|
||||
processor.InsertFirst(instructions);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds returns awake method definitions within awakeDatas.
|
||||
/// </summary>
|
||||
private void AddReturnToAwake(TypeDefinition td)
|
||||
{
|
||||
//This Awake.
|
||||
MethodDefinition awakeMd = td.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
|
||||
ILProcessor processor = awakeMd.Body.GetILProcessor();
|
||||
//If no instructions or the last instruction isnt ret.
|
||||
if (processor.Body.Instructions.Count == 0 || processor.Body.Instructions[processor.Body.Instructions.Count - 1].OpCode != OpCodes.Ret)
|
||||
{
|
||||
processor.Emit(OpCodes.Ret);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls a method by name from awake.
|
||||
/// </summary>
|
||||
private void CallMethodFromAwake(TypeDefinition typeDef, string methodName)
|
||||
{
|
||||
//Will never be null because we added it previously.
|
||||
MethodDefinition awakeMethodDef = typeDef.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
|
||||
MethodReference networkInitMr = typeDef.GetMethodReference(base.Session, methodName);
|
||||
|
||||
ILProcessor processor = awakeMethodDef.Body.GetILProcessor();
|
||||
processor.Emit(OpCodes.Ldarg_0);
|
||||
processor.Emit(networkInitMr.GetCallOpCode(base.Session), networkInitMr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an 'NetworkInitialize' method which is called by the childmost class to initialize scripts on Awake.
|
||||
/// </summary>
|
||||
private void CreateNetworkInitializeMethods(TypeDefinition typeDef, out MethodDefinition networkInitializeIfDisabledMd)
|
||||
{
|
||||
CreateMethod(NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
|
||||
CreateMethod(NETWORKINITIALIZE_LATE_INTERNAL_NAME);
|
||||
networkInitializeIfDisabledMd = CreateMethod(nameof(NetworkBehaviour.NetworkInitializeIfDisabled));
|
||||
|
||||
MethodDefinition CreateMethod(string name, MethodDefinition copied = null)
|
||||
{
|
||||
bool created;
|
||||
MethodDefinition md = typeDef.GetOrCreateMethodDefinition(base.Session, name, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES, typeDef.Module.TypeSystem.Void, out created);
|
||||
|
||||
if (created)
|
||||
{
|
||||
//Emit ret into new method.
|
||||
ILProcessor processor = md.Body.GetILProcessor();
|
||||
//End of method return.
|
||||
processor.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
return md;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an 'NetworkInitialize' method which is called by the childmost class to initialize scripts on Awake.
|
||||
/// </summary>
|
||||
private void CallNetworkInitialize(MethodDefinition networkIntializeMd)
|
||||
{
|
||||
ILProcessor processor = networkIntializeMd.Body.GetILProcessor();
|
||||
|
||||
networkIntializeMd.Body.Instructions.Clear();
|
||||
CallMethod(NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
|
||||
CallMethod(NETWORKINITIALIZE_LATE_INTERNAL_NAME);
|
||||
processor.Emit(OpCodes.Ret);
|
||||
|
||||
void CallMethod(string name)
|
||||
{
|
||||
MethodReference initIfDisabledMr = networkIntializeMd.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(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()
|
||||
{
|
||||
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,18 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 23866e4d620216745a837fa99e801164
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.9R
|
||||
assetPath: Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs
|
||||
uploadId: 762203
|
8
Assets/FishNet/CodeGenerating/Processing/Prediction.meta
Normal file
8
Assets/FishNet/CodeGenerating/Processing/Prediction.meta
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 98f6937bb72a7254b92b4656f28b7333
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user