Compare commits

...

2 Commits

Author SHA1 Message Date
6d83c876de feat: Added FishNet plugin to project and began implementing/testing online multiplayer 2024-04-18 20:24:22 +02:00
6d89d9d48e fix: introduced assetbundles, instead of reading from folder
Reading Assets from folders was only possible in Editor over  LoadAssetFromPath. Now I'm using asset bundles for ScriptedObjects and Audio Prefabs, when the project is built.
When in Editor the folders are still used for convenience (can make changes to assets without rebuilding asset bundles)
2024-04-18 20:23:17 +02:00
1928 changed files with 204057 additions and 247 deletions

3
.gitignore vendored
View File

@ -67,6 +67,9 @@ crashlytics-build.properties
# Packed Addressables
/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin*
# Temporary auto-generated Assets
/[Aa]ssets/[Ss]treamingAssets/*
# Temporary auto-generated Android Assets
/[Aa]ssets/[Ss]treamingAssets/aa.meta
/[Aa]ssets/[Ss]treamingAssets/aa/*

View File

@ -0,0 +1,26 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3ad70174b079c2f4ebc7931d3dd1af6f, type: 3}
m_Name: DefaultPrefabObjects
m_EditorClassIdentifier:
_prefabs:
- {fileID: 4320456058255827552, guid: 65cd4fa5e050652409dd9b062365c7e8, type: 3}
- {fileID: 394654179909484550, guid: 46f84e56340e83e4ba7c6abbccd2ae3f, type: 3}
- {fileID: 8475222101369129519, guid: 8cf33e8e99a9b0c4c8f29ff725650de6, type: 3}
- {fileID: 4512293259955182956, guid: 44611030e61220d42ab7c37ba3c0ea92, type: 3}
- {fileID: 4512293259955182956, guid: 0d6d0f48b03b17f49a6340103cd9b9d0, type: 3}
- {fileID: 4512293259955182956, guid: f32d4c19de900e64cb73cedcb8ba6f70, type: 3}
- {fileID: 4512293259955182956, guid: 300370bdf7819da41937e0beac65b32c, type: 3}
- {fileID: 611616139817875448, guid: bf5f023b4017a5e41a9815ec5745df3d, type: 3}
- {fileID: 201277550, guid: 5b712878ecece354ba4ffb026c0a221c, type: 3}
- {fileID: 201277550, guid: 8ef354bfc16ca8a459074c3fa9b6727e, type: 3}
- {fileID: 8192566354860284824, guid: 6331b3542e64a564c81bc39cedf70c8d, type: 3}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bb50798712a8fcd40b22abbc470201fb
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

8
Assets/Editor.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 38a7f445babd57442af500242aa934ed
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
using UnityEditor;
using System.IO;
public class CreateAssetBundles
{
[MenuItem("Assets/Build AssetBundles")]
static void BuildAllAssetBundles()
{
string assetBundleDirectory = "Assets/StreamingAssets";
if (!Directory.Exists(assetBundleDirectory))
{
Directory.CreateDirectory(assetBundleDirectory);
}
BuildPipeline.BuildAssetBundles(assetBundleDirectory,
BuildAssetBundleOptions.None,
BuildTarget.StandaloneWindows);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 470f9a7d05d183e4aac0b2759ec5dbda
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Assets/FishNet.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f1525dbf8ebd59e438b504fa19c4fd6c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a03c3ea914b8ed649a14606e1430e404
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c2e223f21744b544bbb7e93020199efb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: de84fa9739ed55945b58147a773e1740
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,40 @@
using FishNet.CodeGenerating.Extension;
using MonoFN.Cecil;
namespace FishNet.CodeGenerating.Helping.Extension
{
internal static class FieldReferenceExtensions
{
/// <summary>
/// Gets a Resolve favoring cached results first.
/// </summary>
internal static FieldDefinition CachedResolve(this FieldReference fieldRef, CodegenSession session)
{
return session.GetClass<GeneralHelper>().GetFieldReferenceResolve(fieldRef);
}
/// <summary>
/// Makes a FieldReference generic if it has generic parameters.
/// </summary>
public static FieldReference TryMakeGenericInstance(this FieldReference fr)
{
TypeReference declaringTr = fr.DeclaringType;
if (declaringTr.HasGenericParameters)
{
GenericInstanceType git = declaringTr.MakeGenericInstanceType();
FieldReference result = new FieldReference(fr.Name, fr.FieldType, git);
return result;
}
else
{
return fr;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6d983ebd0c9e1b745902030c2f7a8e99
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 127d8312da53b3e49ba9e3e4c6348857
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,235 @@
using FishNet.CodeGenerating.Helping;
using FishNet.CodeGenerating.Helping.Extension;
using MonoFN.Cecil;
using MonoFN.Cecil.Cil;
using MonoFN.Cecil.Rocks;
using MonoFN.Collections.Generic;
using System.Collections.Generic;
namespace FishNet.CodeGenerating.Extension
{
internal static class MethodDefinitionExtensions
{
public const MethodAttributes PUBLIC_VIRTUAL_ATTRIBUTES = (MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig);
public const MethodAttributes PROTECTED_VIRTUAL_ATTRIBUTES = (MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig);
/// <summary>
/// Returns a custom attribute.
/// </summary>
public static CustomAttribute GetCustomAttribute(this MethodDefinition md, string attributeFullName)
{
if (md == null)
return null;
foreach (CustomAttribute item in md.CustomAttributes)
{
if (item.AttributeType.FullName == attributeFullName)
return item;
}
//Not found.
return null;
}
/// <summary>
/// Clears the method content and returns ret.
/// </summary>
internal static void ClearMethodWithRet(this MethodDefinition md, CodegenSession session, ModuleDefinition importReturnModule = null)
{
md.Body.Instructions.Clear();
ILProcessor processor = md.Body.GetILProcessor();
processor.Add(session.GetClass<GeneralHelper>().CreateRetDefault(md, importReturnModule));
}
/// <summary>
/// Returns the ParameterDefinition index from end of parameters.
/// </summary>
/// <param name="md"></param>
/// <param name="index"></param>
/// <returns></returns>
internal static ParameterDefinition GetEndParameter(this MethodDefinition md, int index)
{
//Not enough parameters.
if (md.Parameters.Count < (index + 1))
return null;
return md.Parameters[md.Parameters.Count - (index + 1)];
}
/// <summary>
/// Creates a variable type within the body and returns it's VariableDef.
/// </summary>
internal static VariableDefinition CreateVariable(this MethodDefinition methodDef, TypeReference variableTypeRef)
{
VariableDefinition variableDef = new VariableDefinition(variableTypeRef);
methodDef.Body.Variables.Add(variableDef);
return variableDef;
}
/// <summary>
/// Creates a variable type within the body and returns it's VariableDef.
/// </summary>
internal static VariableDefinition CreateVariable(this MethodDefinition methodDef, CodegenSession session, System.Type variableType)
{
return CreateVariable(methodDef, session.GetClass<GeneralHelper>().GetTypeReference(variableType));
}
/// <summary>
/// Returns the proper OpCode to use for call methods.
/// </summary>
public static MonoFN.Cecil.Cil.OpCode GetCallOpCode(this MethodDefinition md)
{
if (md.Attributes.HasFlag(MethodAttributes.Virtual))
return MonoFN.Cecil.Cil.OpCodes.Callvirt;
else
return MonoFN.Cecil.Cil.OpCodes.Call;
}
/// <summary>
/// Returns the proper OpCode to use for call methods.
/// </summary>
public static MonoFN.Cecil.Cil.OpCode GetCallOpCode(this MethodReference mr, CodegenSession session)
{
return mr.CachedResolve(session).GetCallOpCode();
}
/// <summary>
/// Adds a parameter and returns added parameters.
/// </summary>
public static ParameterDefinition CreateParameter(this MethodDefinition thisMr, CodegenSession session, ParameterAttributes attr, System.Type type)
{
TypeReference parameterTypeRef = session.ImportReference(type);
ParameterDefinition pd = new ParameterDefinition($"p{thisMr.Parameters.Count}", attr, parameterTypeRef);
thisMr.Parameters.Add(pd);
return pd;
}
/// <summary>
/// Adds otherMd parameters to thisMR and returns added parameters.
/// </summary>
public static List<ParameterDefinition> CreateParameters(this MethodReference thisMr, CodegenSession session, MethodDefinition otherMd)
{
return thisMr.CachedResolve(session).CreateParameters(session, otherMd);
}
/// <summary>
/// Adds otherMr parameters to thisMR and returns added parameters.
/// </summary>
public static List<ParameterDefinition> CreateParameters(this MethodReference thisMr, CodegenSession session, MethodReference otherMr)
{
return thisMr.CachedResolve(session).CreateParameters(session, otherMr.CachedResolve(session));
}
/// <summary>
/// Adds otherMd parameters to thisMd and returns added parameters.
/// </summary>
public static List<ParameterDefinition> CreateParameters(this MethodDefinition thisMd, CodegenSession session, MethodDefinition otherMd)
{
List<ParameterDefinition> results = new List<ParameterDefinition>();
foreach (ParameterDefinition pd in otherMd.Parameters)
{
session.ImportReference(pd.ParameterType);
int currentCount = thisMd.Parameters.Count;
string name = (pd.Name + currentCount);
ParameterDefinition parameterDef = new ParameterDefinition(name, pd.Attributes, pd.ParameterType);
//Set any default values.
parameterDef.Constant = pd.Constant;
parameterDef.IsReturnValue = pd.IsReturnValue;
parameterDef.IsOut = pd.IsOut;
foreach (CustomAttribute item in pd.CustomAttributes)
parameterDef.CustomAttributes.Add(item);
parameterDef.HasConstant = pd.HasConstant;
parameterDef.HasDefault = pd.HasDefault;
thisMd.Parameters.Add(parameterDef);
results.Add(parameterDef);
}
return results;
}
/// <summary>
/// Returns a method reference while considering if declaring type is generic.
/// </summary>
public static MethodReference GetMethodReference(this MethodDefinition md, CodegenSession session)
{
MethodReference methodRef = session.ImportReference(md);
//Is generic.
if (md.DeclaringType.HasGenericParameters)
{
GenericInstanceType git = methodRef.DeclaringType.MakeGenericInstanceType();
MethodReference result = new MethodReference(md.Name, md.ReturnType)
{
HasThis = md.HasThis,
ExplicitThis = md.ExplicitThis,
DeclaringType = git,
CallingConvention = md.CallingConvention,
};
foreach (ParameterDefinition pd in md.Parameters)
{
session.ImportReference(pd.ParameterType);
result.Parameters.Add(pd);
}
return result;
}
else
{
return methodRef;
}
}
/// <summary>
/// Returns a method reference for a generic method.
/// </summary>
public static MethodReference GetMethodReference(this MethodDefinition md, CodegenSession session, TypeReference typeReference)
{
MethodReference methodRef = session.ImportReference(md);
return methodRef.GetMethodReference(session, typeReference);
}
/// <summary>
/// Removes ret if it exist at the end of the method. Returns if ret was removed.
/// </summary>
internal static bool RemoveEndRet(this MethodDefinition md, CodegenSession session)
{
int count = md.Body.Instructions.Count;
if (count > 0 && md.Body.Instructions[count - 1].OpCode == OpCodes.Ret)
{
md.Body.Instructions.RemoveAt(count - 1);
return true;
}
else
{
return false;
}
}
/// <summary>
/// Returns a method reference for a generic method.
/// </summary>
public static MethodReference GetMethodReference(this MethodDefinition md, CodegenSession session, TypeReference[] typeReferences)
{
MethodReference methodRef = session.ImportReference(md);
return methodRef.GetMethodReference(session, typeReferences);
}
/// <summary>
/// Makes a method definition public.
/// </summary>
public static void SetPublicAttributes(this MethodDefinition md)
{
md.Attributes = PUBLIC_VIRTUAL_ATTRIBUTES;
}
public static void SetProtectedAttributes(this MethodDefinition md)
{
md.Attributes = PROTECTED_VIRTUAL_ATTRIBUTES;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 866ed457fe28c3e4b9698d87b9abd709
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,315 @@
using FishNet.CodeGenerating.Helping.Extension;
using FishNet.Object.Prediction;
using FishNet.Utility.Performance;
using MonoFN.Cecil;
using System.Collections.Generic;
namespace FishNet.CodeGenerating.Extension
{
internal static class TypeDefinitionExtensions
{
/// <summary>
/// Returns if a TypeDefinition is nullable.
/// </summary>
public static bool IsNullable(this TypeDefinition td)
{
return (td.Name == typeof(System.Nullable<>).Name);
}
/// <summary>
/// Finds the first method by a given name.
/// </summary>
/// <param name="typeDef"></param>
/// <param name="methodName"></param>
/// <returns></returns>
internal static MethodDefinition GetMethod(this TypeDefinition typeDef, string methodName)
{
foreach (MethodDefinition md in typeDef.Methods)
{
if (md.Name == methodName)
return md;
}
return null;
}
public static MethodReference GetMethodReferenceInBase(this TypeDefinition td, CodegenSession session, string methodName)
{
MethodDefinition baseMd = td.GetMethodDefinitionInBase(session, methodName);
if (baseMd == null)
return null;
MethodReference baseMr;
TypeReference baseTr = td.BaseType;
if (baseTr.CachedResolve(session).HasGenericParameters)
{
GenericInstanceType git = (GenericInstanceType)baseTr;
baseMr = new MethodReference(baseMd.Name, baseMd.ReturnType, git)
{
HasThis = baseMd.HasThis,
CallingConvention = baseMd.CallingConvention,
ExplicitThis = baseMd.ExplicitThis,
};
foreach (ParameterDefinition pd in baseMd.Parameters)
{
session.ImportReference(pd.ParameterType);
baseMr.Parameters.Add(pd);
}
}
else
{
baseMr = session.ImportReference(baseMd);
}
return baseMr;
}
/// <summary>
/// Returns a method in the next base class.
/// </summary>
public static MethodDefinition GetMethodDefinitionInBase(this TypeDefinition td, CodegenSession session, string methodName)
{
if (td.BaseType == null)
{
session.LogError($"BaseType for {td.FullName} is null.");
return null;
}
TypeDefinition baseTd = td.BaseType.CachedResolve(session);
return baseTd.GetMethod(methodName);
}
/// <summary>
/// Returns a method in the next base class.
/// </summary>
public static MethodReference GetMethodReference(this TypeDefinition td, CodegenSession session, string methodName)
{
MethodDefinition md = td.GetMethod(methodName);
//Not found.
if (md == null)
return null;
return md.GetMethodReference(session);
}
/// <summary>
/// Gets a MethodReference or creates one if missing.
/// </summary>
public static MethodReference GetOrCreateMethodReference(this TypeDefinition td, CodegenSession session, string methodName, MethodAttributes attributes, TypeReference returnType, out bool created)
{
MethodDefinition md = td.GetMethod(methodName);
//Not found.
if (md == null)
{
md = new MethodDefinition(methodName, attributes, returnType);
td.Methods.Add(md);
created = true;
}
else
{
created = false;
}
return md.GetMethodReference(session);
}
/// <summary>
/// Gets a MethodDefinition or creates one if missing.
/// </summary>
public static MethodDefinition GetOrCreateMethodDefinition(this TypeDefinition td, CodegenSession session, string methodName, MethodAttributes attributes, TypeReference returnType, out bool created)
{
MethodDefinition md = td.GetMethod(methodName);
//Not found.
if (md == null)
{
md = new MethodDefinition(methodName, attributes, returnType);
td.Methods.Add(md);
created = true;
}
else
{
created = false;
}
return md;
}
/// <summary>
/// Gets a MethodDefinition or creates one if missing.
/// </summary>
public static MethodDefinition GetOrCreateMethodDefinition(this TypeDefinition td, CodegenSession session, string methodName, MethodDefinition methodTemplate, bool copyParameters, out bool created)
{
MethodDefinition md = td.GetMethod(methodName);
//Not found.
if (md == null)
{
TypeReference returnType = session.ImportReference(methodTemplate.ReturnType);
md = new MethodDefinition(methodName, methodTemplate.Attributes, returnType)
{
ExplicitThis = methodTemplate.ExplicitThis,
AggressiveInlining = methodTemplate.AggressiveInlining,
Attributes = methodTemplate.Attributes,
CallingConvention = methodTemplate.CallingConvention,
HasThis = methodTemplate.HasThis,
};
md.Body.InitLocals = methodTemplate.Body.InitLocals;
if (copyParameters)
{
foreach (ParameterDefinition pd in methodTemplate.Parameters)
{
session.ImportReference(pd.ParameterType.CachedResolve(session));
md.Parameters.Add(pd);
}
}
foreach (GenericParameter item in methodTemplate.GenericParameters)
{
session.ImportReference(item);
md.GenericParameters.Add(item);
}
td.Methods.Add(md);
created = true;
}
else
{
created = false;
}
return md;
}
/// <summary>
/// Returns a method in any inherited classes. The first found method is returned.
/// </summary>
public static MethodDefinition GetMethodDefinitionInAnyBase(this TypeDefinition td, CodegenSession session, string methodName)
{
while (td != null)
{
foreach (MethodDefinition md in td.Methods)
{
if (md.Name == methodName)
return md;
}
try
{
td = td.GetNextBaseTypeDefinition(session);
}
catch
{
return null;
}
}
return null;
}
/// <summary>
/// Returns a TypeDefintiion found in typeDef or up it's hierarchy.
/// </summary>
/// <param name="checkTypeDef">True to check if typeDef equals fullName.</param>
/// <returns></returns>
public static TypeDefinition GetTypeDefinitionInBase(this TypeDefinition typeDef, CodegenSession session, string targetFullName, bool checkTypeDef)
{
if (typeDef == null)
return null;
if (!checkTypeDef)
typeDef = typeDef.GetNextBaseTypeDefinition(session);
while (typeDef != null)
{
if (typeDef.FullName == targetFullName)
return typeDef;
typeDef = typeDef.GetNextBaseTypeDefinition(session);
}
return null;
}
/// <summary>
/// Returns the next base type.
/// </summary>
public static TypeDefinition GetNextBaseTypeDefinition(this TypeDefinition typeDef, CodegenSession session)
{
return (typeDef.BaseType == null) ? null : typeDef.BaseType.CachedResolve(session);
}
/// <summary>
/// Creates a FieldReference.
/// </summary>
public static FieldReference CreateFieldReference(this FieldDefinition fd, CodegenSession session)
{
FieldReference fr;
TypeDefinition declaringType = fd.DeclaringType;
//Is generic.
if (declaringType.HasGenericParameters)
{
GenericInstanceType git = new GenericInstanceType(declaringType);
foreach (GenericParameter item in declaringType.GenericParameters)
git.GenericArguments.Add(item);
fr = new FieldReference(fd.Name, fd.FieldType, git);
return fr;
}
//Not generic.
else
{
return session.ImportReference(fd);
}
}
/// <summary>
/// Gets a FieldReference or creates it if missing.
/// </summary>
public static FieldReference GetOrCreateFieldReference(this TypeDefinition td, CodegenSession session, string fieldName, FieldAttributes attributes, TypeReference fieldTypeRef, out bool created)
{
FieldReference fr = td.GetFieldReference(fieldName, session);
if (fr == null)
{
fr = td.CreateFieldDefinition(session, fieldName, attributes, fieldTypeRef);
created = true;
}
else
{
created = false;
}
return fr;
}
/// <summary>
/// Creates a FieldReference.
/// </summary>
public static FieldReference CreateFieldDefinition(this TypeDefinition td, CodegenSession session, string fieldName, FieldAttributes attributes, TypeReference fieldTypeRef)
{
FieldDefinition fd = new FieldDefinition(fieldName, attributes, fieldTypeRef);
td.Fields.Add(fd);
return fd.CreateFieldReference(session);
}
/// <summary>
/// Makes a GenericInstanceType.
/// </summary>
public static GenericInstanceType MakeGenericInstanceType(this TypeDefinition self, CodegenSession session)
{
TypeReference tr = session.ImportReference(self);
return tr.MakeGenericInstanceType();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c9f00cf3dc8b90b469c3c9cb8b87fc88
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,52 @@

using FishNet.CodeGenerating.Helping;
using FishNet.CodeGenerating.Helping.Extension;
using MonoFN.Cecil;
using UnityEngine;
namespace FishNet.CodeGenerating.Extension
{
internal static class TypeReferenceExtensions
{
/// <summary>
/// Returns if a TypeReference is nullable.
/// </summary>
public static bool IsNullable(this TypeReference tr, CodegenSession session)
{
TypeDefinition td = tr.CachedResolve(session);
return td.IsNullable();
}
/// <summary>
/// Returns the fullname of a TypeReference without <>.
/// </summary>
/// <param name="tr"></param>
/// <returns></returns>
public static string GetFullnameWithoutBrackets(this TypeReference tr)
{
string str = tr.FullName;
str = str.Replace("<", "");
str = str.Replace(">", "");
return str;
}
/// <summary>
/// Makes a GenericInstanceType.
/// </summary>
public static GenericInstanceType MakeGenericInstanceType(this TypeReference self)
{
GenericInstanceType instance = new GenericInstanceType(self);
foreach (GenericParameter argument in self.GenericParameters)
instance.GenericArguments.Add(argument);
return instance;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 239b1b10db80c594d93b7ba4ee2c1ec5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9133eb285bd7f3c4f89f4d7a2a079c6b
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d27bb367daea26d46a8fa5f6ca02865e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,74 @@
using FishNet.CodeGenerating.Helping.Extension;
using FishNet.Object;
using FishNet.Object.Helping;
using FishNet.Object.Prediction;
using FishNet.Object.Synchronizing;
using MonoFN.Cecil;
namespace FishNet.CodeGenerating.Helping
{
internal class AttributeHelper : CodegenBase
{
#region Reflection references.
internal string ReplicateAttribute_FullName;
internal string ReconcileAttribute_FullName;
private string ServerAttribute_FullName;
private string ClientAttribute_FullName;
private string ServerRpcAttribute_FullName;
private string ObserversRpcAttribute_FullName;
private string TargetRpcAttribute_FullName;
#endregion
public override bool ImportReferences()
{
ServerAttribute_FullName = typeof(ServerAttribute).FullName;
ClientAttribute_FullName = typeof(ClientAttribute).FullName;
ServerRpcAttribute_FullName = typeof(ServerRpcAttribute).FullName;
ObserversRpcAttribute_FullName = typeof(ObserversRpcAttribute).FullName;
TargetRpcAttribute_FullName = typeof(TargetRpcAttribute).FullName;
#if PREDICTION_1
ReplicateAttribute_FullName = typeof(ReplicateAttribute).FullName;
ReconcileAttribute_FullName = typeof(ReconcileAttribute).FullName;
#else
ReplicateAttribute_FullName = typeof(ReplicateAttribute).FullName;
ReconcileAttribute_FullName = typeof(ReconcileAttribute).FullName;
#endif
return true;
}
/// <summary>
/// Returns type of Rpc attributeFullName is for.
/// </summary>
/// <param name="attributeFullName"></param>
/// <returns></returns>
public RpcType GetRpcAttributeType(CustomAttribute ca)
{
if (ca.Is(ServerRpcAttribute_FullName))
return RpcType.Server;
else if (ca.Is(ObserversRpcAttribute_FullName))
return RpcType.Observers;
else if (ca.Is(TargetRpcAttribute_FullName))
return RpcType.Target;
else
return RpcType.None;
}
/// <summary>
/// Returns type of Rpc attributeFullName is for.
/// </summary>
/// <param name="attributeFullName"></param>
/// <returns></returns>
internal QolAttributeType GetQolAttributeType(string attributeFullName)
{
if (attributeFullName == ServerAttribute_FullName)
return QolAttributeType.Server;
else if (attributeFullName == ClientAttribute_FullName)
return QolAttributeType.Client;
else
return QolAttributeType.None;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d32f3dc23b55598429c5cfe6156e6243
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,184 @@
using FishNet.CodeGenerating.Helping;
using FishNet.CodeGenerating.ILCore;
using FishNet.CodeGenerating.Processing;
using FishNet.CodeGenerating.Processing.Rpc;
using MonoFN.Cecil;
using System.Collections.Generic;
using System.Linq;
using Unity.CompilationPipeline.Common.Diagnostics;
#if !UNITY_2020_1_OR_NEWER
using UnityEngine;
#endif
using SR = System.Reflection;
namespace FishNet.CodeGenerating
{
internal class CodegenSession
{
/// <summary>
/// Current module for this session.
/// </summary>
internal ModuleDefinition Module;
/// <summary>
/// Outputs errors when codegen fails.
/// </summary>
internal List<DiagnosticMessage> Diagnostics;
/// <summary>
/// SyncVars that are being accessed from an assembly other than the currently being processed one.
/// </summary>
internal List<FieldDefinition> DifferentAssemblySyncVars = new List<FieldDefinition>();
/// <summary>
/// CodegenBase classes for processing a module.
/// </summary>
private List<CodegenBase> _bases;
/// <summary>
/// Quick lookup of base classes.
/// </summary>
private Dictionary<string, CodegenBase> _basesCache = new Dictionary<string, CodegenBase>();
/// <summary>
/// Returns class of type if found within CodegenBase classes.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
internal T GetClass<T>() where T : CodegenBase
{
string tName = typeof(T).Name;
return (T)_basesCache[tName];
}
/// <summary>
/// Resets all helpers while importing any information needed by them.
/// </summary>
/// <param name="module"></param>
/// <returns></returns>
internal bool Initialize(ModuleDefinition module)
{
Module = module;
Diagnostics = new List<DiagnosticMessage>();
_bases = new List<CodegenBase>()
{
new ReaderImports(), new ReaderProcessor()
,new WriterImports(), new WriterProcessor()
, new PhysicsHelper(), new TimeManagerHelper(), new AttributeHelper(), new GeneralHelper()
, new ObjectHelper(), new NetworkBehaviourHelper()
, new TransportHelper()
, new NetworkConnectionImports(), new PredictedObjectHelper(), new GeneratorHelper()
, new CustomSerializerProcessor()
, new NetworkBehaviourProcessor()
, new QolAttributeProcessor()
, new RpcProcessor()
, new SyncTypeProcessor()
, new PredictionProcessor()
};
//Add all to dictionary first, then import.
foreach (CodegenBase item in _bases)
{
string tName = item.GetType().Name;
_basesCache.Add(tName, item);
}
//Initialize.
foreach (CodegenBase item in _bases)
{
item.Initialize(this);
if (!item.ImportReferences())
return false;
}
return true;
}
#region Logging.
/// <summary>
/// Logs a warning.
/// </summary>
/// <param name="msg"></param>
internal void LogWarning(string msg)
{
Diagnostics.AddWarning(msg);
}
/// <summary>
/// Logs an error.
/// </summary>
/// <param name="msg"></param>
internal void LogError(string msg)
{
Diagnostics.AddError(msg);
}
#endregion
#region ImportReference.
public MethodReference ImportReference(SR.MethodBase method)
{
return Module.ImportReference(method);
}
public MethodReference ImportReference(SR.MethodBase method, IGenericParameterProvider context)
{
return Module.ImportReference(method, context);
}
public TypeReference ImportReference(TypeReference type)
{
return Module.ImportReference(type);
}
public TypeReference ImportReference(TypeReference type, IGenericParameterProvider context)
{
return Module.ImportReference(type, context);
}
public FieldReference ImportReference(FieldReference field)
{
return Module.ImportReference(field);
}
public FieldReference ImportReference(FieldReference field, IGenericParameterProvider context)
{
return Module.ImportReference(field, context);
}
public MethodReference ImportReference(MethodReference method)
{
return Module.ImportReference(method);
}
public MethodReference ImportReference(MethodReference method, IGenericParameterProvider context)
{
return Module.ImportReference(method, context);
}
public TypeReference ImportReference(System.Type type)
{
return ImportReference(type, null);
}
public TypeReference ImportReference(System.Type type, IGenericParameterProvider context)
{
return Module.ImportReference(type, context);
}
public FieldReference ImportReference(SR.FieldInfo field)
{
return Module.ImportReference(field);
}
public FieldReference ImportReference(SR.FieldInfo field, IGenericParameterProvider context)
{
return Module.ImportReference(field, context);
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3e8416eee3308f54fa942003de975420
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 935ec97b96b35b94f8c6880c6908304c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6fee1744ec071184db72fc2443e77ece
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a66d771ab331fae408142a5c04abd74e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,41 @@
using MonoFN.Cecil;
using MonoFN.Cecil.Cil;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Unity.CompilationPipeline.Common.Diagnostics;
namespace FishNet.CodeGenerating.Helping
{
internal static class Diagnostics
{
internal static void AddError(this List<DiagnosticMessage> diagnostics, string message)
{
diagnostics.AddMessage(DiagnosticType.Error, (SequencePoint)null, message);
}
internal static void AddWarning(this List<DiagnosticMessage> diagnostics, string message)
{
diagnostics.AddMessage(DiagnosticType.Warning, (SequencePoint)null, message);
}
internal static void AddError(this List<DiagnosticMessage> diagnostics, MethodDefinition methodDef, string message)
{
diagnostics.AddMessage(DiagnosticType.Error, methodDef.DebugInformation.SequencePoints.FirstOrDefault(), message);
}
internal static void AddMessage(this List<DiagnosticMessage> diagnostics, DiagnosticType diagnosticType, SequencePoint sequencePoint, string message)
{
diagnostics.Add(new DiagnosticMessage
{
DiagnosticType = diagnosticType,
File = sequencePoint?.Document.Url.Replace($"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}", ""),
Line = sequencePoint?.StartLine ?? 0,
Column = sequencePoint?.StartColumn ?? 0,
MessageData = $" - {message}"
});
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fb7b65b572b01444cbe3c9d830cd3587
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,191 @@
using FishNet.CodeGenerating.Helping.Extension;
using MonoFN.Cecil;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace FishNet.CodeGenerating.Helping
{
internal static class Constructors
{
/// <summary>
/// Gets the first constructor that optionally has, or doesn't have parameters.
/// </summary>
/// <param name="typeRef"></param>
/// <returns></returns>
public static MethodDefinition GetFirstConstructor(this TypeReference typeRef, CodegenSession session, bool requireParameters)
{
return typeRef.CachedResolve(session).GetFirstConstructor(requireParameters);
}
/// <summary>
/// Gets the first constructor that optionally has, or doesn't have parameters.
/// </summary>
/// <param name="typeRef"></param>
/// <returns></returns>
public static MethodDefinition GetFirstConstructor(this TypeDefinition typeDef, bool requireParameters)
{
foreach (MethodDefinition methodDef in typeDef.Methods)
{
if (methodDef.IsConstructor && methodDef.IsPublic)
{
if (requireParameters && methodDef.Parameters.Count > 0)
return methodDef;
else if (!requireParameters && methodDef.Parameters.Count == 0)
return methodDef;
}
}
return null;
}
/// <summary>
/// Gets the first public constructor with no parameters.
/// </summary>
/// <returns></returns>
public static MethodDefinition GetDefaultConstructor(this TypeReference typeRef, CodegenSession session)
{
return typeRef.CachedResolve(session).GetDefaultConstructor();
}
/// <summary>
/// Gets the first public constructor with no parameters.
/// </summary>
/// <returns></returns>
public static MethodDefinition GetDefaultConstructor(this TypeDefinition typeDef)
{
foreach (MethodDefinition methodDef in typeDef.Methods)
{
if (methodDef.IsConstructor && methodDef.IsPublic && methodDef.Parameters.Count == 0)
return methodDef;
}
return null;
}
/// <summary>
/// Gets all constructors on typeDef.
/// </summary>
/// <returns></returns>
public static List<MethodDefinition> GetConstructors(this TypeDefinition typeDef)
{
List<MethodDefinition> lst = new List<MethodDefinition>();
foreach (MethodDefinition methodDef in typeDef.Methods)
{
if (methodDef.IsConstructor)
lst.Add(methodDef);
}
return lst;
}
/// <summary>
/// Gets constructor which has arguments.
/// </summary>
/// <param name="typeRef"></param>
/// <returns></returns>
public static MethodDefinition GetConstructor(this TypeReference typeRef, CodegenSession session, Type[] arguments)
{
return typeRef.CachedResolve(session).GetConstructor(arguments);
}
/// <summary>
/// Gets constructor which has arguments.
/// </summary>
/// <param name="typeDef"></param>
/// <returns></returns>
public static MethodDefinition GetConstructor(this TypeDefinition typeDef, Type[] arguments)
{
Type[] argsCopy = (arguments == null) ? new Type[0] : arguments;
foreach (MethodDefinition methodDef in typeDef.Methods)
{
if (methodDef.IsConstructor && methodDef.IsPublic && methodDef.Parameters.Count == argsCopy.Length)
{
bool match = true;
for (int i = 0; i < argsCopy.Length; i++)
{
if (methodDef.Parameters[0].ParameterType.FullName != argsCopy[i].FullName)
{
match = false;
break;
}
}
if (match)
return methodDef;
}
}
return null;
}
/// <summary>
/// Gets constructor which has arguments.
/// </summary>
/// <param name="typeRef"></param>
/// <returns></returns>
public static MethodDefinition GetConstructor(this TypeReference typeRef, CodegenSession session, TypeReference[] arguments)
{
return typeRef.CachedResolve(session).GetConstructor(arguments);
}
/// <summary>
/// Gets constructor which has arguments.
/// </summary>
/// <param name="typeDef"></param>
/// <returns></returns>
public static MethodDefinition GetConstructor(this TypeDefinition typeDef, TypeReference[] arguments)
{
TypeReference[] argsCopy = (arguments == null) ? new TypeReference[0] : arguments;
foreach (MethodDefinition methodDef in typeDef.Methods)
{
if (methodDef.IsConstructor && methodDef.IsPublic && methodDef.Parameters.Count == argsCopy.Length)
{
bool match = true;
for (int i = 0; i < argsCopy.Length; i++)
{
if (methodDef.Parameters[0].ParameterType.FullName != argsCopy[i].FullName)
{
match = false;
break;
}
}
if (match)
return methodDef;
}
}
return null;
}
/// <summary>
/// Resolves the constructor with parameterCount for typeRef.
/// </summary>
/// <param name="typeRef"></param>
/// <returns></returns>
public static MethodDefinition GetConstructor(this TypeReference typeRef, CodegenSession session, int parameterCount)
{
return typeRef.CachedResolve(session).GetConstructor(parameterCount);
}
/// <summary>
/// Resolves the constructor with parameterCount for typeRef.
/// </summary>
/// <param name="typeDef"></param>
/// <returns></returns>
public static MethodDefinition GetConstructor(this TypeDefinition typeDef, int parameterCount)
{
foreach (MethodDefinition methodDef in typeDef.Methods)
{
if (methodDef.IsConstructor && methodDef.IsPublic && methodDef.Parameters.Count == parameterCount)
return methodDef;
}
return null;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1a7e03137ca78704e999f3a3dc68b953
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,198 @@
using MonoFN.Cecil;
using MonoFN.Cecil.Cil;
using System.Collections.Generic;
namespace FishNet.CodeGenerating.Helping.Extension
{
internal static class ILProcessorExtensions
{
/// <summary>
/// Creates a debug log for text without any conditions.
/// </summary>
public static List<Instruction> DebugLog(this ILProcessor processor, CodegenSession session, string txt)
{
List<Instruction> insts = new List<Instruction>();
insts.Add(processor.Create(OpCodes.Ldstr, txt));
insts.Add(processor.Create(OpCodes.Call, session.GetClass<GeneralHelper>().Debug_LogCommon_MethodRef));
return insts;
}
/// <summary>
/// Creates a debug log for vd without any conditions.
/// </summary>
public static List<Instruction> DebugLog(this ILProcessor processor, CodegenSession session, VariableDefinition vd)
{
List<Instruction> insts = new List<Instruction>();
insts.Add(processor.Create(OpCodes.Ldloc, vd));
insts.Add(processor.Create(OpCodes.Box, vd.VariableType));
insts.Add(processor.Create(OpCodes.Call, session.GetClass<GeneralHelper>().Debug_LogCommon_MethodRef));
return insts;
}
/// <summary>
/// Creates a debug log for vd without any conditions.
/// </summary>
public static List<Instruction> DebugLog(this ILProcessor processor, CodegenSession session, FieldDefinition fd, bool loadArg0)
{
List<Instruction> insts = new List<Instruction>();
if (loadArg0)
insts.Add(processor.Create(OpCodes.Ldarg_0));
insts.Add(processor.Create(OpCodes.Ldfld, fd));
insts.Add(processor.Create(OpCodes.Box, fd.FieldType));
insts.Add(processor.Create(OpCodes.Call, session.GetClass<GeneralHelper>().Debug_LogCommon_MethodRef));
return insts;
}
/// <summary>
/// Creates a debug log for pd without any conditions.
/// </summary>
public static List<Instruction> DebugLog(this ILProcessor processor, CodegenSession session, ParameterDefinition pd)
{
List<Instruction> insts = new List<Instruction>();
insts.Add(processor.Create(OpCodes.Ldloc, pd));
insts.Add(processor.Create(OpCodes.Box, pd.ParameterType));
insts.Add(processor.Create(OpCodes.Call, session.GetClass<GeneralHelper>().Debug_LogCommon_MethodRef));
return insts;
}
/// <summary>
/// Removes Ret if the last instruction.
/// </summary>
/// <returns>True if ret was removed.</returns>
public static bool RemoveEndRet(this ILProcessor processor)
{
int instCount = processor.Body.Instructions.Count;
if (instCount == 0)
return false;
if (processor.Body.Instructions[instCount-1].OpCode == OpCodes.Ret)
{
processor.Body.Instructions.RemoveAt(instCount - 1);
return true;
}
else
{
return false;
}
}
///// <summary>
///// Creates a debug log for mr without any conditions.
///// </summary>
//public static void DebugLog(this ILProcessor processor, MethodReference mr)
//{
// processor.Emit(OpCodes.Call, mr);
// processor.Emit(OpCodes.Box, mr.ReturnType);
// processor.Emit(OpCodes.Call, base.GetClass<GeneralHelper>().Debug_LogCommon_MethodRef);
//}
/// <summary>
/// Inserts instructions at the beginning.
/// </summary>
/// <param name="processor"></param>
/// <param name="instructions"></param>
public static void InsertAt(this ILProcessor processor, int target, List<Instruction> instructions)
{
for (int i = 0; i < instructions.Count; i++)
processor.Body.Instructions.Insert(i + target, instructions[i]);
}
/// <summary>
/// Inserts instructions at the beginning.
/// </summary>
/// <param name="processor"></param>
/// <param name="instructions"></param>
public static void InsertFirst(this ILProcessor processor, List<Instruction> instructions)
{
for (int i = 0; i < instructions.Count; i++)
processor.Body.Instructions.Insert(i, instructions[i]);
}
/// <summary>
/// Inserts instructions at the end while also moving Ret down.
/// </summary>
/// <param name="processor"></param>
/// <param name="instructions"></param>
public static void InsertLast(this ILProcessor processor, List<Instruction> instructions)
{
bool retRemoved = false;
int startingCount = processor.Body.Instructions.Count;
//Remove ret if it exist and add it back in later.
if (startingCount > 0)
{
if (processor.Body.Instructions[startingCount - 1].OpCode == OpCodes.Ret)
{
processor.Body.Instructions.RemoveAt(startingCount - 1);
retRemoved = true;
}
}
foreach (Instruction inst in instructions)
processor.Append(inst);
//Add ret back if it was removed.
if (retRemoved)
processor.Emit(OpCodes.Ret);
}
/// <summary>
/// Inserts instructions before target.
/// </summary>
/// <param name="processor"></param>
/// <param name="instructions"></param>
public static void InsertBefore(this ILProcessor processor, Instruction target, List<Instruction> instructions)
{
int index = processor.Body.Instructions.IndexOf(target);
for (int i = 0; i < instructions.Count; i++)
processor.Body.Instructions.Insert(index + i, instructions[i]);
}
/// <summary>
/// Adds instructions to the end of processor.
/// </summary>
/// <param name="processor"></param>
/// <param name="instructions"></param>
public static void Add(this ILProcessor processor, List<Instruction> instructions)
{
for (int i = 0; i < instructions.Count; i++)
processor.Body.Instructions.Add(instructions[i]);
}
/// <summary>
/// Inserts instructions before returns. Only works on void types.
/// </summary>
/// <param name="processor"></param>
/// <param name="instructions"></param>
public static void InsertBeforeReturns(this ILProcessor processor, CodegenSession session, List<Instruction> instructions)
{
if (processor.Body.Method.ReturnType.FullName != session.Module.TypeSystem.Void.FullName)
{
session.LogError($"Cannot insert instructions before returns on {processor.Body.Method.FullName} because it does not return void.");
return;
}
/* Insert at the end of the method
* and get the first instruction that was inserted.
* Any returns or breaks which would exit the method
* will jump to this instruction instead. */
processor.InsertLast(instructions);
Instruction startInst = processor.Body.Instructions[processor.Body.Instructions.Count - instructions.Count];
//Look for anything that jumps to rets.
for (int i = 0; i < processor.Body.Instructions.Count; i++)
{
Instruction inst = processor.Body.Instructions[i];
if (inst.Operand is Instruction operInst)
{
if (operInst.OpCode == OpCodes.Ret)
inst.Operand = startInst;
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 820cf8401d4d71c4196dda444559ef8a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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
{
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 360667149f16b6c4aba61fd05427cbfb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,174 @@
using FishNet.CodeGenerating.Extension;
using MonoFN.Cecil;
using MonoFN.Cecil.Rocks;
using System;
namespace FishNet.CodeGenerating.Helping.Extension
{
internal static class MethodReferenceExtensions
{
/// <summary>
/// Returns a custom attribute.
/// </summary>
public static CustomAttribute GetCustomAttribute(this MethodReference mr, string attributeFullName)
{
MethodDefinition md = mr.Resolve();
return MethodDefinitionExtensions.GetCustomAttribute(md, attributeFullName);
}
/// <summary>
/// Makes a generic method with specified arguments.
/// </summary>
/// <param name="method"></param>
/// <param name="genericArguments"></param>
/// <returns></returns>
public static GenericInstanceMethod MakeGenericMethod(this MethodReference method, params TypeReference[] genericArguments)
{
GenericInstanceMethod result = new GenericInstanceMethod(method);
foreach (TypeReference argument in genericArguments)
result.GenericArguments.Add(argument);
return result;
}
/// <summary>
/// Makes a generic method with the same arguments as the original.
/// </summary>
/// <param name="method"></param>
/// <returns></returns>
public static GenericInstanceMethod MakeGenericMethod(this MethodReference method)
{
GenericInstanceMethod result = new GenericInstanceMethod(method);
foreach (ParameterDefinition pd in method.Parameters)
result.GenericArguments.Add(pd.ParameterType);
return result;
}
/// <summary>
/// Returns a method reference for a generic method.
/// </summary>
public static MethodReference GetMethodReference(this MethodReference mr, CodegenSession session, TypeReference typeReference)
{
return mr.GetMethodReference(session, new TypeReference[] { typeReference });
}
/// <summary>
/// Returns a method reference for a generic method.
/// </summary>
public static MethodReference GetMethodReference(this MethodReference mr, CodegenSession session, TypeReference[] typeReferences)
{
if (mr.HasGenericParameters)
{
if (typeReferences == null || typeReferences.Length == 0)
{
session.LogError($"Method {mr.Name} has generic parameters but TypeReferences are null or 0 length.");
return null;
}
else
{
GenericInstanceMethod gim = mr.MakeGenericMethod(typeReferences);
return gim;
}
}
else
{
return mr;
}
}
/// <summary>
/// Gets a Resolve favoring cached results first.
/// </summary>
internal static MethodDefinition CachedResolve(this MethodReference methodRef, CodegenSession session)
{
return session.GetClass<GeneralHelper>().GetMethodReferenceResolve(methodRef);
}
/// <summary>
/// Removes ret if it exist at the end of the method. Returns if ret was removed.
/// </summary>
internal static bool RemoveEndRet(this MethodReference mr, CodegenSession session)
{
MethodDefinition md = mr.CachedResolve(session);
return MethodDefinitionExtensions.RemoveEndRet(md, session);
}
/// <summary>
/// Given a method of a generic class such as ArraySegment`T.get_Count,
/// and a generic instance such as ArraySegment`int
/// Creates a reference to the specialized method ArraySegment`int`.get_Count
/// <para> Note that calling ArraySegment`T.get_Count directly gives an invalid IL error </para>
/// </summary>
/// <param name="self"></param>
/// <param name="instanceType"></param>
/// <returns></returns>
public static MethodReference MakeHostInstanceGeneric(this MethodReference self, CodegenSession session, GenericInstanceType instanceType)
{
MethodReference reference = new MethodReference(self.Name, self.ReturnType, instanceType)
{
CallingConvention = self.CallingConvention,
HasThis = self.HasThis,
ExplicitThis = self.ExplicitThis
};
foreach (ParameterDefinition parameter in self.Parameters)
reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
foreach (GenericParameter generic_parameter in self.GenericParameters)
reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference));
return session.ImportReference(reference);
}
/// <summary>
/// Given a method of a generic class such as ArraySegment`T.get_Count,
/// and a generic instance such as ArraySegment`int
/// Creates a reference to the specialized method ArraySegment`int`.get_Count
/// <para> Note that calling ArraySegment`T.get_Count directly gives an invalid IL error </para>
/// </summary>
/// <param name="self"></param>
/// <param name="instanceType"></param>
/// <returns></returns>
public static MethodReference MakeHostInstanceGeneric(this MethodReference self, TypeReference typeRef, params TypeReference[] args)
{
GenericInstanceType git = typeRef.MakeGenericInstanceType(args);
MethodReference reference = new MethodReference(self.Name, self.ReturnType, git)
{
CallingConvention = self.CallingConvention,
HasThis = self.HasThis,
ExplicitThis = self.ExplicitThis
};
foreach (ParameterDefinition parameter in self.Parameters)
reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
foreach (GenericParameter generic_parameter in self.GenericParameters)
reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference));
return reference;
}
public static bool Is<T>(this MethodReference method, string name)
{
return method.DeclaringType.Is<T>() && method.Name == name;
}
public static bool Is<T>(this TypeReference td)
{
return Is(td, typeof(T));
}
public static bool Is(this TypeReference td, Type t)
{
if (t.IsGenericType)
{
return td.GetElementType().FullName == t.FullName;
}
return td.FullName == t.FullName;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ee1c15a06ab386e439ec5aa41e3496f7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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()}");
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 42648785493390646898f5fa13e1dfd6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 31071055a2e388141b8f11e1ba4e147e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,500 @@
using FishNet.CodeGenerating.Extension;
using MonoFN.Cecil;
using MonoFN.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace FishNet.CodeGenerating.Helping.Extension
{
internal static class TypeDefinitionExtensionsOld
{
/// <summary>
/// Creates a GenericInstanceType and adds parameters.
/// </summary>
internal static GenericInstanceType CreateGenericInstanceType(this TypeDefinition type, Collection<GenericParameter> parameters)
{
GenericInstanceType git = new GenericInstanceType(type);
foreach (GenericParameter gp in parameters)
git.GenericArguments.Add(gp);
return git;
}
/// <summary>
/// Finds public fields in type and base type
/// </summary>
/// <param name="variable"></param>
/// <returns></returns>
public static IEnumerable<FieldDefinition> FindAllPublicFields(this TypeDefinition typeDef, CodegenSession session
, System.Type[] excludedBaseTypes = null, string[] excludedAssemblyPrefixes = null)
{
GeneralHelper gh = session.GetClass<GeneralHelper>();
while (typeDef != null)
{
if (IsExcluded(typeDef, excludedBaseTypes, excludedAssemblyPrefixes))
break;
foreach (FieldDefinition fd in typeDef.Fields)
{
if (fd.IsStatic)
continue;
if (fd.IsNotSerialized)
continue;
if (gh.HasExcludeSerializationAttribute(fd))
continue;
if (fd.IsPrivate)
continue;
yield return fd;
}
try { typeDef = typeDef.BaseType?.CachedResolve(session); }
catch { break; }
}
}
/// <summary>
/// Finds public properties on typeDef and all base types which have a public get/set accessor.
/// </summary>
/// <param name="typeDef"></param>
/// <returns></returns>
public static IEnumerable<PropertyDefinition> FindAllPublicProperties(this TypeDefinition typeDef, CodegenSession session
, System.Type[] excludedBaseTypes = null, string[] excludedAssemblyPrefixes = null)
{
GeneralHelper gh = session.GetClass<GeneralHelper>();
while (typeDef != null)
{
if (IsExcluded(typeDef, excludedBaseTypes, excludedAssemblyPrefixes))
break;
foreach (PropertyDefinition pd in typeDef.Properties)
{
//Missing get or set method.
if (pd.GetMethod == null || pd.SetMethod == null)
continue;
if (gh.HasExcludeSerializationAttribute(pd))
continue;
if (pd.GetMethod.IsPrivate)
continue;
if (pd.SetMethod.IsPrivate)
continue;
if (pd.GetMethod.ReturnType.IsGenericParameter)
continue;
yield return pd;
}
try { typeDef = typeDef.BaseType?.CachedResolve(session); }
catch { break; }
}
}
/// <summary>
/// Returns if typeDef is excluded.
/// </summary>
private static bool IsExcluded(TypeDefinition typeDef, System.Type[] excludedBaseTypes = null, string[] excludedAssemblyPrefixes = null)
{
if (excludedBaseTypes != null)
{
foreach (System.Type t in excludedBaseTypes)
{
if (typeDef.FullName == t.FullName)
return true;
}
}
if (excludedAssemblyPrefixes != null)
{
foreach (string s in excludedAssemblyPrefixes)
{
int len = s.Length;
string tdAsmName = typeDef.Module.Assembly.FullName;
if (tdAsmName.Length >= len && tdAsmName.Substring(0, len).ToLower() == s.ToLower())
return true;
}
}
//Fall through, not excluded.
return false;
}
/// <summary>
/// Returns if typeDef is excluded.
/// </summary>
public static bool IsExcluded(this TypeDefinition typeDef, string excludedAssemblyPrefix)
{
int len = excludedAssemblyPrefix.Length;
string tdAsmName = typeDef.Module.Assembly.FullName;
if (tdAsmName.Length >= len && tdAsmName.Substring(0, len).ToLower() == excludedAssemblyPrefix.ToLower())
return true;
//Fall through, not excluded.
return false;
}
/// <summary>
/// Returns if typeDef or any of it's parents inherit from NetworkBehaviour.
/// </summary>
/// <param name="typeDef"></param>
/// <returns></returns>
internal static bool InheritsNetworkBehaviour(this TypeDefinition typeDef, CodegenSession session)
{
string nbFullName = session.GetClass<NetworkBehaviourHelper>().FullName;
TypeDefinition copyTd = typeDef;
while (copyTd != null)
{
if (copyTd.FullName == nbFullName)
return true;
copyTd = copyTd.GetNextBaseTypeDefinition(session);
}
//Fall through, network behaviour not found.
return false;
}
/// <summary>
/// Returns a nested TypeDefinition of name.
/// </summary>
internal static TypeDefinition GetNestedType(this TypeDefinition typeDef, string name)
{
foreach (TypeDefinition nestedTd in typeDef.NestedTypes)
{
if (nestedTd.Name == name)
return nestedTd;
}
return null;
}
/// <summary>
/// Returns if the BaseType for TypeDef exist and is not NetworkBehaviour,
/// </summary>
/// <param name="typeDef"></param>
/// <returns></returns>
internal static bool CanProcessBaseType(this TypeDefinition typeDef, CodegenSession session)
{
return (typeDef != null && typeDef.BaseType != null && typeDef.BaseType.FullName != session.GetClass<NetworkBehaviourHelper>().FullName);
}
/// <summary>
/// Returns if the BaseType for TypeDef exist and is not NetworkBehaviour,
/// </summary>
/// <param name="typeDef"></param>
/// <returns></returns>
internal static TypeDefinition GetNextBaseClassToProcess(this TypeDefinition typeDef, CodegenSession session)
{
if (typeDef.BaseType != null && typeDef.BaseType.FullName != session.GetClass<NetworkBehaviourHelper>().FullName)
return typeDef.BaseType.CachedResolve(session);
else
return null;
}
internal static TypeDefinition GetLastBaseClass(this TypeDefinition typeDef, CodegenSession session)
{
TypeDefinition copyTd = typeDef;
while (copyTd.BaseType != null)
copyTd = copyTd.BaseType.CachedResolve(session);
return copyTd;
}
/// <summary>
/// Searches for a type in current and inherited types.
/// </summary>
internal static TypeDefinition GetClassInInheritance(this TypeDefinition typeDef, CodegenSession session, string typeFullName)
{
TypeDefinition copyTd = typeDef;
do
{
if (copyTd.FullName == typeFullName)
return copyTd;
if (copyTd.BaseType != null)
copyTd = copyTd.BaseType.CachedResolve(session);
else
copyTd = null;
} while (copyTd != null);
//Not found.
return null;
}
/// <summary>
/// Searches for a type in current and inherited types.
/// </summary>
internal static TypeDefinition GetClassInInheritance(this TypeDefinition typeDef, CodegenSession session, TypeDefinition targetTypeDef)
{
if (typeDef == null)
return null;
TypeDefinition copyTd = typeDef;
do
{
if (copyTd == targetTypeDef)
return copyTd;
if (copyTd.BaseType != null)
copyTd = copyTd.BaseType.CachedResolve(session);
else
copyTd = null;
} while (copyTd != null);
//Not found.
return null;
}
/// <summary>
/// Returns if typeDef is static (abstract, sealed).
/// </summary>
internal static bool IsStatic(this TypeDefinition typeDef)
{
//Combining flags in a single check some reason doesn't work right with HasFlag.
return (typeDef.Attributes.HasFlag(TypeAttributes.Abstract) && typeDef.Attributes.HasFlag(TypeAttributes.Sealed));
}
/// <summary>
/// Gets an enum underlying type for typeDef.
/// </summary>
/// <param name="typeDef"></param>
/// <returns></returns>
internal static TypeReference GetEnumUnderlyingTypeReference(this TypeDefinition typeDef)
{
foreach (FieldDefinition field in typeDef.Fields)
{
if (!field.IsStatic)
return field.FieldType;
}
throw new ArgumentException($"Invalid enum {typeDef.FullName}");
}
/// <summary>
/// Returns if typeDef is derived from type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="typeDef"></param>
/// <returns></returns>
internal static bool InheritsFrom<T>(this TypeDefinition typeDef, CodegenSession session)
{
return typeDef.InheritsFrom(session, typeof(T));
}
/// <summary>
/// Returns if typeDef is derived from type.
/// </summary>
/// <param name="typeDef"></param>
/// <param name="type"></param>
/// <returns></returns>
internal static bool InheritsFrom(this TypeDefinition typeDef, CodegenSession session, Type type)
{
if (!typeDef.IsClass)
return false;
TypeDefinition copyTd = typeDef;
while (copyTd.BaseType != null)
{
if (copyTd.BaseType.IsType(type))
return true;
copyTd = copyTd.GetNextBaseTypeDefinition(session);
}
//Fall through.
return false;
}
/// <summary>
/// Adds a method to typeDef.
/// </summary>
/// <param name="typDef"></param>
/// <param name="methodName"></param>
/// <param name="attributes"></param>
/// <returns></returns>
internal static MethodDefinition AddMethod(this TypeDefinition typDef, string methodName, MethodAttributes attributes)
{
return AddMethod(typDef, methodName, attributes, typDef.Module.ImportReference(typeof(void)));
}
/// <summary>
/// Adds a method to typeDef.
/// </summary>
/// <param name="typeDef"></param>
/// <param name="methodName"></param>
/// <param name="attributes"></param>
/// <param name="typeReference"></param>
/// <returns></returns>
internal static MethodDefinition AddMethod(this TypeDefinition typeDef, string methodName, MethodAttributes attributes, TypeReference typeReference)
{
var method = new MethodDefinition(methodName, attributes, typeReference);
typeDef.Methods.Add(method);
return method;
}
/// <summary>
/// Returns if a type is a subclass of another.
/// </summary>
/// <param name="typeDef"></param>
/// <param name="ClassTypeFullName"></param>
/// <returns></returns>
internal static bool IsSubclassOf(this TypeDefinition typeDef,CodegenSession session, string ClassTypeFullName)
{
if (!typeDef.IsClass) return false;
TypeReference baseTypeRef = typeDef.BaseType;
while (baseTypeRef != null)
{
if (baseTypeRef.FullName == ClassTypeFullName)
{
return true;
}
try
{
baseTypeRef = baseTypeRef.CachedResolve(session).BaseType;
}
catch
{
return false;
}
}
return false;
}
/// <summary>
/// Gets a field reference by name.
/// </summary>
/// <param name="typeDef"></param>
/// <param name="fieldName"></param>
/// <returns></returns>
public static FieldReference GetFieldReference(this TypeDefinition typeDef, string fieldName, CodegenSession session)
{
if (typeDef.HasFields)
{
for (int i = 0; i < typeDef.Fields.Count; i++)
{
if (typeDef.Fields[i].Name == fieldName)
return session.ImportReference(typeDef.Fields[i]);
}
}
return null;
}
/// <summary>
/// Returns if the TypeDefinition implements TInterface.
/// </summary>
/// <typeparam name="TInterface"></typeparam>
/// <param name="typeDef"></param>
/// <returns></returns>
public static bool ImplementsInterface<TInterface>(this TypeDefinition typeDef)
{
if (typeDef.Interfaces == null)
return false;
for (int i = 0; i < typeDef.Interfaces.Count; i++)
{
if (typeDef.Interfaces[i].InterfaceType.Is<TInterface>())
return true;
}
return false;
}
/// <summary>
/// Returns if the TypeDefinition implements TInterface.
/// </summary>
/// <typeparam name="TInterface"></typeparam>
/// <param name="typeDef"></param>
/// <returns></returns>
public static bool ImplementsInterface(this TypeDefinition typeDef, string interfaceName)
{
for (int i = 0; i < typeDef.Interfaces.Count; i++)
{
if (typeDef.Interfaces[i].InterfaceType.FullName == interfaceName)
return true;
}
return false;
}
/// <summary>
/// Returns if the TypeDefinition implements TInterface.
/// </summary>
/// <typeparam name="TInterface"></typeparam>
/// <param name="typeDef"></param>
/// <returns></returns>
public static bool ImplementsInterfaceRecursive<T>(this TypeDefinition typeDef, CodegenSession session)
{
TypeDefinition climbTypeDef = typeDef;
while (climbTypeDef != null)
{
if (climbTypeDef.Interfaces.Any(i => i.InterfaceType.Is<T>()))
return true;
try
{
if (climbTypeDef.BaseType != null)
climbTypeDef = climbTypeDef.BaseType.CachedResolve(session);
else
climbTypeDef = null;
}
//Could not resolve assembly; can happen for assemblies being checked outside FishNet/csharp.
catch (AssemblyResolutionException)
{
break;
}
}
return false;
}
/// <summary>
/// Returns if the TypeDefinition implements TInterface.
/// </summary>
/// <typeparam name="TInterface"></typeparam>
/// <param name="typeDef"></param>
/// <returns></returns>
public static bool ImplementsInterfaceRecursive(this TypeDefinition typeDef, CodegenSession session, string interfaceName)
{
TypeDefinition climbTypeDef = typeDef;
while (climbTypeDef != null)
{
if (climbTypeDef.Interfaces.Any(i => i.InterfaceType.FullName == interfaceName))
return true;
try
{
if (climbTypeDef.BaseType != null)
climbTypeDef = climbTypeDef.BaseType.CachedResolve(session);
else
climbTypeDef = null;
}
//Could not resolve assembly; can happen for assemblies being checked outside FishNet/csharp.
catch (AssemblyResolutionException)
{
break;
}
}
return false;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 645e49fe7eeff3a4e9eb65d77fc6e2ca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,138 @@
using MonoFN.Cecil;
using MonoFN.Cecil.Rocks;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace FishNet.CodeGenerating.Helping.Extension
{
internal static class TypeReferenceExtensionsOld
{
/// <summary>
/// Gets a Resolve favoring cached results first.
/// </summary>
internal static TypeDefinition CachedResolve(this TypeReference typeRef, CodegenSession session)
{
return session.GetClass<GeneralHelper>().GetTypeReferenceResolve(typeRef);
}
/// <summary>
/// Returns if typeRef is a class or struct.
/// </summary>
internal static bool IsClassOrStruct(this TypeReference typeRef, CodegenSession session)
{
TypeDefinition typeDef = typeRef.CachedResolve(session);
return (!typeDef.IsPrimitive && !typeDef.IsEnum && (typeDef.IsClass || typeDef.IsValueType));
}
/// <summary>
/// Returns all properties on typeRef and all base types which have a public get/set accessor.
/// </summary>
/// <param name="typeRef"></param>
/// <returns></returns>
public static IEnumerable<PropertyDefinition> FindAllSerializableProperties(this TypeReference typeRef, CodegenSession session, System.Type[] excludedBaseTypes = null, string[] excludedAssemblyPrefixes = null)
{
return typeRef.CachedResolve(session).FindAllPublicProperties(session, excludedBaseTypes, excludedAssemblyPrefixes);
}
/// <summary>
/// Gets all public fields in typeRef and base type.
/// </summary>
/// <param name="typeRef"></param>
/// <returns></returns>
public static IEnumerable<FieldDefinition> FindAllSerializableFields(this TypeReference typeRef, CodegenSession session,
System.Type[] excludedBaseTypes = null, string[] excludedAssemblyPrefixes = null)
{
return typeRef.Resolve().FindAllPublicFields(session, excludedBaseTypes, excludedAssemblyPrefixes);
}
/// <summary>
/// Returns if a typeRef is type.
/// </summary>
/// <param name="typeRef"></param>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsType(this TypeReference typeRef, Type type)
{
if (type.IsGenericType)
return typeRef.GetElementType().FullName == type.FullName;
else
return typeRef.FullName == type.FullName;
}
/// <summary>
/// Returns if typeRef is a multidimensional array.
/// </summary>
/// <param name="typeRef"></param>
/// <returns></returns>
public static bool IsMultidimensionalArray(this TypeReference typeRef)
{
return typeRef is ArrayType arrayType && arrayType.Rank > 1;
}
/// <summary>
/// Returns if typeRef can be resolved.
/// </summary>
/// <param name="typeRef"></param>
/// <returns></returns>
public static bool CanBeResolved(this TypeReference typeRef, CodegenSession session)
{
while (typeRef != null)
{
if (typeRef.Scope.Name == "Windows")
{
return false;
}
if (typeRef.Scope.Name == "mscorlib")
{
TypeDefinition resolved = typeRef.CachedResolve(session);
return resolved != null;
}
try
{
typeRef = typeRef.CachedResolve(session).BaseType;
}
catch
{
return false;
}
}
return true;
}
/// <summary>
/// Creates a generic type out of another type, if needed.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static TypeReference ConvertToGenericIfNeeded(this TypeDefinition type)
{
if (type.HasGenericParameters)
{
// get all the generic parameters and make a generic instance out of it
TypeReference[] genericTypes = new TypeReference[type.GenericParameters.Count];
for (int i = 0; i < type.GenericParameters.Count; i++)
{
genericTypes[i] = type.GenericParameters[i].GetElementType();
}
return type.MakeGenericInstanceType(genericTypes);
}
else
{
return type;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2344f5ab0fda07b498c03fbe0e082c14
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6139ff104f3c24442b26dbc4e40d5ce5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,507 @@
using FishNet.CodeGenerating.Extension;
using FishNet.CodeGenerating.Helping.Extension;
using FishNet.CodeGenerating.Processing;
using FishNet.Configuring;
using FishNet.Managing.Logging;
using FishNet.Object;
using FishNet.Object.Delegating;
using FishNet.Object.Helping;
using FishNet.Object.Prediction.Delegating;
using MonoFN.Cecil;
using MonoFN.Cecil.Cil;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace FishNet.CodeGenerating.Helping
{
internal class NetworkBehaviourHelper : CodegenBase
{
#region Reflection references.
//Names.
internal string FullName;
//Prediction.
#if PREDICTION_1
public string ClearReplicateCache_MethodName = nameof(NetworkBehaviour.ClearReplicateCache_Virtual);
#else
public string Reconcile_Client_Start_MethodName = nameof(NetworkBehaviour.Reconcile_Client_Start);
public string Replicate_Replay_Start_MethodName = nameof(NetworkBehaviour.Replicate_Replay_Start);
public MethodReference Replicate_NonAuthoritative_MethodRef;
public MethodReference Replicate_Authortative_MethodRef;
#endif
#if PREDICTION_1
public MethodReference Replicate_Owner_MethodRef;
public MethodReference Replicate_NonOwner_MethodRef;
#endif
public MethodReference Replicate_Reader_MethodRef;
#if PREDICTION_1
public MethodReference Replicate_ExitEarly_A_MethodRef;
public MethodReference Reconcile_ExitEarly_A_MethodRef;
#endif
public MethodReference Reconcile_Server_MethodRef;
//public FieldReference UsesPrediction_FieldRef;
public MethodReference Replicate_Replay_Start_MethodRef;
public MethodReference Reconcile_Client_MethodRef;
public MethodReference Replicate_Replay_MethodRef;
public MethodReference Reconcile_Reader_MethodRef;
public MethodReference RegisterReplicateRpc_MethodRef;
public MethodReference RegisterReconcileRpc_MethodRef;
public MethodReference ReplicateRpcDelegate_Ctor_MethodRef;
public MethodReference ReconcileRpcDelegate_Ctor_MethodRef;
//public MethodReference Replicate_Server_SendToSpectators_MethodRef;
//RPCs.
public MethodReference SendServerRpc_MethodRef;
public MethodReference SendObserversRpc_MethodRef;
public MethodReference SendTargetRpc_MethodRef;
public MethodReference DirtySyncType_MethodRef;
public MethodReference RegisterServerRpc_MethodRef;
public MethodReference RegisterObserversRpc_MethodRef;
public MethodReference RegisterTargetRpc_MethodRef;
public MethodReference ServerRpcDelegate_Ctor_MethodRef;
public MethodReference ClientRpcDelegate_Ctor_MethodRef;
//Is checks.
public MethodReference IsClientInitialized_MethodRef;
public MethodReference IsOwner_MethodRef;
public MethodReference IsServerInitialized_MethodRef;
public MethodReference IsHost_MethodRef;
public MethodReference IsNetworked_MethodRef;
//Misc.
public TypeReference TypeRef;
public MethodReference OwnerMatches_MethodRef;
public MethodReference LocalConnection_MethodRef;
public MethodReference Owner_MethodRef;
public MethodReference NetworkInitializeIfDisabled_MethodRef;
//TimeManager.
public MethodReference TimeManager_MethodRef;
#endregion
#region Const.
internal const uint MAX_SYNCTYPE_ALLOWANCE = byte.MaxValue;
internal const uint MAX_RPC_ALLOWANCE = ushort.MaxValue;
internal const uint MAX_PREDICTION_ALLOWANCE = byte.MaxValue;
internal const string AWAKE_METHOD_NAME = "Awake";
internal const string DISABLE_LOGGING_TEXT = "This message may be disabled by setting the Logging field in your attribute to LoggingType.Off";
#endregion
public override bool ImportReferences()
{
Type networkBehaviourType = typeof(NetworkBehaviour);
TypeRef = base.ImportReference(networkBehaviourType);
FullName = networkBehaviourType.FullName;
base.ImportReference(networkBehaviourType);
//ServerRpcDelegate and ClientRpcDelegate constructors.
ServerRpcDelegate_Ctor_MethodRef = base.ImportReference(typeof(ServerRpcDelegate).GetConstructors().First());
ClientRpcDelegate_Ctor_MethodRef = base.ImportReference(typeof(ClientRpcDelegate).GetConstructors().First());
//Prediction Rpc delegate constructors.
ReplicateRpcDelegate_Ctor_MethodRef = base.ImportReference(typeof(ReplicateRpcDelegate).GetConstructors().First());
ReconcileRpcDelegate_Ctor_MethodRef = base.ImportReference(typeof(ReconcileRpcDelegate).GetConstructors().First());
foreach (MethodInfo mi in networkBehaviourType.GetMethods((BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic)))
{
//CreateDelegates.
if (mi.Name == nameof(NetworkBehaviour.RegisterServerRpc))
RegisterServerRpc_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.RegisterObserversRpc))
RegisterObserversRpc_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.RegisterTargetRpc))
RegisterTargetRpc_MethodRef = base.ImportReference(mi);
//Prediction delegates.
else if (mi.Name == nameof(NetworkBehaviour.RegisterReplicateRpc))
RegisterReplicateRpc_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.RegisterReconcileRpc))
RegisterReconcileRpc_MethodRef = base.ImportReference(mi);
//SendRpcs.
else if (mi.Name == nameof(NetworkBehaviour.SendServerRpc))
SendServerRpc_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.SendObserversRpc))
SendObserversRpc_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.SendTargetRpc))
SendTargetRpc_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.Replicate_Reader))
Replicate_Reader_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Reader))
Reconcile_Reader_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Server))
Reconcile_Server_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Client))
Reconcile_Client_MethodRef = base.ImportReference(mi);
//#if !PREDICTION_1
// else if (mi.Name == nameof(NetworkBehaviour.Replicate_Server_SendToSpectators_Internal))
// Replicate_Server_SendToSpectators_MethodRef = base.ImportReference(mi);
//#endif
//Misc.
else if (mi.Name == nameof(NetworkBehaviour.OwnerMatches))
OwnerMatches_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.DirtySyncType))
DirtySyncType_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.NetworkInitializeIfDisabled))
NetworkInitializeIfDisabled_MethodRef = base.ImportReference(mi);
//Prediction
#if PREDICTION_1
else if (mi.Name == nameof(NetworkBehaviour.Replicate_ExitEarly_A))
Replicate_ExitEarly_A_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.Reconcile_ExitEarly_A))
Reconcile_ExitEarly_A_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.Replicate_Owner))
Replicate_Owner_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.Replicate_NonOwner))
Replicate_NonOwner_MethodRef = base.ImportReference(mi);
#else
else if (mi.Name == nameof(NetworkBehaviour.Replicate_Authoritative))
Replicate_Authortative_MethodRef = base.ImportReference(mi);
else if (mi.Name == Replicate_Replay_Start_MethodName)
Replicate_Replay_Start_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.Replicate_Replay))
Replicate_Replay_MethodRef = base.ImportReference(mi);
else if (mi.Name == nameof(NetworkBehaviour.Replicate_NonAuthoritative))
Replicate_NonAuthoritative_MethodRef = base.ImportReference(mi);
#endif
}
foreach (PropertyInfo pi in networkBehaviourType.GetProperties((BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic)))
{
//Server/Client states.
if (pi.Name == nameof(NetworkBehaviour.IsClientInitialized))
IsClientInitialized_MethodRef = base.ImportReference(pi.GetMethod);
else if (pi.Name == nameof(NetworkBehaviour.IsServerInitialized))
IsServerInitialized_MethodRef = base.ImportReference(pi.GetMethod);
else if (pi.Name == nameof(NetworkBehaviour.IsHostStarted))
IsHost_MethodRef = base.ImportReference(pi.GetMethod);
else if (pi.Name == nameof(NetworkBehaviour.IsOwner))
IsOwner_MethodRef = base.ImportReference(pi.GetMethod);
else if (pi.Name == nameof(NetworkBehaviour.IsNetworked))
IsNetworked_MethodRef = base.ImportReference(pi.GetMethod);
//Owner.
else if (pi.Name == nameof(NetworkBehaviour.Owner))
Owner_MethodRef = base.ImportReference(pi.GetMethod);
else if (pi.Name == nameof(NetworkBehaviour.LocalConnection))
LocalConnection_MethodRef = base.ImportReference(pi.GetMethod);
//Misc.
else if (pi.Name == nameof(NetworkBehaviour.TimeManager))
TimeManager_MethodRef = base.ImportReference(pi.GetMethod);
}
//#if !PREDICTION_1
// foreach (FieldInfo fi in networkBehaviourType.GetFields((BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic)))
// {
// if (fi.Name == nameof(NetworkBehaviour.UsesPrediction))
// {
// UsesPrediction_FieldRef = base.ImportReference(fi);
// break;
// }
// }
//#endif
return true;
}
/// <summary>
/// Returnsthe child most Awake by iterating up childMostTypeDef.
/// </summary>
/// <param name="childMostTypeDef"></param>
/// <param name="created"></param>
/// <returns></returns>
internal MethodDefinition GetAwakeMethodDefinition(TypeDefinition typeDef)
{
return typeDef.GetMethod(AWAKE_METHOD_NAME);
}
/// <summary>
/// Creates a replicate delegate.
/// </summary>
/// <param name="processor"></param>
/// <param name="originalMethodDef"></param>
/// <param name="readerMethodDef"></param>
/// <param name="rpcType"></param>
internal void CreateReplicateDelegate(MethodDefinition originalMethodDef, MethodDefinition readerMethodDef, uint methodHash)
{
MethodDefinition methodDef = originalMethodDef.DeclaringType.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
ILProcessor processor = methodDef.Body.GetILProcessor();
List<Instruction> insts = new List<Instruction>();
insts.Add(processor.Create(OpCodes.Ldarg_0));
insts.Add(processor.Create(OpCodes.Ldc_I4, (int)methodHash));
/* Create delegate and call NetworkBehaviour method. */
insts.Add(processor.Create(OpCodes.Ldnull));
insts.Add(processor.Create(OpCodes.Ldftn, readerMethodDef));
/* Has to be done last. This allows the NetworkBehaviour to
* initialize it's fields first. */
processor.InsertLast(insts);
}
/// <summary>
/// Creates a RPC delegate for rpcType.
/// </summary>
/// <param name="processor"></param>
/// <param name="originalMethodDef"></param>
/// <param name="readerMethodDef"></param>
/// <param name="rpcType"></param>
internal void CreateRpcDelegate(bool runLocally, TypeDefinition typeDef, MethodDefinition readerMethodDef, RpcType rpcType, uint methodHash, CustomAttribute rpcAttribute)
{
MethodDefinition methodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
ILProcessor processor = methodDef.Body.GetILProcessor();
List<Instruction> insts = new List<Instruction>();
insts.Add(processor.Create(OpCodes.Ldarg_0));
insts.Add(processor.Create(OpCodes.Ldc_I4, (int)methodHash));
/* Create delegate and call NetworkBehaviour method. */
insts.Add(processor.Create(OpCodes.Ldarg_0));
insts.Add(processor.Create(OpCodes.Ldftn, readerMethodDef));
//Server.
if (rpcType == RpcType.Server)
{
insts.Add(processor.Create(OpCodes.Newobj, ServerRpcDelegate_Ctor_MethodRef));
insts.Add(processor.Create(OpCodes.Call, RegisterServerRpc_MethodRef));
}
//Observers.
else if (rpcType == RpcType.Observers)
{
insts.Add(processor.Create(OpCodes.Newobj, ClientRpcDelegate_Ctor_MethodRef));
insts.Add(processor.Create(OpCodes.Call, RegisterObserversRpc_MethodRef));
}
//Target
else if (rpcType == RpcType.Target)
{
insts.Add(processor.Create(OpCodes.Newobj, ClientRpcDelegate_Ctor_MethodRef));
insts.Add(processor.Create(OpCodes.Call, RegisterTargetRpc_MethodRef));
}
/* Has to be done last. This allows the NetworkBehaviour to
* initialize it's fields first. */
processor.InsertLast(insts);
}
/// <summary>
/// Creates exit method condition if local client is not owner.
/// </summary>
/// <param name="retIfOwner">True if to ret when owner, false to ret when not owner.</param>
/// <returns>Returns Ret instruction.</returns>
internal Instruction CreateLocalClientIsOwnerCheck(MethodDefinition methodDef, LoggingType loggingType, bool notifyMessageCanBeDisabled, bool retIfOwner, bool insertFirst)
{
List<Instruction> instructions = new List<Instruction>();
/* This is placed after the if check.
* Should the if check pass then code
* jumps to this instruction. */
ILProcessor processor = methodDef.Body.GetILProcessor();
Instruction endIf = processor.Create(OpCodes.Nop);
instructions.Add(processor.Create(OpCodes.Ldarg_0)); //argument: this
//If !base.IsOwner endIf.
instructions.Add(processor.Create(OpCodes.Call, IsOwner_MethodRef));
if (retIfOwner)
instructions.Add(processor.Create(OpCodes.Brfalse, endIf));
else
instructions.Add(processor.Create(OpCodes.Brtrue, endIf));
//If logging is not disabled.
if (loggingType != LoggingType.Off)
{
string disableLoggingText = (notifyMessageCanBeDisabled) ? DISABLE_LOGGING_TEXT : string.Empty;
string msg = (retIfOwner) ?
$"Cannot complete action because you are the owner of this object. {disableLoggingText}." :
$"Cannot complete action because you are not the owner of this object. {disableLoggingText}.";
instructions.AddRange(base.GetClass<GeneralHelper>().LogMessage(methodDef, msg, loggingType));
}
//Return block.
Instruction retInst = processor.Create(OpCodes.Ret);
instructions.Add(retInst);
//After if statement, jumped to when successful check.
instructions.Add(endIf);
if (insertFirst)
{
processor.InsertFirst(instructions);
}
else
{
foreach (Instruction inst in instructions)
processor.Append(inst);
}
return retInst;
}
/// <summary>
/// Creates exit method condition if remote client is not owner.
/// </summary>
/// <param name="processor"></param>
internal Instruction CreateRemoteClientIsOwnerCheck(ILProcessor processor, ParameterDefinition connectionParameterDef)
{
/* This is placed after the if check.
* Should the if check pass then code
* jumps to this instruction. */
Instruction endIf = processor.Create(OpCodes.Nop);
processor.Emit(OpCodes.Ldarg_0); //argument: this
//If !base.IsOwner endIf.
processor.Emit(OpCodes.Ldarg, connectionParameterDef);
processor.Emit(OpCodes.Call, OwnerMatches_MethodRef);
processor.Emit(OpCodes.Brtrue, endIf);
//Return block.
Instruction retInst = processor.Create(OpCodes.Ret);
processor.Append(retInst);
//After if statement, jumped to when successful check.
processor.Append(endIf);
return retInst;
}
/// <summary>
/// Creates exit method condition if not client.
/// </summary>
/// <param name="useStatic">When true InstanceFinder.IsClient is used, when false base.IsClientInitialized is used.</param>
internal void CreateIsClientCheck(MethodDefinition methodDef, LoggingType loggingType, bool useStatic, bool insertFirst, bool checkIsNetworked)
{
/* This is placed after the if check.
* Should the if check pass then code
* jumps to this instruction. */
ILProcessor processor = methodDef.Body.GetILProcessor();
Instruction endIf = processor.Create(OpCodes.Nop);
List<Instruction> instructions = new List<Instruction>();
if (checkIsNetworked)
instructions.AddRange(CreateIsNetworkedCheck(methodDef, endIf));
//Checking against the NetworkObject.
if (!useStatic)
{
instructions.Add(processor.Create(OpCodes.Ldarg_0)); //argument: this
//If (!base.IsClient)
instructions.Add(processor.Create(OpCodes.Call, IsClientInitialized_MethodRef));
}
//Checking instanceFinder.
else
{
instructions.Add(processor.Create(OpCodes.Call, base.GetClass<ObjectHelper>().InstanceFinder_IsClient_MethodRef));
}
instructions.Add(processor.Create(OpCodes.Brtrue, endIf));
//If warning then also append warning text.
if (loggingType != LoggingType.Off)
{
string msg = $"Cannot complete action because client is not active. This may also occur if the object is not yet initialized, has deinitialized, or if it does not contain a NetworkObject component.";
instructions.AddRange(base.GetClass<GeneralHelper>().LogMessage(methodDef, msg, loggingType));
}
//Add return.
instructions.AddRange(CreateRetDefault(methodDef));
//After if statement, jumped to when successful check.
instructions.Add(endIf);
if (insertFirst)
{
processor.InsertFirst(instructions);
}
else
{
foreach (Instruction inst in instructions)
processor.Append(inst);
}
}
/// <summary>
/// Creates exit method condition if not server.
/// </summary>
/// <param name="useStatic">When true InstanceFinder.IsServer is used, when false base.IsServerInitialized is used.</param>
internal void CreateIsServerCheck(MethodDefinition methodDef, LoggingType loggingType, bool useStatic, bool insertFirst, bool checkIsNetworked)
{
/* This is placed after the if check.
* Should the if check pass then code
* jumps to this instruction. */
ILProcessor processor = methodDef.Body.GetILProcessor();
Instruction endIf = processor.Create(OpCodes.Nop);
List<Instruction> instructions = new List<Instruction>();
if (checkIsNetworked)
instructions.AddRange(CreateIsNetworkedCheck(methodDef, endIf));
if (!useStatic)
{
instructions.Add(processor.Create(OpCodes.Ldarg_0)); //argument: this
//If (!base.IsServer)
instructions.Add(processor.Create(OpCodes.Call, IsServerInitialized_MethodRef));
}
//Checking instanceFinder.
else
{
instructions.Add(processor.Create(OpCodes.Call, base.GetClass<ObjectHelper>().InstanceFinder_IsServer_MethodRef));
}
instructions.Add(processor.Create(OpCodes.Brtrue, endIf));
//If warning then also append warning text.
if (loggingType != LoggingType.Off)
{
string msg = $"Cannot complete action because server is not active. This may also occur if the object is not yet initialized, has deinitialized, or if it does not contain a NetworkObject component.";
instructions.AddRange(base.GetClass<GeneralHelper>().LogMessage(methodDef, msg, loggingType));
}
//Add return.
instructions.AddRange(CreateRetDefault(methodDef));
//After if statement, jumped to when successful check.
instructions.Add(endIf);
if (insertFirst)
{
processor.InsertFirst(instructions);
}
else
{
foreach (Instruction inst in instructions)
processor.Append(inst);
}
}
/// <summary>
/// Creates a call to base.IsNetworked and returns instructions.
/// </summary>
private List<Instruction> CreateIsNetworkedCheck(MethodDefinition methodDef, Instruction endIfInst)
{
List<Instruction> insts = new List<Instruction>();
ILProcessor processor = methodDef.Body.GetILProcessor();
insts.Add(processor.Create(OpCodes.Ldarg_0));
insts.Add(processor.Create(OpCodes.Call, base.GetClass<NetworkBehaviourHelper>().IsNetworked_MethodRef));
insts.Add(processor.Create(OpCodes.Brfalse, endIfInst));
return insts;
}
/// <summary>
/// Creates a return using the ReturnType for methodDef.
/// </summary>
/// <param name="processor"></param>
/// <param name="methodDef"></param>
/// <returns></returns>
public List<Instruction> CreateRetDefault(MethodDefinition methodDef, ModuleDefinition importReturnModule = null)
{
ILProcessor processor = methodDef.Body.GetILProcessor();
List<Instruction> instructions = new List<Instruction>();
//If requires a value return.
if (methodDef.ReturnType != methodDef.Module.TypeSystem.Void)
{
//Import type first.
methodDef.Module.ImportReference(methodDef.ReturnType);
if (importReturnModule != null)
importReturnModule.ImportReference(methodDef.ReturnType);
VariableDefinition vd = base.GetClass<GeneralHelper>().CreateVariable(methodDef, methodDef.ReturnType);
instructions.Add(processor.Create(OpCodes.Ldloca_S, vd));
instructions.Add(processor.Create(OpCodes.Initobj, vd.VariableType));
instructions.Add(processor.Create(OpCodes.Ldloc, vd));
}
instructions.Add(processor.Create(OpCodes.Ret));
return instructions;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0c42e06349d6890459a155a2afe17d41
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6d061dda8ed87ed48a08020942ad63f6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 033da35314653aa4689b4582e7929295
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,117 @@
using FishNet.CodeGenerating.Extension;
using FishNet.CodeGenerating.Helping.Extension;
using FishNet.Connection;
using MonoFN.Cecil;
using MonoFN.Cecil.Cil;
using System.Collections.Generic;
using UnityEngine;
using SR = System.Reflection;
namespace FishNet.CodeGenerating.Helping
{
internal class PhysicsHelper : CodegenBase
{
#region Reflection references.
public MethodReference GetScene_MethodRef;
public MethodReference GetPhysicsScene2D_MethodRef;
public MethodReference GetPhysicsScene3D_MethodRef;
public MethodReference Physics3D_Simulate_MethodRef;
public MethodReference Physics2D_Simulate_MethodRef;
public MethodReference Physics3D_SyncTransforms_MethodRef;
public MethodReference Physics2D_SyncTransforms_MethodRef;
#endregion
public override bool ImportReferences()
{
SR.MethodInfo locMi;
//GetScene.
locMi = typeof(GameObject).GetMethod("get_scene");
GetScene_MethodRef = base.ImportReference(locMi);
//Physics.SyncTransform.
foreach (SR.MethodInfo mi in typeof(Physics).GetMethods())
{
if (mi.Name == nameof(Physics.SyncTransforms))
{
Physics3D_SyncTransforms_MethodRef = base.ImportReference(mi);
break;
}
}
foreach (SR.MethodInfo mi in typeof(Physics2D).GetMethods())
{
if (mi.Name == nameof(Physics2D.SyncTransforms))
{
Physics2D_SyncTransforms_MethodRef = base.ImportReference(mi);
break;
}
}
//PhysicsScene.Simulate.
foreach (SR.MethodInfo mi in typeof(PhysicsScene).GetMethods())
{
if (mi.Name == nameof(PhysicsScene.Simulate))
{
Physics3D_Simulate_MethodRef = base.ImportReference(mi);
break;
}
}
foreach (SR.MethodInfo mi in typeof(PhysicsScene2D).GetMethods())
{
if (mi.Name == nameof(PhysicsScene2D.Simulate))
{
Physics2D_Simulate_MethodRef = base.ImportReference(mi);
break;
}
}
//GetPhysicsScene.
foreach (SR.MethodInfo mi in typeof(PhysicsSceneExtensions).GetMethods())
{
if (mi.Name == nameof(PhysicsSceneExtensions.GetPhysicsScene))
{
GetPhysicsScene3D_MethodRef = base.ImportReference(mi);
break;
}
}
foreach (SR.MethodInfo mi in typeof(PhysicsSceneExtensions2D).GetMethods())
{
if (mi.Name == nameof(PhysicsSceneExtensions2D.GetPhysicsScene2D))
{
GetPhysicsScene2D_MethodRef = base.ImportReference(mi);
break;
}
}
return true;
}
/// <summary>
/// Returns instructions to get a physics scene from a gameObject.
/// </summary>
public List<Instruction> GetPhysicsScene(MethodDefinition md, VariableDefinition gameObjectVd, bool threeDimensional)
{
ILProcessor processor = md.Body.GetILProcessor();
return GetPhysicsScene(processor, gameObjectVd, threeDimensional);
}
/// <summary>
/// Returns instructions to get a physics scene from a gameObject.
/// </summary>
public List<Instruction> GetPhysicsScene(ILProcessor processor, VariableDefinition gameObjectVd, bool threeDimensional)
{
List<Instruction> insts = new List<Instruction>();
//gameObject.scene.GetPhysics...
insts.Add(processor.Create(OpCodes.Ldloc, gameObjectVd));
insts.Add(processor.Create(GetScene_MethodRef.GetCallOpCode(base.Session), GetScene_MethodRef));
if (threeDimensional)
insts.Add(processor.Create(OpCodes.Call, GetPhysicsScene3D_MethodRef));
else
insts.Add(processor.Create(OpCodes.Call, GetPhysicsScene2D_MethodRef));
return insts;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 28ae27f7bc8e89547a606262508fdd36
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e9a06c812bf785a44a38a5852ff866d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,66 @@
using FishNet.CodeGenerating.Extension;
using FishNet.CodeGenerating.Helping.Extension;
using FishNet.Connection;
using FishNet.Serializing;
using MonoFN.Cecil;
using System;
using System.Reflection;
namespace FishNet.CodeGenerating.Helping
{
internal class ReaderImports : CodegenBase
{
#region Reflection references.
public TypeReference PooledReader_TypeRef;
public TypeReference Reader_TypeRef;
public TypeReference NetworkConnection_TypeRef;
public MethodReference PooledReader_ReadNetworkBehaviour_MethodRef;
public MethodReference Reader_ReadPackedWhole_MethodRef;
public MethodReference Reader_ReadDictionary_MethodRef;
public MethodReference Reader_ReadList_MethodRef;
public MethodReference Reader_ReadArray_MethodRef;
public TypeReference GenericReader_TypeRef;
public MethodReference GenericReader_ReadUnpacked_MethodRef;
public MethodReference GenericReader_ReadAutoPacked_MethodRef;
#endregion
/// <summary>
/// Imports references needed by this helper.
/// </summary>
/// <param name="moduleDef"></param>
/// <returns></returns>
public override bool ImportReferences()
{
ReaderProcessor rp = base.GetClass<ReaderProcessor>();
PooledReader_TypeRef = base.ImportReference(typeof(PooledReader));
Reader_TypeRef = base.ImportReference(typeof(Reader));
NetworkConnection_TypeRef = base.ImportReference(typeof(NetworkConnection));
GenericReader_TypeRef = base.ImportReference(typeof(GenericReader<>));
TypeDefinition genericWriterTd = GenericReader_TypeRef.CachedResolve(base.Session);
GenericReader_ReadUnpacked_MethodRef = base.ImportReference(genericWriterTd.GetMethod(nameof(GenericReader<int>.SetReadUnpacked)));
GenericReader_ReadAutoPacked_MethodRef = base.ImportReference(genericWriterTd.GetMethod(nameof(GenericReader<int>.SetReadAutoPacked)));
Type pooledReaderType = typeof(PooledReader);
foreach (MethodInfo methodInfo in pooledReaderType.GetMethods())
{
int parameterCount = methodInfo.GetParameters().Length;
/* Special methods. */
if (methodInfo.Name == nameof(PooledReader.ReadPackedWhole))
Reader_ReadPackedWhole_MethodRef = base.ImportReference(methodInfo);
//Relay readers.
else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadDictionaryAllocated))
Reader_ReadDictionary_MethodRef = base.ImportReference(methodInfo);
else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadListAllocated))
Reader_ReadList_MethodRef = base.ImportReference(methodInfo);
else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadArrayAllocated))
Reader_ReadArray_MethodRef = base.ImportReference(methodInfo);
}
return true;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 350861dcbcbabc447acd37e4310b0697
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 11dbcc0798e079e4a85fe98dfc9fe23a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6ced44bfdb3068f4cb7513c9be85729a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c480d34b00248444d91a61e015b14e2a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2b30600f0fdb27c4fb86c310b08f43b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b1882eac63e3d94aad8f41915bc1ab8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,12 @@
namespace FishNet.CodeGenerating.Helping
{
internal enum QolAttributeType
{
None,
Server,
Client
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 357a22940018b8e49976e13272c5b2ef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,19 @@
namespace FishNet.CodeGenerating.Helping
{
internal enum SerializerType
{
Invalid,
Enum,
Array,
List,
NetworkBehaviour,
ClassOrStruct,
Nullable,
Dictionary,
Null,
ByReference,
MultiDimensionalArray
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e7f1bbf5c398c3e4e92e53ec3e49d5e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,17 @@
using MonoFN.Cecil.Cil;
using System.Collections.Generic;
namespace FishNet.CodeGenerating.Helping
{
/// <summary>
/// Data used to modify an RpcIndex should the class have to be rebuilt.
/// </summary>
internal class SyncIndexData
{
public uint SyncCount = 0;
public List<Instruction> DelegateInstructions = new List<Instruction>();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 55f2e751e4788464b8394f6b8bdb548a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,14 @@
namespace FishNet.CodeGenerating.Helping
{
public enum SyncType
{
Unset,
Variable,
List,
Dictionary,
HashSet,
Custom,
Unhandled,
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 44c753d6ac0c7864c9962d91703b2afe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,97 @@
using FishNet.CodeGenerating.Extension;
using FishNet.CodeGenerating.Helping.Extension;
using FishNet.Object;
using FishNet.Serializing;
using MonoFN.Cecil;
using System;
using System.Collections.Generic;
using System.Reflection;
namespace FishNet.CodeGenerating.Helping
{
internal class WriterImports : CodegenBase
{
#region Reflection references.
public MethodReference WriterPool_GetWriter_MethodRef;
public MethodReference WriterPool_GetWriterLength_MethodRef;
public MethodReference Writer_WritePackedWhole_MethodRef;
public TypeReference PooledWriter_TypeRef;
public TypeReference Writer_TypeRef;
public MethodReference PooledWriter_Dispose_MethodRef;
public MethodReference Writer_WriteDictionary_MethodRef;
public MethodReference Writer_WriteList_MethodRef;
public MethodReference Writer_WriteArray_MethodRef;
public TypeReference AutoPackTypeRef;
public TypeReference GenericWriter_TypeRef;
public MethodReference GenericWriter_WriteUnpacked_MethodRef;
public MethodReference GenericWriter_WriteAutoPacked_MethodRef;
public MethodReference Writer_WriteUnpacked_MethodRef;
#endregion
/// <summary>
/// Imports references needed by this helper.
/// </summary>
/// <param name="moduleDef"></param>
/// <returns></returns>
public override bool ImportReferences()
{
PooledWriter_TypeRef = base.ImportReference(typeof(PooledWriter));
Writer_TypeRef = base.ImportReference(typeof(Writer));
AutoPackTypeRef = base.ImportReference(typeof(AutoPackType));
GenericWriter_TypeRef = base.ImportReference(typeof(GenericWriter<>));
Writer_WriteUnpacked_MethodRef = Writer_TypeRef.CachedResolve(base.Session).GetMethodReference(base.Session, nameof(Writer.WriteUnpacked));
TypeDefinition genericWriterTd = GenericWriter_TypeRef.CachedResolve(base.Session);
GenericWriter_WriteUnpacked_MethodRef = base.ImportReference(genericWriterTd.GetMethod(nameof(GenericWriter<int>.SetWriteUnpacked)));
GenericWriter_WriteAutoPacked_MethodRef = base.ImportReference(genericWriterTd.GetMethod(nameof(GenericWriter<int>.SetWriteAutoPacked)));
//WriterPool.GetWriter
Type writerPoolType = typeof(WriterPool);
base.ImportReference(writerPoolType);
foreach (var methodInfo in writerPoolType.GetMethods())
{
if (methodInfo.Name == nameof(WriterPool.Retrieve))
{
//GetWriter().
if (methodInfo.GetParameters().Length == 0)
{
WriterPool_GetWriter_MethodRef = base.ImportReference(methodInfo);
}
//GetWriter(?).
else if (methodInfo.GetParameters().Length == 1)
{
ParameterInfo pi = methodInfo.GetParameters()[0];
//GetWriter(int).
if (pi.ParameterType == typeof(int))
WriterPool_GetWriterLength_MethodRef = base.ImportReference(methodInfo);
}
}
}
WriterProcessor gwh = base.GetClass<WriterProcessor>();
Type pooledWriterType = typeof(PooledWriter);
foreach (MethodInfo methodInfo in pooledWriterType.GetMethods())
{
int parameterCount = methodInfo.GetParameters().Length;
if (methodInfo.Name == nameof(PooledWriter.Store))
PooledWriter_Dispose_MethodRef = base.ImportReference(methodInfo);
else if (methodInfo.Name == nameof(PooledWriter.WritePackedWhole))
Writer_WritePackedWhole_MethodRef = base.ImportReference(methodInfo);
//Relay writers.
else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteDictionary))
Writer_WriteDictionary_MethodRef = base.ImportReference(methodInfo);
else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteList))
Writer_WriteList_MethodRef = base.ImportReference(methodInfo);
else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteArray))
Writer_WriteArray_MethodRef = base.ImportReference(methodInfo);
}
return true;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 425638e29bab6f1488e8865c9e3f8b57
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b1808ca0399d069499d3ecb4e031c3e2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,401 @@
using FishNet.Broadcast;
using FishNet.CodeGenerating.Extension;
using FishNet.CodeGenerating.Helping;
using FishNet.CodeGenerating.Helping.Extension;
using FishNet.CodeGenerating.Processing;
using FishNet.CodeGenerating.Processing.Rpc;
using FishNet.Configuring;
using FishNet.Editing.Upgrading;
using FishNet.Serializing.Helping;
using MonoFN.Cecil;
using MonoFN.Cecil.Cil;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Unity.CompilationPipeline.Common.ILPostProcessing;
namespace FishNet.CodeGenerating.ILCore
{
public class FishNetILPP : ILPostProcessor
{
#region Const.
internal const string RUNTIME_ASSEMBLY_NAME = "FishNet.Runtime";
#endregion
public override bool WillProcess(ICompiledAssembly compiledAssembly)
{
if (compiledAssembly.Name.StartsWith("Unity."))
return false;
if (compiledAssembly.Name.StartsWith("UnityEngine."))
return false;
if (compiledAssembly.Name.StartsWith("UnityEditor."))
return false;
if (compiledAssembly.Name.Contains("Editor"))
return false;
/* This line contradicts the one below where referencesFishNet
* becomes true if the assembly is FishNetAssembly. This is here
* intentionally to stop codegen from running on the runtime
* fishnet assembly, but the option below is for debugging. I would
* comment out this check if I wanted to compile fishnet runtime. */
//if (CODEGEN_THIS_NAMESPACE.Length == 0)
//{
// if (compiledAssembly.Name == RUNTIME_ASSEMBLY_NAME)
// return false;
//}
bool referencesFishNet = FishNetILPP.IsFishNetAssembly(compiledAssembly) || compiledAssembly.References.Any(filePath => Path.GetFileNameWithoutExtension(filePath) == RUNTIME_ASSEMBLY_NAME);
return referencesFishNet;
}
public override ILPostProcessor GetInstance() => this;
public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
{
AssemblyDefinition assemblyDef = ILCoreHelper.GetAssemblyDefinition(compiledAssembly);
if (assemblyDef == null)
return null;
//Check WillProcess again; somehow certain editor scripts skip the WillProcess check.
if (!WillProcess(compiledAssembly))
return null;
CodegenSession session = new CodegenSession();
if (!session.Initialize(assemblyDef.MainModule))
return null;
bool modified = false;
bool fnAssembly = IsFishNetAssembly(compiledAssembly);
if (fnAssembly)
modified |= ModifyMakePublicMethods(session);
/* If one or more scripts use RPCs but don't inherit NetworkBehaviours
* then don't bother processing the rest. */
if (session.GetClass<NetworkBehaviourProcessor>().NonNetworkBehaviourHasInvalidAttributes(session.Module.Types))
return new ILPostProcessResult(null, session.Diagnostics);
modified |= session.GetClass<WriterProcessor>().Process();
modified |= session.GetClass<ReaderProcessor>().Process();
modified |= CreateDeclaredSerializerDelegates(session);
modified |= CreateDeclaredSerializers(session);
modified |= CreateDeclaredComparerDelegates(session);
modified |= CreateIBroadcast(session);
#if !DISABLE_QOL_ATTRIBUTES
modified |= CreateQOLAttributes(session);
#endif
modified |= CreateNetworkBehaviours(session);
modified |= CreateSerializerInitializeDelegates(session);
if (fnAssembly)
{
AssemblyNameReference anr = session.Module.AssemblyReferences.FirstOrDefault<AssemblyNameReference>(x => x.FullName == session.Module.Assembly.FullName);
if (anr != null)
session.Module.AssemblyReferences.Remove(anr);
}
/* If there are warnings about SyncVars being in different assemblies.
* This is awful ... codegen would need to be reworked to save
* syncvars across all assemblies so that scripts referencing them from
* another assembly can have it's instructions changed. This however is an immense
* amount of work so it will have to be put on hold, for... a long.. long while. */
if (session.DifferentAssemblySyncVars.Count > 0)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"Assembly {session.Module.Name} has inherited access to SyncVars in different assemblies. When accessing SyncVars across assemblies be sure to use Get/Set methods withinin the inherited assembly script to change SyncVars. Accessible fields are:");
foreach (FieldDefinition item in session.DifferentAssemblySyncVars)
sb.AppendLine($"Field {item.Name} within {item.DeclaringType.FullName} in assembly {item.Module.Name}.");
session.LogWarning("v------- IMPORTANT -------v");
session.LogWarning(sb.ToString());
session.DifferentAssemblySyncVars.Clear();
}
//session.LogWarning($"Assembly {compiledAssembly.Name} took {stopwatch.ElapsedMilliseconds}.");
if (!modified)
{
return null;
}
else
{
TryLogV3ToV4Helpers(session);
MemoryStream pe = new MemoryStream();
MemoryStream pdb = new MemoryStream();
WriterParameters writerParameters = new WriterParameters
{
SymbolWriterProvider = new PortablePdbWriterProvider(),
SymbolStream = pdb,
WriteSymbols = true
};
assemblyDef.Write(pe, writerParameters);
return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), session.Diagnostics);
}
}
/// <summary>
/// Logs warning if v3 to v4 helpers are enabled.
/// </summary>
private void TryLogV3ToV4Helpers(CodegenSession session)
{
#if !FISHNET_DISABLE_V3TOV4_HELPERS
/* There is no way to check if this has run already once per codegen
* so the only option is to print per session, which means
* a print will occur every time an assembly compiles. This means
* several prints will potentially occur per script change.
*
* However, these warnings typically only print when all errors are gone.
* When this is true the user may go ahead and disable this warning
* as instructed. */
session.LogWarning(UpgradeFromV3ToV4Menu.EnabledWarning);
#endif
}
/// <summary>
/// Makees methods public scope which use CodegenMakePublic attribute.
/// </summary>
/// <returns></returns>
private bool ModifyMakePublicMethods(CodegenSession session)
{
string makePublicTypeFullName = typeof(MakePublicAttribute).FullName;
foreach (TypeDefinition td in session.Module.Types)
{
foreach (MethodDefinition md in td.Methods)
{
foreach (CustomAttribute ca in md.CustomAttributes)
{
if (ca.AttributeType.FullName == makePublicTypeFullName)
{
md.Attributes &= ~MethodAttributes.Assembly;
md.Attributes |= MethodAttributes.Public;
}
}
}
}
//There is always at least one modified.
return true;
}
/// <summary>
/// Creates delegates for user declared serializers.
/// </summary>
internal bool CreateDeclaredSerializerDelegates(CodegenSession session)
{
bool modified = false;
List<TypeDefinition> allTypeDefs = session.Module.Types.ToList();
foreach (TypeDefinition td in allTypeDefs)
{
if (session.GetClass<GeneralHelper>().HasExcludeSerializationAttribute(td))
continue;
if (td.Attributes.HasFlag(WriterProcessor.CUSTOM_SERIALIZER_TYPEDEF_ATTRIBUTES))
modified |= session.GetClass<CustomSerializerProcessor>().CreateSerializerDelegates(td, true);
}
return modified;
}
/// <summary>
/// Creates serializers for custom types within user declared serializers.
/// </summary>
private bool CreateDeclaredSerializers(CodegenSession session)
{
bool modified = false;
List<TypeDefinition> allTypeDefs = session.Module.Types.ToList();
foreach (TypeDefinition td in allTypeDefs)
{
if (session.GetClass<GeneralHelper>().HasExcludeSerializationAttribute(td))
continue;
if (td.Attributes.HasFlag(WriterProcessor.CUSTOM_SERIALIZER_TYPEDEF_ATTRIBUTES))
modified |= session.GetClass<CustomSerializerProcessor>().CreateSerializers(td);
}
return modified;
}
/// <summary>
/// Creates delegates for user declared comparers.
/// </summary>
internal bool CreateDeclaredComparerDelegates(CodegenSession session)
{
bool modified = false;
List<TypeDefinition> allTypeDefs = session.Module.Types.ToList();
foreach (TypeDefinition td in allTypeDefs)
{
if (session.GetClass<GeneralHelper>().HasExcludeSerializationAttribute(td))
continue;
modified |= session.GetClass<CustomSerializerProcessor>().CreateComparerDelegates(td);
}
return modified;
}
/// <summary>
/// Creaters serializers and calls for IBroadcast.
/// </summary>
/// <param name="moduleDef"></param>
/// <param name="diagnostics"></param>
private bool CreateIBroadcast(CodegenSession session)
{
bool modified = false;
string networkBehaviourFullName = session.GetClass<NetworkBehaviourHelper>().FullName;
HashSet<TypeDefinition> typeDefs = new HashSet<TypeDefinition>();
foreach (TypeDefinition td in session.Module.Types)
{
TypeDefinition climbTd = td;
do
{
//Reached NetworkBehaviour class.
if (climbTd.FullName == networkBehaviourFullName)
break;
///* Check initial class as well all types within
// * the class. Then check all of it's base classes. */
if (climbTd.ImplementsInterface<IBroadcast>())
typeDefs.Add(climbTd);
//7ms
//Add nested. Only going to go a single layer deep.
foreach (TypeDefinition nestedTypeDef in td.NestedTypes)
{
if (nestedTypeDef.ImplementsInterface<IBroadcast>())
typeDefs.Add(nestedTypeDef);
}
//0ms
climbTd = climbTd.GetNextBaseTypeDefinition(session);
//this + name check 40ms
} while (climbTd != null);
}
//Create reader/writers for found typeDefs.
foreach (TypeDefinition td in typeDefs)
{
TypeReference typeRef = session.ImportReference(td);
bool canSerialize = session.GetClass<GeneralHelper>().HasSerializerAndDeserializer(typeRef, true);
if (!canSerialize)
session.LogError($"Broadcast {td.Name} does not support serialization. Use a supported type or create a custom serializer.");
else
modified = true;
}
return modified;
}
/// <summary>
/// Handles QOLAttributes such as [Server].
/// </summary>
/// <returns></returns>
private bool CreateQOLAttributes(CodegenSession session)
{
bool modified = false;
bool codeStripping = false;
List<TypeDefinition> allTypeDefs = session.Module.Types.ToList();
/* First pass, potentially only pass.
* If code stripping them this will be run again. The first iteration
* is to ensure things are removed in the proper order. */
foreach (TypeDefinition td in allTypeDefs)
{
if (session.GetClass<GeneralHelper>().HasExcludeSerializationAttribute(td))
continue;
modified |= session.GetClass<QolAttributeProcessor>().Process(td, codeStripping);
}
return modified;
}
/// <summary>
/// Creates NetworkBehaviour changes.
/// </summary>
private bool CreateNetworkBehaviours(CodegenSession session)
{
//Get all network behaviours to process.
List<TypeDefinition> networkBehaviourTypeDefs = session.Module.Types
.Where(td => td.IsSubclassOf(session, session.GetClass<NetworkBehaviourHelper>().FullName))
.ToList();
/* Remove types which are inherited. This gets the child most networkbehaviours.
* Since processing iterates upward from each child there is no reason
* to include any inherited NBs. */
RemoveInheritedTypeDefinitions(networkBehaviourTypeDefs);
foreach (TypeDefinition typeDef in networkBehaviourTypeDefs)
{
session.ImportReference(typeDef);
session.GetClass<NetworkBehaviourProcessor>().ProcessLocal(typeDef);
}
//Call base methods on RPCs.
foreach (TypeDefinition td in session.Module.Types)
session.GetClass<RpcProcessor>().RedirectBaseCalls();
/* Removes typedefinitions which are inherited by
* another within tds. For example, if the collection
* td contains A, B, C and our structure is
* A : B : C then B and C will be removed from the collection
* Since they are both inherited by A. */
void RemoveInheritedTypeDefinitions(List<TypeDefinition> tds)
{
HashSet<TypeDefinition> inheritedTds = new HashSet<TypeDefinition>();
/* Remove any networkbehaviour typedefs which are inherited by
* another networkbehaviour typedef. */
for (int i = 0; i < tds.Count; i++)
{
/* Iterates all base types and
* adds them to inheritedTds so long
* as the base type is not a NetworkBehaviour. */
TypeDefinition copyTd = tds[i].GetNextBaseTypeDefinition(session);
while (copyTd != null)
{
//Class is NB.
if (copyTd.FullName == session.GetClass<NetworkBehaviourHelper>().FullName)
break;
inheritedTds.Add(copyTd);
copyTd = copyTd.GetNextBaseTypeDefinition(session);
}
}
//Remove all inherited types.
foreach (TypeDefinition item in inheritedTds)
tds.Remove(item);
}
//Moment a NetworkBehaviour exist the assembly is considered modified.
bool modified = (networkBehaviourTypeDefs.Count > 0);
return modified;
}
/// <summary>
/// Creates generic delegates for all read and write methods.
/// </summary>
/// <param name="moduleDef"></param>
/// <param name="diagnostics"></param>
private bool CreateSerializerInitializeDelegates(CodegenSession session)
{
session.GetClass<WriterProcessor>().CreateInitializeDelegates();
session.GetClass<ReaderProcessor>().CreateInitializeDelegates();
return true;
}
internal static bool IsFishNetAssembly(ICompiledAssembly assembly) => (assembly.Name == FishNetILPP.RUNTIME_ASSEMBLY_NAME);
internal static bool IsFishNetAssembly(CodegenSession session) => (session.Module.Assembly.Name.Name == FishNetILPP.RUNTIME_ASSEMBLY_NAME);
internal static bool IsFishNetAssembly(ModuleDefinition moduleDef) => (moduleDef.Assembly.Name.Name == FishNetILPP.RUNTIME_ASSEMBLY_NAME);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f03d76b376c1d5b4591039af6fd4c9e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,38 @@
using MonoFN.Cecil;
using MonoFN.Cecil.Cil;
using System.IO;
using Unity.CompilationPipeline.Common.ILPostProcessing;
namespace FishNet.CodeGenerating.ILCore
{
internal static class ILCoreHelper
{
/// <summary>
/// Returns AssembleDefinition for compiledAssembly.
/// </summary>
/// <param name="compiledAssembly"></param>
/// <returns></returns>
internal static AssemblyDefinition GetAssemblyDefinition(ICompiledAssembly compiledAssembly)
{
PostProcessorAssemblyResolver assemblyResolver = new PostProcessorAssemblyResolver(compiledAssembly);
ReaderParameters readerParameters = new ReaderParameters
{
SymbolStream = new MemoryStream(compiledAssembly.InMemoryAssembly.PdbData),
SymbolReaderProvider = new PortablePdbReaderProvider(),
AssemblyResolver = assemblyResolver,
ReflectionImporterProvider = new PostProcessorReflectionImporterProvider(),
ReadingMode = ReadingMode.Immediate
};
AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(new MemoryStream(compiledAssembly.InMemoryAssembly.PeData), readerParameters);
//Allows us to resolve inside FishNet assembly, such as for components.
assemblyResolver.AddAssemblyDefinitionBeingOperatedOn(assemblyDefinition);
return assemblyDefinition;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dfcfb917dd9268744962ae61aa0115b7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,139 @@
using MonoFN.Cecil;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using Unity.CompilationPipeline.Common.ILPostProcessing;
namespace FishNet.CodeGenerating
{
internal class PostProcessorAssemblyResolver : IAssemblyResolver
{
private readonly string[] m_AssemblyReferences;
private readonly Dictionary<string, AssemblyDefinition> m_AssemblyCache = new Dictionary<string, AssemblyDefinition>();
private readonly ICompiledAssembly m_CompiledAssembly;
private AssemblyDefinition m_SelfAssembly;
public PostProcessorAssemblyResolver(ICompiledAssembly compiledAssembly)
{
m_CompiledAssembly = compiledAssembly;
m_AssemblyReferences = compiledAssembly.References;
}
public void Dispose() { }
public AssemblyDefinition Resolve(AssemblyNameReference name) => Resolve(name, new ReaderParameters(ReadingMode.Deferred));
public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{
lock (m_AssemblyCache)
{
if (name.Name == m_CompiledAssembly.Name)
{
return m_SelfAssembly;
}
var fileName = FindFile(name);
if (fileName == null)
{
return null;
}
var lastWriteTime = File.GetLastWriteTime(fileName);
var cacheKey = $"{fileName}{lastWriteTime}";
if (m_AssemblyCache.TryGetValue(cacheKey, out var result))
{
return result;
}
parameters.AssemblyResolver = this;
var ms = MemoryStreamFor(fileName);
var pdb = $"{fileName}.pdb";
if (File.Exists(pdb))
{
parameters.SymbolStream = MemoryStreamFor(pdb);
}
var assemblyDefinition = AssemblyDefinition.ReadAssembly(ms, parameters);
m_AssemblyCache.Add(cacheKey, assemblyDefinition);
return assemblyDefinition;
}
}
private string FindFile(AssemblyNameReference name)
{
var fileName = m_AssemblyReferences.FirstOrDefault(r => Path.GetFileName(r) == $"{name.Name}.dll");
if (fileName != null)
{
return fileName;
}
// perhaps the type comes from an exe instead
fileName = m_AssemblyReferences.FirstOrDefault(r => Path.GetFileName(r) == $"{name.Name}.exe");
if (fileName != null)
{
return fileName;
}
//Unfortunately the current ICompiledAssembly API only provides direct references.
//It is very much possible that a postprocessor ends up investigating a type in a directly
//referenced assembly, that contains a field that is not in a directly referenced assembly.
//if we don't do anything special for that situation, it will fail to resolve. We should fix this
//in the ILPostProcessing API. As a workaround, we rely on the fact here that the indirect references
//are always located next to direct references, so we search in all directories of direct references we
//got passed, and if we find the file in there, we resolve to it.
return m_AssemblyReferences
.Select(Path.GetDirectoryName)
.Distinct()
.Select(parentDir => Path.Combine(parentDir, $"{name.Name}.dll"))
.FirstOrDefault(File.Exists);
}
private static MemoryStream MemoryStreamFor(string fileName)
{
return Retry(10, TimeSpan.FromSeconds(1), () =>
{
byte[] byteArray;
using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
byteArray = new byte[fs.Length];
var readLength = fs.Read(byteArray, 0, (int)fs.Length);
if (readLength != fs.Length)
{
throw new InvalidOperationException("File read length is not full length of file.");
}
}
return new MemoryStream(byteArray);
});
}
private static MemoryStream Retry(int retryCount, TimeSpan waitTime, Func<MemoryStream> func)
{
try
{
return func();
}
catch (IOException)
{
if (retryCount == 0)
{
throw;
}
Console.WriteLine($"Caught IO Exception, trying {retryCount} more times");
Thread.Sleep(waitTime);
return Retry(retryCount - 1, waitTime, func);
}
}
public void AddAssemblyDefinitionBeingOperatedOn(AssemblyDefinition assemblyDefinition)
{
m_SelfAssembly = assemblyDefinition;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c247f4266b2864eb96e6a9ae6557d31
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 484e8ad8c4dde382ea67036b32935ef1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,12 @@
using MonoFN.Cecil;
namespace FishNet.CodeGenerating.ILCore
{
internal class PostProcessorReflectionImporterProvider : IReflectionImporterProvider
{
public IReflectionImporter GetReflectionImporter(ModuleDefinition moduleDef)
{
return new PostProcessorReflectionImporter(moduleDef);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f9273a5dad109ab0783891e36c983080
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 15ff4c71a3c972440810ac633b69764d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8462034e5255191499a018bd8fbbf751
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,367 @@

using FishNet.CodeGenerating.Extension;
using FishNet.CodeGenerating.Helping;
using FishNet.CodeGenerating.Helping.Extension;
using FishNet.Serializing;
using FishNet.Serializing.Helping;
using MonoFN.Cecil;
using MonoFN.Cecil.Cil;
using System.Collections.Generic;
using UnityEngine;
namespace FishNet.CodeGenerating.Processing
{
internal class CustomSerializerProcessor : CodegenBase
{
#region Types.
internal enum ExtensionType
{
None,
Write,
Read
}
#endregion
internal bool CreateSerializerDelegates(TypeDefinition typeDef, bool replace)
{
bool modified = false;
/* Find all declared methods and register delegates to them.
* After they are all registered create any custom writers
* needed to complete the declared methods. It's important to
* make generated writers after so that a generated method
* isn't made for a type when the user has already made a declared one. */
foreach (MethodDefinition methodDef in typeDef.Methods)
{
ExtensionType extensionType = GetExtensionType(methodDef);
if (extensionType == ExtensionType.None)
continue;
if (base.GetClass<GeneralHelper>().HasNotSerializableAttribute(methodDef))
continue;
MethodReference methodRef = base.ImportReference(methodDef);
if (extensionType == ExtensionType.Write)
{
base.GetClass<WriterProcessor>().AddWriterMethod(methodRef.Parameters[1].ParameterType, methodRef, false, !replace);
modified = true;
}
else if (extensionType == ExtensionType.Read)
{
base.GetClass<ReaderProcessor>().AddReaderMethod(methodRef.ReturnType, methodRef, false, !replace);
modified = true;
}
}
return modified;
}
/// <summary>
/// Creates serializers for any custom types for declared methods.
/// </summary>
/// <param name="declaredMethods"></param>
/// <param name="moduleDef"></param>
internal bool CreateSerializers(TypeDefinition typeDef)
{
bool modified = false;
List<(MethodDefinition, ExtensionType)> declaredMethods = new List<(MethodDefinition, ExtensionType)>();
/* Go through all custom serializers again and see if
* they use any types that the user didn't make a serializer for
* and that there isn't a built-in type for. Create serializers
* for these types. */
foreach (MethodDefinition methodDef in typeDef.Methods)
{
ExtensionType extensionType = GetExtensionType(methodDef);
if (extensionType == ExtensionType.None)
continue;
if (base.GetClass<GeneralHelper>().HasNotSerializableAttribute(methodDef))
continue;
declaredMethods.Add((methodDef, extensionType));
modified = true;
}
//Now that all declared are loaded see if any of them need generated serializers.
foreach ((MethodDefinition methodDef, ExtensionType extensionType) in declaredMethods)
CreateSerializers(extensionType, methodDef);
return modified;
}
/// <summary>
/// Creates a custom serializer for any types not handled within users declared.
/// </summary>
/// <param name="extensionType"></param>
/// <param name="moduleDef"></param>
/// <param name="methodDef"></param>
/// <param name="diagnostics"></param>
private void CreateSerializers(ExtensionType extensionType, MethodDefinition methodDef)
{
for (int i = 0; i < methodDef.Body.Instructions.Count; i++)
CheckToModifyInstructions(extensionType, methodDef, ref i);
}
/// <summary>
/// Creates delegates for custom comparers.
/// </summary>
internal bool CreateComparerDelegates(TypeDefinition typeDef)
{
bool modified = false;
GeneralHelper gh = base.GetClass<GeneralHelper>();
/* Find all declared methods and register delegates to them.
* After they are all registered create any custom writers
* needed to complete the declared methods. It's important to
* make generated writers after so that a generated method
* isn't made for a type when the user has already made a declared one. */
foreach (MethodDefinition methodDef in typeDef.Methods)
{
if (gh.HasNotSerializableAttribute(methodDef))
continue;
if (!methodDef.HasCustomAttribute<CustomComparerAttribute>())
continue;
//Validate return type.
if (methodDef.ReturnType.FullName != gh.GetTypeReference(typeof(bool)).FullName)
{
base.LogError($"Comparer method {methodDef.Name} in type {typeDef.FullName} must return bool.");
continue;
}
/* Make sure parameters are correct. */
//Invalid count.
if (methodDef.Parameters.Count != 2)
{
base.LogError($"Comparer method {methodDef.Name} in type {typeDef.FullName} must have exactly two parameters, each of the same type which is being compared.");
continue;
}
TypeReference p0Tr = methodDef.Parameters[0].ParameterType;
TypeReference p1Tr = methodDef.Parameters[0].ParameterType;
//Not the same types.
if (p0Tr != p1Tr)
{
base.LogError($"Both parameters must be the same type in comparer method {methodDef.Name} in type {typeDef.FullName}.");
continue;
}
base.ImportReference(methodDef);
base.ImportReference(p0Tr);
gh.RegisterComparerDelegate(methodDef, p0Tr);
gh.CreateComparerDelegate(methodDef, p0Tr);
}
return modified;
}
/// <summary>
/// Checks if instructions need to be modified and does so.
/// </summary>
/// <param name="methodDef"></param>
/// <param name="instructionIndex"></param>
private void CheckToModifyInstructions(ExtensionType extensionType, MethodDefinition methodDef, ref int instructionIndex)
{
Instruction instruction = methodDef.Body.Instructions[instructionIndex];
//Fields.
if (instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld)
CheckFieldReferenceInstruction(extensionType, methodDef, ref instructionIndex);
//Method calls.
else if (instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Callvirt)
CheckCallInstruction(extensionType, methodDef, ref instructionIndex, (MethodReference)instruction.Operand);
}
/// <summary>
/// Checks if a reader or writer must be generated for a field type.
/// </summary>
/// <param name="methodDef"></param>
/// <param name="instructionIndex"></param>
private void CheckFieldReferenceInstruction(ExtensionType extensionType, MethodDefinition methodDef, ref int instructionIndex)
{
Instruction instruction = methodDef.Body.Instructions[instructionIndex];
FieldReference field = (FieldReference)instruction.Operand;
TypeReference typeRef = field.DeclaringType;
if (typeRef.IsType(typeof(GenericWriter<>)) || typeRef.IsType(typeof(GenericReader<>)) && typeRef.IsGenericInstance)
{
GenericInstanceType typeGenericInst = (GenericInstanceType)typeRef;
TypeReference parameterType = typeGenericInst.GenericArguments[0];
CreateReaderOrWriter(extensionType, methodDef, ref instructionIndex, parameterType);
}
}
/// <summary>
/// Checks if a reader or writer must be generated for a call type.
/// </summary>
/// <param name="extensionType"></param>
/// <param name="moduleDef"></param>
/// <param name="methodDef"></param>
/// <param name="instructionIndex"></param>
/// <param name="method"></param>
private void CheckCallInstruction(ExtensionType extensionType, MethodDefinition methodDef, ref int instructionIndex, MethodReference method)
{
if (!method.IsGenericInstance)
return;
//True if call is to read/write.
bool canCreate = (
method.Is<Writer>(nameof(Writer.Write)) ||
method.Is<Reader>(nameof(Reader.Read))
);
if (canCreate)
{
GenericInstanceMethod instanceMethod = (GenericInstanceMethod)method;
TypeReference parameterType = instanceMethod.GenericArguments[0];
if (parameterType.IsGenericParameter)
return;
CreateReaderOrWriter(extensionType, methodDef, ref instructionIndex, parameterType);
}
}
/// <summary>
/// Creates a reader or writer for parameterType if needed. Otherwise calls existing reader.
/// </summary>
private void CreateReaderOrWriter(ExtensionType extensionType, MethodDefinition methodDef, ref int instructionIndex, TypeReference parameterType)
{
ReaderProcessor rp = base.GetClass<ReaderProcessor>();
WriterProcessor wp = base.GetClass<WriterProcessor>();
////If parameterType has user declared do nothing.
//if (wp.IsGlobalSerializer(parameterType))
// return;
if (!parameterType.IsGenericParameter && parameterType.CanBeResolved(base.Session))
{
TypeDefinition typeDefinition = parameterType.CachedResolve(base.Session);
//If class and not value type check for accessible constructor.
if (typeDefinition.IsClass && !typeDefinition.IsValueType)
{
MethodDefinition constructor = typeDefinition.GetDefaultConstructor(base.Session);
//Constructor is inaccessible, cannot create serializer for type.
if (constructor != null && !constructor.IsPublic)
{
base.LogError($"Unable to generator serializers for {typeDefinition.FullName} because it's constructor is not public.");
return;
}
}
ILProcessor processor = methodDef.Body.GetILProcessor();
//Find already existing read or write method.
MethodReference createdMethodRef = (extensionType == ExtensionType.Write) ?
wp.GetWriteMethodReference(parameterType) :
rp.GetReadMethodReference(parameterType);
//If a created method already exist nothing further is required.
if (createdMethodRef != null)
{
TryInsertAutoPack(ref instructionIndex);
//Replace call to generic with already made serializer.
Instruction newInstruction = processor.Create(OpCodes.Call, createdMethodRef);
methodDef.Body.Instructions[instructionIndex] = newInstruction;
return;
}
else
{
createdMethodRef = (extensionType == ExtensionType.Write) ?
wp.CreateWriter(parameterType) :
rp.CreateReader(parameterType);
}
//If method was created.
if (createdMethodRef != null)
{
TryInsertAutoPack(ref instructionIndex);
//Set new instruction.
Instruction newInstruction = processor.Create(OpCodes.Call, createdMethodRef);
methodDef.Body.Instructions[instructionIndex] = newInstruction;
}
}
void TryInsertAutoPack(ref int insertIndex)
{
if (IsAutoPackMethod(parameterType, extensionType))
{
ILProcessor processor = methodDef.Body.GetILProcessor();
AutoPackType packType = base.GetClass<GeneralHelper>().GetDefaultAutoPackType(parameterType);
Instruction autoPack = processor.Create(OpCodes.Ldc_I4, (int)packType);
methodDef.Body.Instructions.Insert(insertIndex, autoPack);
insertIndex++;
}
}
}
/// <summary>
/// Returns if a typeRef serializer requires or uses autopacktype.
/// </summary>
private bool IsAutoPackMethod(TypeReference typeRef, ExtensionType extensionType)
{
if (extensionType == ExtensionType.Write)
return base.GetClass<WriterProcessor>().IsAutoPackedType(typeRef);
else if (extensionType == ExtensionType.Read)
return base.GetClass<ReaderProcessor>().IsAutoPackedType(typeRef);
else
return false;
}
/// <summary>
/// Returns the RPC attribute on a method, if one exist. Otherwise returns null.
/// </summary>
/// <param name="methodDef"></param>
/// <returns></returns>
private ExtensionType GetExtensionType(MethodDefinition methodDef)
{
bool hasExtensionAttribute = methodDef.HasCustomAttribute<System.Runtime.CompilerServices.ExtensionAttribute>();
if (!hasExtensionAttribute)
return ExtensionType.None;
bool write = (methodDef.ReturnType == methodDef.Module.TypeSystem.Void);
//Return None for Mirror types.
#if MIRROR
if (write)
{
if (methodDef.Parameters.Count > 0 && methodDef.Parameters[0].ParameterType.FullName == "Mirror.NetworkWriter")
return ExtensionType.None;
}
else
{
if (methodDef.Parameters.Count > 0 && methodDef.Parameters[0].ParameterType.FullName == "Mirror.NetworkReader")
return ExtensionType.None;
}
#endif
string prefix = (write) ? WriterProcessor.CUSTOM_WRITER_PREFIX : ReaderProcessor.CUSTOM_READER_PREFIX;
//Does not contain prefix.
if (methodDef.Name.Length < prefix.Length || methodDef.Name.Substring(0, prefix.Length) != prefix)
return ExtensionType.None;
//Make sure first parameter is right.
if (methodDef.Parameters.Count >= 1)
{
TypeReference tr = methodDef.Parameters[0].ParameterType;
if (tr.FullName != base.GetClass<WriterImports>().Writer_TypeRef.FullName &&
tr.FullName != base.GetClass<ReaderImports>().Reader_TypeRef.FullName)
return ExtensionType.None;
}
if (write && methodDef.Parameters.Count < 2)
{
base.LogError($"{methodDef.FullName} must have at least two parameters, the first being PooledWriter, and second value to write.");
return ExtensionType.None;
}
else if (!write && methodDef.Parameters.Count < 1)
{
base.LogError($"{methodDef.FullName} must have at least one parameters, the first being PooledReader.");
return ExtensionType.None;
}
return (write) ? ExtensionType.Write : ExtensionType.Read;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9269fd8a62199e24c965b4c99b641244
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More