first commit

first commit
This commit is contained in:
mher 2025-06-28 11:28:54 +03:30
parent 4ad6af53b9
commit 2108da7906
1842 changed files with 211825 additions and 0 deletions

View File

@ -0,0 +1,28 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3ad70174b079c2f4ebc7931d3dd1af6f, type: 3}
m_Name: DefaultPrefabObjects
m_EditorClassIdentifier:
_prefabs:
- {fileID: 3007804520713623267, guid: e3ebf1e5214cbb941b2da4f7add0060a, type: 3}
- {fileID: 8475222101369129519, guid: 8cf33e8e99a9b0c4c8f29ff725650de6, type: 3}
- {fileID: 4512293259955182956, guid: 44611030e61220d42ab7c37ba3c0ea92, type: 3}
- {fileID: 4512293259955182956, guid: 0d6d0f48b03b17f49a6340103cd9b9d0, type: 3}
- {fileID: 611616139817875448, guid: bf5f023b4017a5e41a9815ec5745df3d, type: 3}
- {fileID: 4512293259955182956, guid: 35639798ad77fc145871588b25d66259, type: 3}
- {fileID: 4512293259955182956, guid: fe2b65b02f0484b41aa8cfa9fbbb0e1d, type: 3}
- {fileID: 4512293259955182956, guid: dafef736ca1ae384e9a19eb672843563, type: 3}
- {fileID: 4512293259955182956, guid: f32d4c19de900e64cb73cedcb8ba6f70, type: 3}
- {fileID: 4512293259955182956, guid: b8017cef39731ba439c70fecc09488e3, type: 3}
- {fileID: 201277550, guid: 5b712878ecece354ba4ffb026c0a221c, type: 3}
- {fileID: 201277550, guid: 26a567abbe21227428f5c3d309d1d73c, type: 3}
- {fileID: 8192566354860284824, guid: 6331b3542e64a564c81bc39cedf70c8d, type: 3}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4c21a388bb04ca646927587df80f819a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
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,18 @@
fileFormatVersion: 2
guid: de84fa9739ed55945b58147a773e1740
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Extension/FieldDefinitionExtensions.cs
uploadId: 762203

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(fr.Name, fr.FieldType, git);
return result;
}
else
{
return fr;
}
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 6d983ebd0c9e1b745902030c2f7a8e99
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Extension/FieldReferenceExtensions.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: 127d8312da53b3e49ba9e3e4c6348857
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Extension/ILProcessorExtensions.cs
uploadId: 762203

View File

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

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 866ed457fe28c3e4b9698d87b9abd709
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Extension/MethodDefinitionExtensions.cs
uploadId: 762203

View File

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

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: c9f00cf3dc8b90b469c3c9cb8b87fc88
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Extension/TypeDefinitionExtensions.cs
uploadId: 762203

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(self);
foreach (GenericParameter argument in self.GenericParameters)
instance.GenericArguments.Add(argument);
return instance;
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 239b1b10db80c594d93b7ba4ee2c1ec5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Extension/TypeReferenceExtensions.cs
uploadId: 762203

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,14 @@
fileFormatVersion: 2
guid: 9133eb285bd7f3c4f89f4d7a2a079c6b
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/FN_README.txt
uploadId: 762203

View File

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

View File

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

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: d32f3dc23b55598429c5cfe6156e6243
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs
uploadId: 762203

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();
/// <summary>
/// CodegenBase classes for processing a module.
/// </summary>
private List<CodegenBase> _bases;
/// <summary>
/// Quick lookup of base classes.
/// </summary>
private Dictionary<string, CodegenBase> _basesCache = new();
/// <summary>
/// Returns class of type if found within CodegenBase classes.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
internal T GetClass<T>() where T : CodegenBase
{
string tName = typeof(T).Name;
return (T)_basesCache[tName];
}
/// <summary>
/// Resets all helpers while importing any information needed by them.
/// </summary>
/// <param name="module"></param>
/// <returns></returns>
internal bool Initialize(ModuleDefinition module)
{
Module = module;
Diagnostics = new();
_bases = new()
{
new ReaderImports(), new ReaderProcessor()
,new WriterImports(), new WriterProcessor()
, new PhysicsHelper(), new TimeManagerHelper(), new AttributeHelper(), new GeneralHelper()
, new ObjectHelper(), new NetworkBehaviourHelper()
, new TransportHelper()
, new NetworkConnectionImports(), new PredictedObjectHelper(), new GeneratorHelper()
, new CustomSerializerProcessor()
, new NetworkBehaviourProcessor()
, new QolAttributeProcessor()
, new RpcProcessor()
, new SyncTypeProcessor()
, new PredictionProcessor()
};
//Add all to dictionary first, then import.
foreach (CodegenBase item in _bases)
{
string tName = item.GetType().Name;
_basesCache.Add(tName, item);
}
//Initialize.
foreach (CodegenBase item in _bases)
{
item.Initialize(this);
if (!item.ImportReferences())
return false;
}
return true;
}
#region Logging.
/// <summary>
/// Logs a warning.
/// </summary>
/// <param name="msg"></param>
internal void LogWarning(string msg)
{
Diagnostics.AddWarning(msg);
}
/// <summary>
/// Logs an error.
/// </summary>
/// <param name="msg"></param>
internal void LogError(string msg)
{
Diagnostics.AddError(msg);
}
#endregion
#region ImportReference.
public MethodReference ImportReference(SR.MethodBase method)
{
return Module.ImportReference(method);
}
public MethodReference ImportReference(SR.MethodBase method, IGenericParameterProvider context)
{
return Module.ImportReference(method, context);
}
public TypeReference ImportReference(TypeReference type)
{
return Module.ImportReference(type);
}
public TypeReference ImportReference(TypeReference type, IGenericParameterProvider context)
{
return Module.ImportReference(type, context);
}
public FieldReference ImportReference(FieldReference field)
{
return Module.ImportReference(field);
}
public FieldReference ImportReference(FieldReference field, IGenericParameterProvider context)
{
return Module.ImportReference(field, context);
}
public MethodReference ImportReference(MethodReference method)
{
return Module.ImportReference(method);
}
public MethodReference ImportReference(MethodReference method, IGenericParameterProvider context)
{
return Module.ImportReference(method, context);
}
public TypeReference ImportReference(System.Type type)
{
return ImportReference(type, null);
}
public TypeReference ImportReference(System.Type type, IGenericParameterProvider context)
{
return Module.ImportReference(type, context);
}
public FieldReference ImportReference(SR.FieldInfo field)
{
return Module.ImportReference(field);
}
public FieldReference ImportReference(SR.FieldInfo field, IGenericParameterProvider context)
{
return Module.ImportReference(field, context);
}
#endregion
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 3e8416eee3308f54fa942003de975420
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: 935ec97b96b35b94f8c6880c6908304c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: a66d771ab331fae408142a5c04abd74e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/CustomAttributeExtensions.cs
uploadId: 762203

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()
{
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,18 @@
fileFormatVersion: 2
guid: fb7b65b572b01444cbe3c9d830cd3587
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/Diagnostics.cs
uploadId: 762203

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();
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,18 @@
fileFormatVersion: 2
guid: 1a7e03137ca78704e999f3a3dc68b953
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/GetConstructor.cs
uploadId: 762203

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

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 820cf8401d4d71c4196dda444559ef8a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/ILProcessorExtensions.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: 360667149f16b6c4aba61fd05427cbfb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/InstructionExtensions.cs
uploadId: 762203

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

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: ee1c15a06ab386e439ec5aa41e3496f7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/MethodReferenceExtensions.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: 42648785493390646898f5fa13e1dfd6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/ModuleDefinitionExtensions.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: 31071055a2e388141b8f11e1ba4e147e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/ParameterDefinitionExtensions.cs
uploadId: 762203

View File

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

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 645e49fe7eeff3a4e9eb65d77fc6e2ca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/TypeDefinitionExtensions.cs
uploadId: 762203

View File

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

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 2344f5ab0fda07b498c03fbe0e082c14
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/TypeReferenceExtensions.cs
uploadId: 762203

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 6139ff104f3c24442b26dbc4e40d5ce5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs
uploadId: 762203

View File

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

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 0c42e06349d6890459a155a2afe17d41
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: 6d061dda8ed87ed48a08020942ad63f6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionImports.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: 033da35314653aa4689b4582e7929295
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs
uploadId: 762203

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();
//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,18 @@
fileFormatVersion: 2
guid: 28ae27f7bc8e89547a606262508fdd36
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: e9a06c812bf785a44a38a5852ff866d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/PredictedObjectHelper.cs
uploadId: 762203

View File

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

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 350861dcbcbabc447acd37e4310b0697
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: 11dbcc0798e079e4a85fe98dfc9fe23a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/TimeManagerHelper.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: 6ced44bfdb3068f4cb7513c9be85729a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/TransportHelper.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: 2b30600f0fdb27c4fb86c310b08f43b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/Comparers.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: 9b1882eac63e3d94aad8f41915bc1ab8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs
uploadId: 762203

View File

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

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 357a22940018b8e49976e13272c5b2ef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/QOLAttributeType.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: e7f1bbf5c398c3e4e92e53ec3e49d5e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/SerializatierType.cs
uploadId: 762203

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

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 55f2e751e4788464b8394f6b8bdb548a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/SyncIndexData.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: 44c753d6ac0c7864c9962d91703b2afe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs
uploadId: 762203

View File

@ -0,0 +1,100 @@
using FishNet.CodeGenerating.Extension;
using FishNet.CodeGenerating.Helping.Extension;
using FishNet.Object;
using FishNet.Serializing;
using MonoFN.Cecil;
using System;
using System.Collections.Generic;
using System.Reflection;
namespace FishNet.CodeGenerating.Helping
{
internal class WriterImports : CodegenBase
{
#region Reflection references.
public MethodReference WriterPool_GetWriter_MethodRef;
public MethodReference WriterPool_GetWriterLength_MethodRef;
public MethodReference Writer_WritePackedWhole_Signed_MethodRef;
public MethodReference Writer_WritePackedWhole_Unsigned_MethodRef;
public TypeReference PooledWriter_TypeRef;
public TypeReference Writer_TypeRef;
public MethodReference PooledWriter_Dispose_MethodRef;
public MethodReference Writer_WriteDictionary_MethodRef;
public MethodReference Writer_WriteList_MethodRef;
public MethodReference Writer_WriteArray_MethodRef;
public TypeReference AutoPackTypeRef;
public TypeReference GenericWriter_TypeRef;
public MethodReference GenericWriter_Write_MethodRef;
public MethodReference Writer_Write_MethodRef;
#endregion
/// <summary>
/// Imports references needed by this helper.
/// </summary>
/// <param name="moduleDef"></param>
/// <returns></returns>
public override bool ImportReferences()
{
PooledWriter_TypeRef = base.ImportReference(typeof(PooledWriter));
Writer_TypeRef = base.ImportReference(typeof(Writer));
AutoPackTypeRef = base.ImportReference(typeof(AutoPackType));
GenericWriter_TypeRef = base.ImportReference(typeof(GenericWriter<>));
Writer_Write_MethodRef = Writer_TypeRef.CachedResolve(base.Session).GetMethodReference(base.Session, nameof(Writer.Write));
TypeDefinition genericWriterTd = GenericWriter_TypeRef.CachedResolve(base.Session);
GenericWriter_Write_MethodRef = base.ImportReference(genericWriterTd.GetMethod(nameof(GenericWriter<int>.SetWrite)));
//WriterPool.GetWriter
Type writerPoolType = typeof(WriterPool);
base.ImportReference(writerPoolType);
foreach (var methodInfo in writerPoolType.GetMethods())
{
if (methodInfo.Name == nameof(WriterPool.Retrieve))
{
//GetWriter().
if (methodInfo.GetParameters().Length == 0)
{
WriterPool_GetWriter_MethodRef = base.ImportReference(methodInfo);
}
//GetWriter(?).
else if (methodInfo.GetParameters().Length == 1)
{
ParameterInfo pi = methodInfo.GetParameters()[0];
//GetWriter(int).
if (pi.ParameterType == typeof(int))
WriterPool_GetWriterLength_MethodRef = base.ImportReference(methodInfo);
}
}
}
WriterProcessor gwh = base.GetClass<WriterProcessor>();
Type pooledWriterType = typeof(PooledWriter);
foreach (MethodInfo methodInfo in pooledWriterType.GetMethods())
{
int parameterCount = methodInfo.GetParameters().Length;
if (methodInfo.Name == nameof(PooledWriter.Store))
PooledWriter_Dispose_MethodRef = base.ImportReference(methodInfo);
else if (methodInfo.Name == nameof(PooledWriter.WriteUnsignedPackedWhole))
{
//todo: check if signed or not and set to signed/unsigned variable.
//do the same changes for methods which call these.
//Writer_WritePackedWhole_MethodRef = base.ImportReference(methodInfo);
}
//Relay writers.
else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteDictionary))
Writer_WriteDictionary_MethodRef = base.ImportReference(methodInfo);
else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteList))
Writer_WriteList_MethodRef = base.ImportReference(methodInfo);
else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteArray))
Writer_WriteArray_MethodRef = base.ImportReference(methodInfo);
}
return true;
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 425638e29bab6f1488e8865c9e3f8b57
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs
uploadId: 762203

View File

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

View File

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

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: f03d76b376c1d5b4591039af6fd4c9e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs
uploadId: 762203

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(compiledAssembly);
ReaderParameters readerParameters = new()
{
SymbolStream = new MemoryStream(compiledAssembly.InMemoryAssembly.PdbData),
SymbolReaderProvider = new PortablePdbReaderProvider(),
AssemblyResolver = assemblyResolver,
ReflectionImporterProvider = new PostProcessorReflectionImporterProvider(),
ReadingMode = ReadingMode.Immediate
};
AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(new MemoryStream(compiledAssembly.InMemoryAssembly.PeData), readerParameters);
//Allows us to resolve inside FishNet assembly, such as for components.
assemblyResolver.AddAssemblyDefinitionBeingOperatedOn(assemblyDefinition);
return assemblyDefinition;
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: dfcfb917dd9268744962ae61aa0115b7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs
uploadId: 762203

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

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 2c247f4266b2864eb96e6a9ae6557d31
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/ILCore/PostProcessorAssemblyResolver.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: 484e8ad8c4dde382ea67036b32935ef1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporter.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: f9273a5dad109ab0783891e36c983080
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporterProvider.cs
uploadId: 762203

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,18 @@
fileFormatVersion: 2
guid: 8462034e5255191499a018bd8fbbf751
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs
uploadId: 762203

View File

@ -0,0 +1,341 @@

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

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 9269fd8a62199e24c965b4c99b641244
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Processing/CustomSerializerProcessor.cs
uploadId: 762203

View File

@ -0,0 +1,503 @@
using FishNet.CodeGenerating.Extension;
using FishNet.CodeGenerating.Helping;
using FishNet.CodeGenerating.Helping.Extension;
using FishNet.CodeGenerating.Processing.Rpc;
using FishNet.Configuring;
using FishNet.Object;
using MonoFN.Cecil;
using MonoFN.Cecil.Cil;
using MonoFN.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
namespace FishNet.CodeGenerating.Processing
{
internal class NetworkBehaviourProcessor : CodegenBase
{
#region Private.
/// <summary>
/// Classes which have been processed for all NetworkBehaviour features.
/// </summary>
private HashSet<TypeDefinition> _processedClasses = new();
#endregion
#region Const.
internal const string NETWORKINITIALIZE_EARLY_INTERNAL_NAME = "NetworkInitialize___Early";
internal const string NETWORKINITIALIZE_LATE_INTERNAL_NAME = "NetworkInitialize___Late";
private readonly string[] _networkInitializeMethodNames = new string[] { NETWORKINITIALIZE_EARLY_INTERNAL_NAME, NETWORKINITIALIZE_LATE_INTERNAL_NAME };
#endregion
internal bool ProcessLocal(TypeDefinition typeDef)
{
bool modified = false;
TypeDefinition copyTypeDef = typeDef;
//TypeDefs which are using prediction.
List<TypeDefinition> _usesPredictionTypeDefs = new();
//Make collection of NBs to processor.
List<TypeDefinition> typeDefs = new();
do
{
if (!HasClassBeenProcessed(copyTypeDef))
{
//Disallow nested network behaviours.
ICollection<TypeDefinition> nestedTds = copyTypeDef.NestedTypes;
foreach (TypeDefinition item in nestedTds)
{
if (item.InheritsNetworkBehaviour(base.Session))
{
base.LogError($"{copyTypeDef.FullName} contains nested NetworkBehaviours. These are not supported.");
return modified;
}
}
typeDefs.Add(copyTypeDef);
}
copyTypeDef = TypeDefinitionExtensionsOld.GetNextBaseClassToProcess(copyTypeDef, base.Session);
} while (copyTypeDef != null);
/* Reverse type definitions so that the parent
* is first. This counts indexes up as we go further
* down the children. By doing so we do not have to
* rebuild rpc or synctype indexes when a parent is inherited
* multiple times. EG: with this solution if parent had 1 sync type
* and childA had 2 the parent would be index 0, and childA would have 1 and 2.
* But also, if childB inherited childA it would have 3+.
*
* Going in reverse also gaurantees the awake method will already be created
* or modified in any class a child inherits. This lets us call it appropriately
* as well error if the awake does not exist, such as could not be created. */
typeDefs.Reverse();
foreach (TypeDefinition td in typeDefs)
{
/* Create NetworkInitialize before-hand so the other procesors
* can use it. */
MethodDefinition networkInitializeIfDisabledMd;
CreateNetworkInitializeMethods(td, out networkInitializeIfDisabledMd);
CallNetworkInitialize(networkInitializeIfDisabledMd);
/* Prediction. */
/* Run prediction first since prediction will modify
* user data passed into prediction methods. Because of this
* other RPCs should use the modified version and reader/writers
* made for prediction. */
if (base.GetClass<PredictionProcessor>().Process(td))
{
_usesPredictionTypeDefs.Add(td);
modified = true;
}
//25ms
/* RPCs. */
modified |= base.GetClass<RpcProcessor>().ProcessLocal(td);
//30ms
/* //perf rpcCounts can be optimized by having different counts
* for target, observers, server, replicate, and reoncile rpcs. Since
* each registers to their own delegates this is possible. */
/* SyncTypes. */
modified |= base.GetClass<SyncTypeProcessor>().ProcessLocal(td);
//Call base networkinitialize early/late.
CallBaseOnNetworkInitializeMethods(td);
//Add networkinitialize executed check to early/late.
AddNetworkInitializeExecutedChecks(td);
//Copy user logic from awake into a new method.
CopyAwakeUserLogic(td);
/* Create awake method or if already exist make
* it public virtual. */
if (!ModifyAwakeMethod(td, out bool awakeCreated))
{
//This is a hard fail and will break the solution so throw here.
base.LogError($"{td.FullName} has an Awake method which could not be modified, or could not be found. This often occurs when a child class is in an assembly different from the parent, and the parent does not implement Awake. To resolve this make an Awake in {td.Name} public virtual.");
return modified;
}
//Calls NetworkInitializeEarly from awake.
CallMethodFromAwake(td, NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
//Only call base if awake was created. Otherwise let the users implementation handle base calling.
if (awakeCreated)
CallBaseAwake(td);
//Call logic user may have put in awake.
CallAwakeUserLogic(td);
//NetworkInitializeLate from awake.
CallMethodFromAwake(td, NETWORKINITIALIZE_LATE_INTERNAL_NAME);
//Since awake methods are erased ret has to be added at the end.
AddReturnToAwake(td);
//70ms
_processedClasses.Add(td);
}
/* If here then all inerited classes for firstTypeDef have
* been processed. */
return modified;
}
/// <summary>
/// Gets the name to use for user awake logic method.
/// </summary>
internal string GetAwakeUserLogicMethodDefinition(TypeDefinition td) => $"Awake_UserLogic_{td.FullName}_{base.Module.Name}";
/// <summary>
/// Returns if a class has been processed.
/// </summary>
/// <param name="typeDef"></param>
/// <returns></returns>
private bool HasClassBeenProcessed(TypeDefinition typeDef)
{
return _processedClasses.Contains(typeDef);
}
/// <summary>
/// Returns if any typeDefs have attributes which are not allowed to be used outside NetworkBehaviour.
/// </summary>
/// <param name="typeDefs"></param>
/// <returns></returns>
internal bool NonNetworkBehaviourHasInvalidAttributes(Collection<TypeDefinition> typeDefs)
{
SyncTypeProcessor stProcessor = base.GetClass<SyncTypeProcessor>();
RpcProcessor rpcProcessor = base.GetClass<RpcProcessor>();
foreach (TypeDefinition typeDef in typeDefs)
{
//Inherits, don't need to check.
if (typeDef.InheritsNetworkBehaviour(base.Session))
continue;
//Check each method for attribute.
foreach (MethodDefinition md in typeDef.Methods)
{
//Has RPC attribute but doesn't inherit from NB.
if (rpcProcessor.Attributes.HasRpcAttributes(md))
{
base.LogError($"{typeDef.FullName} has one or more RPC attributes but does not inherit from NetworkBehaviour.");
return true;
}
}
//Check fields for attribute.
foreach (FieldDefinition fd in typeDef.Fields)
{
if (stProcessor.IsSyncType(fd))
{
base.LogError($"{typeDef.FullName} implements one or more SyncTypes but does not inherit from NetworkBehaviour.");
return true;
}
}
}
//Fallthrough / pass.
return false;
}
/// <summary>
/// Calls the next awake method if the nested awake was created by codegen.
/// </summary>
/// <returns></returns>
private void CallBaseAwake(TypeDefinition td)
{
/* If base is not a class which can be processed then there
* is no need to continue. */
if (!td.CanProcessBaseType(base.Session))
return;
//Base Awake.
MethodReference baseAwakeMr = td.GetMethodReferenceInBase(base.Session, NetworkBehaviourHelper.AWAKE_METHOD_NAME);
//This Awake.
MethodDefinition tdAwakeMd = td.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
ILProcessor processor = tdAwakeMd.Body.GetILProcessor();
processor.Emit(OpCodes.Ldarg_0); //base.
processor.Emit(OpCodes.Call, baseAwakeMr);
}
/// <summary>
/// Calls the next awake method if the nested awake was created by codegen.
/// </summary>
/// <returns></returns>
private void CallAwakeUserLogic(TypeDefinition td)
{
//UserLogic.
MethodDefinition userLogicMd = td.GetMethod(GetAwakeUserLogicMethodDefinition(td));
/* Userlogic may be null if Awake was created.
* If so, there's no need to proceed. */
if (userLogicMd == null)
return;
//This Awake.
MethodDefinition awakeMd = td.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
//Call logic.
base.GetClass<GeneralHelper>().CallCopiedMethod(awakeMd, userLogicMd);
}
/// <summary>
/// Adds a check to NetworkInitialize to see if it has already run.
/// </summary>
/// <param name="typeDef"></param>
private void AddNetworkInitializeExecutedChecks(TypeDefinition typeDef)
{
AddCheck(NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
AddCheck(NETWORKINITIALIZE_LATE_INTERNAL_NAME);
void AddCheck(string methodName)
{
string fieldName = $"{methodName}{typeDef.FullName}{typeDef.Module.Name}_Excuted";
MethodDefinition md = typeDef.GetMethod(methodName);
if (md == null)
return;
TypeReference boolTr = base.GetClass<GeneralHelper>().GetTypeReference(typeof(bool));
FieldReference fr = typeDef.GetOrCreateFieldReference(base.Session, fieldName, FieldAttributes.Private, boolTr, out bool created);
if (created)
{
List<Instruction> insts = new();
ILProcessor processor = md.Body.GetILProcessor();
//Add check if already called.
//if (alreadyInitialized) return;
Instruction skipFirstRetInst = processor.Create(OpCodes.Nop);
insts.Add(processor.Create(OpCodes.Ldarg_0));
insts.Add(processor.Create(OpCodes.Ldfld, fr));
insts.Add(processor.Create(OpCodes.Brfalse_S, skipFirstRetInst));
insts.Add(processor.Create(OpCodes.Ret));
insts.Add(skipFirstRetInst);
//Set field to true.
insts.Add(processor.Create(OpCodes.Ldarg_0));
insts.Add(processor.Create(OpCodes.Ldc_I4_1));
insts.Add(processor.Create(OpCodes.Stfld, fr));
processor.InsertFirst(insts);
}
}
}
/// <summary>
/// Calls base for NetworkInitializeEarly/Late on a TypeDefinition.
/// </summary>
private void CallBaseOnNetworkInitializeMethods(TypeDefinition typeDef)
{
//If base is null cannot call base.
if (typeDef.BaseType == null)
return;
foreach (string mdName in _networkInitializeMethodNames)
{
MethodReference baseMr = typeDef.GetMethodReferenceInBase(base.Session, mdName);
//Not found in base.
if (baseMr == null)
continue;
MethodDefinition baseMd = baseMr.CachedResolve(base.Session);
MethodDefinition thisMd = typeDef.GetMethod(mdName);
ILProcessor processor = thisMd.Body.GetILProcessor();
bool alreadyHasBaseCall = false;
//Check if already calls baseAwake.
foreach (Instruction item in thisMd.Body.Instructions)
{
//If a call or call virt. Although, callvirt should never occur.
if (item.OpCode == OpCodes.Call || item.OpCode == OpCodes.Callvirt)
{
if (item.Operand != null && item.Operand.GetType().Name == nameof(MethodDefinition))
{
MethodDefinition md = (MethodDefinition)item.Operand;
if (md == baseMd)
{
alreadyHasBaseCall = true;
break;
}
}
}
}
if (!alreadyHasBaseCall)
{
//Create instructions for base call.
List<Instruction> instructions = new()
{
processor.Create(OpCodes.Ldarg_0), //this.
processor.Create(OpCodes.Call, baseMr)
};
processor.InsertFirst(instructions);
}
}
}
/// <summary>
/// Adds returns awake method definitions within awakeDatas.
/// </summary>
private void AddReturnToAwake(TypeDefinition td)
{
//This Awake.
MethodDefinition awakeMd = td.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
ILProcessor processor = awakeMd.Body.GetILProcessor();
//If no instructions or the last instruction isnt ret.
if (processor.Body.Instructions.Count == 0 || processor.Body.Instructions[processor.Body.Instructions.Count - 1].OpCode != OpCodes.Ret)
{
processor.Emit(OpCodes.Ret);
}
}
/// <summary>
/// Calls a method by name from awake.
/// </summary>
private void CallMethodFromAwake(TypeDefinition typeDef, string methodName)
{
//Will never be null because we added it previously.
MethodDefinition awakeMethodDef = typeDef.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
MethodReference networkInitMr = typeDef.GetMethodReference(base.Session, methodName);
ILProcessor processor = awakeMethodDef.Body.GetILProcessor();
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(networkInitMr.GetCallOpCode(base.Session), networkInitMr);
}
/// <summary>
/// Creates an 'NetworkInitialize' method which is called by the childmost class to initialize scripts on Awake.
/// </summary>
private void CreateNetworkInitializeMethods(TypeDefinition typeDef, out MethodDefinition networkInitializeIfDisabledMd)
{
CreateMethod(NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
CreateMethod(NETWORKINITIALIZE_LATE_INTERNAL_NAME);
networkInitializeIfDisabledMd = CreateMethod(nameof(NetworkBehaviour.NetworkInitializeIfDisabled));
MethodDefinition CreateMethod(string name, MethodDefinition copied = null)
{
bool created;
MethodDefinition md = typeDef.GetOrCreateMethodDefinition(base.Session, name, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES, typeDef.Module.TypeSystem.Void, out created);
if (created)
{
//Emit ret into new method.
ILProcessor processor = md.Body.GetILProcessor();
//End of method return.
processor.Emit(OpCodes.Ret);
}
return md;
}
}
/// <summary>
/// Creates an 'NetworkInitialize' method which is called by the childmost class to initialize scripts on Awake.
/// </summary>
private void CallNetworkInitialize(MethodDefinition networkIntializeMd)
{
ILProcessor processor = networkIntializeMd.Body.GetILProcessor();
networkIntializeMd.Body.Instructions.Clear();
CallMethod(NETWORKINITIALIZE_EARLY_INTERNAL_NAME);
CallMethod(NETWORKINITIALIZE_LATE_INTERNAL_NAME);
processor.Emit(OpCodes.Ret);
void CallMethod(string name)
{
MethodReference initIfDisabledMr = networkIntializeMd.DeclaringType.GetMethodReference(base.Session, name);
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(initIfDisabledMr.GetCallOpCode(base.Session), initIfDisabledMr);
}
}
/// <summary>
/// Copies logic from users Awake if present, to a new method.
/// </summary>
private void CopyAwakeUserLogic(TypeDefinition typeDef)
{
MethodDefinition awakeMd = typeDef.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
//If found copy.
if (awakeMd != null)
base.GetClass<GeneralHelper>().CopyIntoNewMethod(awakeMd, GetAwakeUserLogicMethodDefinition(typeDef), out _);
}
/// <summary>
/// Erases content in awake if it already exist, otherwise makes a new Awake.
/// Makes Awake public and virtual.
/// </summary>
/// <returns>True if successful.</returns>
private bool ModifyAwakeMethod(TypeDefinition typeDef, out bool created)
{
MethodDefinition awakeMd = typeDef.GetOrCreateMethodDefinition(base.Session, NetworkBehaviourHelper.AWAKE_METHOD_NAME, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES, typeDef.Module.TypeSystem.Void, out created);
//Awake is found. Check for invalid return type.
if (!created)
{
if (awakeMd.ReturnType != typeDef.Module.TypeSystem.Void)
{
base.LogError($"IEnumerator Awake methods are not supported within NetworkBehaviours.");
return false;
}
//Make public if good.
awakeMd.SetPublicAttributes();
}
//Already was made.
else
{
ILProcessor processor = awakeMd.Body.GetILProcessor();
processor.Emit(OpCodes.Ret);
}
//Clear original awake.
awakeMd.Body.Instructions.Clear();
return true;
}
/// <summary>
/// Makes all Awake methods within typeDef and base classes public and virtual.
/// </summary>
/// <param name="typeDef"></param>
internal void CreateFirstNetworkInitializeCall(TypeDefinition typeDef, MethodDefinition firstUserAwakeMethodDef, MethodDefinition firstNetworkInitializeMethodDef)
{
ILProcessor processor;
//Get awake for current method.
MethodDefinition thisAwakeMethodDef = typeDef.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME);
bool created = false;
//If no awake then make one.
if (thisAwakeMethodDef == null)
{
created = true;
thisAwakeMethodDef = new(NetworkBehaviourHelper.AWAKE_METHOD_NAME, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES, typeDef.Module.TypeSystem.Void);
thisAwakeMethodDef.Body.InitLocals = true;
typeDef.Methods.Add(thisAwakeMethodDef);
processor = thisAwakeMethodDef.Body.GetILProcessor();
processor.Emit(OpCodes.Ret);
}
//MethodRefs for networkinitialize and awake.
MethodReference networkInitializeMethodRef = typeDef.Module.ImportReference(firstNetworkInitializeMethodDef);
processor = thisAwakeMethodDef.Body.GetILProcessor();
//Create instructions for base call.
List<Instruction> instructions = new()
{
processor.Create(OpCodes.Ldarg_0), //this.
processor.Create(OpCodes.Call, networkInitializeMethodRef)
};
/* If awake was created then make a call to the users
* first awake. There's no reason to do this if awake
* already existed because the user would have control
* over making that call. */
if (created && firstUserAwakeMethodDef != null)
{
MethodReference baseAwakeMethodRef = typeDef.Module.ImportReference(firstUserAwakeMethodDef);
instructions.Add(processor.Create(OpCodes.Ldarg_0)); //this.
instructions.Add(processor.Create(OpCodes.Call, baseAwakeMethodRef));
}
processor.InsertFirst(instructions);
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 23866e4d620216745a837fa99e801164
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.9R
assetPath: Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs
uploadId: 762203

View File

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

File diff suppressed because it is too large Load Diff

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